From 62dec0574b21bf8f42d02ee5f4e261d9d91b3206 Mon Sep 17 00:00:00 2001 From: Loic Date: Mon, 9 Oct 2023 11:05:25 +0200 Subject: [PATCH 01/12] [doc] add osrm spec --- documentation/apis/osrm/1.0.0/api.json | 262 +++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 documentation/apis/osrm/1.0.0/api.json diff --git a/documentation/apis/osrm/1.0.0/api.json b/documentation/apis/osrm/1.0.0/api.json new file mode 100644 index 00000000..024d2444 --- /dev/null +++ b/documentation/apis/osrm/1.0.0/api.json @@ -0,0 +1,262 @@ +{ + "openapi": "3.0.0", + "info": { + "description": "Description of the OSRM API available via Road2. Only route operation in its v1 is available.", + "version": "1.0.0", + "title": "OSRM API inside Road2", + "contact": { + "email": "contact.geoservices@ign.fr" + } + }, + "servers": [ + { + "url": "http://localhost:8080/osrm/1.0.0/", + "description": "Local server" + } + ], + "tags": [ + { + "name": "Discover", + "description": "Discover the resources available on this instance." + }, + { + "name": "Route", + "description": "Ask for one or multiple routes." + } + ], + "paths": { + "/resources": { + "get": { + "tags": [ + "Discover" + ], + "summary": "Request used to discover the resources available on this instance.", + "description": "", + "operationId": "resources", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/resources" + } + } + } + }, + "404": { + "description": "Not found" + } + } + } + }, + "/resource/{resourceId}/{profileId}/{optimizationId}/route/v1/profile/{coordinates}": { + "get": { + "tags": [ + "Route" + ], + "summary": "Compute a route", + "description": "This API route is the same of [the OSRM v1 route](http://project-osrm.org/docs/v5.5.1/api/#route-service). But the profile parameter is separated in three parameters : resourceId, profileId and optimizationId. It allows us to have the concepts of Road2 and a better compatibility with the native OSRM API.", + "operationId": "route", + "parameters": [ + { + "name": "resourceId", + "in": "path", + "description": "The resource used for the compute. The list of resources is available on /resources. A resource is a concept of Road2, it is an agregation of multiple graphs. For instance, an unique topology with multiple costs.", + "required": true, + "schema": { + "type": "string" + }, + "example": "bdtopo-osrm" + }, + { + "name": "coordinates", + "in": "query", + "description": "String of format {longitude},{latitude};{longitude},{latitude}[;{longitude},{latitude} ...] or polyline({polyline}).", + "required": true, + "schema": { + "type": "string" + }, + "example": "2.337306,48.849319" + }, + { + "name": "profileId", + "in": "path", + "description": "The profile used for the compute. The list of profiles per resource is available on /resources.", + "required": true, + "schema": { + "type": "string" + }, + "example": "car" + }, + { + "name": "optimizationId", + "in": "path", + "description": "The optimization used for the compute. The list of optimizations per resource is available on /resources.", + "required": true, + "schema": { + "type": "string" + }, + "example": "fastest" + }, + { + "name": "alternatives", + "in": "query", + "description": "Search for alternative routes and return as well. Please note that even if an alternative route is requested, a result cannot be guaranteed.", + "required": false, + "schema": { + "type": "string" + }, + "example": "false" + }, + { + "name": "steps", + "in": "query", + "description": "Return route steps for each route leg.", + "required": false, + "schema": { + "type": "string" + }, + "example": "false" + }, + { + "name": "annotations", + "in": "query", + "description": "Returns additional metadata for each coordinate along the route geometry.", + "required": false, + "schema": { + "type": "string" + }, + "example": "false" + }, + { + "name": "geometries", + "in": "query", + "description": "Returned route geometry format (influences overview and per step). Values can be : polyline (default), polyline6 , geojson.", + "required": false, + "schema": { + "type": "string" + }, + "example": "polyline" + }, + { + "name": "overview", + "in": "query", + "description": "Add overview geometry either full, simplified according to highest zoom level it could be display on, or not at all. Values can be : simplified (default), full , false.", + "required": false, + "schema": { + "type": "string" + }, + "example": "simplified" + }, + { + "name": "continue_straight", + "in": "query", + "description": "Forces the route to keep going straight at waypoints constraining uturns there even if it would be faster. Default value depends on the profile. Values can be : default (default), true , false. ", + "required": false, + "schema": { + "type": "string" + }, + "example": "default" + } + ], + "responses": { + "200": { + "description": "Successful operation. For all the details, see [the OSRM documentation](http://project-osrm.org/docs/v5.5.1/api/#route-service).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/osrmValidResponse" + } + } + } + }, + "400": { + "description": "Error. For all the details, see [the OSRM documentation](http://project-osrm.org/docs/v5.5.1/api/#route-service).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/osrmErrorResponse" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "osrmErrorResponse": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "message": { + "type": "string" + } + } + }, + "osrmValidResponse": { + "type": "object", + "properties": { + "code": { + "type": "string", + "description" : "if the request was successful Ok otherwise see the service dependent and general status codes on the [the OSRM documentation](http://project-osrm.org/docs/v5.5.1/api/#route-service)." + }, + "routes": { + "type": "array", + "items": { + "type": "object", + "description": "For all the details, see the OSRM documentation of a [route object](http://project-osrm.org/docs/v5.5.1/api/#route-object)." + } + }, + "waypoints": { + "type": "array", + "items": { + "type": "object", + "description": "For all the details, see the OSRM documentation of a [waypoint object](http://project-osrm.org/docs/v5.5.1/api/#waypoint-object)." + } + } + } + }, + "resources": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "profiles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "optimizations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + } + } +} \ No newline at end of file From 2396ff7ef4f034083e53c904633841732ebc6fb1 Mon Sep 17 00:00:00 2001 From: Loic Date: Mon, 9 Oct 2023 16:39:14 +0200 Subject: [PATCH 02/12] [doc] add valhalla spec --- documentation/apis/valhalla/1.0.0/api.json | 178 +++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 documentation/apis/valhalla/1.0.0/api.json diff --git a/documentation/apis/valhalla/1.0.0/api.json b/documentation/apis/valhalla/1.0.0/api.json new file mode 100644 index 00000000..f908a172 --- /dev/null +++ b/documentation/apis/valhalla/1.0.0/api.json @@ -0,0 +1,178 @@ +{ + "openapi": "3.0.0", + "info": { + "description": "Description of the Valhalla API available via Road2. Only the isochrone API is available for now.", + "version": "1.0.0", + "title": "Valhalla API inside Road2", + "contact": { + "email": "contact.geoservices@ign.fr" + } + }, + "servers": [ + { + "url": "http://localhost:8080/valhalla/1.0.0/", + "description": "Local server" + } + ], + "tags": [ + { + "name": "Discover", + "description": "Discover the resources available on this instance." + }, + { + "name": "Isolines", + "description": "Ask for one or multiple isolines." + } + ], + "paths": { + "/resources": { + "get": { + "tags": [ + "Discover" + ], + "summary": "Request used to discover the resources available on this instance.", + "description": "", + "operationId": "resources", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/resources" + } + } + } + }, + "404": { + "description": "Not found" + } + } + } + }, + "/resource/{resourceId}/{profileId}/{optimizationId}/isochrone": { + "get": { + "tags": [ + "Isolines" + ], + "summary": "Compute a route", + "description": "This API route is the same of [the valhalla isochrone](https://valhalla.github.io/valhalla/api/isochrone/api-reference/). But there are three additional parameters : resourceId, profileId and optimizationId. It allows us to have the concepts of Road2 and a better compatibility with the native Valhalla API.", + "operationId": "isochrone", + "parameters": [ + { + "name": "resourceId", + "in": "path", + "description": "The resource used for the compute. The list of resources is available on /resources. A resource is a concept of Road2, it is an agregation of multiple graphs. For instance, an unique topology with multiple costs.", + "required": true, + "schema": { + "type": "string" + }, + "example": "bdtopo-osrm" + }, + { + "name": "profileId", + "in": "path", + "description": "The profile used for the compute. The list of profiles per resource is available on /resources.", + "required": true, + "schema": { + "type": "string" + }, + "example": "car" + }, + { + "name": "optimizationId", + "in": "path", + "description": "The optimization used for the compute. The list of optimizations per resource is available on /resources.", + "required": true, + "schema": { + "type": "string" + }, + "example": "fastest" + }, + { + "name": "json", + "in": "query", + "description": "That is a JSON which contains all the parameters for the computation. The parameter costing is always cost and the real value is given with resourceId/profileId/optimizationId. For all the details, see [the valhalla docmentation](https://valhalla.github.io/valhalla/api/isochrone/api-reference/)", + "required": false, + "schema": { + "type": "string" + }, + "example": "{\"locations\":[{\"lat\":40.744014,\"lon\":-73.990508}],\"costing\":\"cost\",\"contours\":[{\"time\":15.0,\"color\":\"ff0000\"}]}&id=Walk_From_Office" + } + ], + "responses": { + "200": { + "description": "Successful operation. The service returns a [GeoJSON](http://geojson.org/). For more details, see [the valhalla documentation](https://valhalla.github.io/valhalla/api/isochrone/api-reference/).", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "4XX": { + "description": "Error. For more details, see [the valhalla documentation](https://valhalla.github.io/valhalla/api/turn-by-turn/api-reference/#http-status-codes-and-conditions).", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "5XX": { + "description": "Error. For more details, see [the valhalla documentation](https://valhalla.github.io/valhalla/api/turn-by-turn/api-reference/#http-status-codes-and-conditions).", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "resources": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "profiles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "optimizations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + } + } + } \ No newline at end of file From 8819de2afa5c93ca7b283b4458016d6d8ffcba0f Mon Sep 17 00:00:00 2001 From: Xav Dmz Date: Wed, 15 Nov 2023 16:37:13 +0100 Subject: [PATCH 03/12] WIP: adding OSRM native API. --- docker/config/service.json | 4 + documentation/apis/osrm/1.0.0/api.json | 2 +- .../apis/osrm/1.0.0/controller/controller.js | 244 ++++ src/js/apis/osrm/1.0.0/index.js | 234 ++++ src/js/apis/osrm/1.0.0/init.js | 1017 +++++++++++++++++ src/js/apis/osrm/1.0.0/update.js | 48 + src/js/apis/simple/1.0.0/update.js | 4 +- 7 files changed, 1550 insertions(+), 3 deletions(-) create mode 100644 src/js/apis/osrm/1.0.0/controller/controller.js create mode 100644 src/js/apis/osrm/1.0.0/index.js create mode 100644 src/js/apis/osrm/1.0.0/init.js create mode 100644 src/js/apis/osrm/1.0.0/update.js diff --git a/docker/config/service.json b/docker/config/service.json index b3b4c686..1dec91f6 100644 --- a/docker/config/service.json +++ b/docker/config/service.json @@ -58,6 +58,10 @@ { "name" : "simple", "version" : "1.0.0" + }, + { + "name" : "osrm", + "version" : "1.0.0" } ] } diff --git a/documentation/apis/osrm/1.0.0/api.json b/documentation/apis/osrm/1.0.0/api.json index 024d2444..d2d7a1de 100644 --- a/documentation/apis/osrm/1.0.0/api.json +++ b/documentation/apis/osrm/1.0.0/api.json @@ -71,7 +71,7 @@ }, { "name": "coordinates", - "in": "query", + "in": "path", "description": "String of format {longitude},{latitude};{longitude},{latitude}[;{longitude},{latitude} ...] or polyline({polyline}).", "required": true, "schema": { diff --git a/src/js/apis/osrm/1.0.0/controller/controller.js b/src/js/apis/osrm/1.0.0/controller/controller.js new file mode 100644 index 00000000..89f94547 --- /dev/null +++ b/src/js/apis/osrm/1.0.0/controller/controller.js @@ -0,0 +1,244 @@ +'use strict'; + +const log4js = require('log4js'); + +const polyline = require('@mapbox/polyline'); +const Turf = require('@turf/turf'); + +const Distance = require('../../../../geography/distance'); +const Duration = require('../../../../time/duration'); +const errorManager = require('../../../../utils/errorManager'); +const NearestRequest = require('../../../../requests/nearestRequest'); +const Point = require('../../../../geometry/point'); +const RouteRequest = require('../../../../requests/routeRequest'); + +var LOGGER = log4js.getLogger("CONTROLLER"); + +module.exports = { + + /** + * + * @function + * @name checkRouteParameters + * @description Vérification des paramètres d'une requête sur /route + * @param {object} parameters - ensemble des paramètres de la requête + * @param {object} service - Instance de la classe Service + * @param {string} method - Méthode de la requête + * @return {object} RouteRequest - Instance de la classe RouteRequest + * + */ + + checkRouteParameters: function(parameters, service, method) { + + let resource; + let start = {}; + let end = {}; + let profile; + let optimization; + let tmpStringCoordinates; + let askedProjection; + + LOGGER.debug("checkRouteParameters()"); + + // Resource + if (!parameters.resource) { + throw errorManager.createError(" Parameter 'resourceId' not found ", 400); + } else { + + LOGGER.debug("user resource:"); + LOGGER.debug(parameters.resource); + + // Vérification de la disponibilité de la ressource et de la compatibilité de son type avec la requête + if (!service.verifyResourceExistenceById(parameters.resource)) { + throw errorManager.createError(" Parameter 'resourceId' is invalid: it does not exist on this service ", 400); + } else { + + resource = service.getResourceById(parameters.resource); + // On vérifie que la ressource peut accepter cette opération + if (!resource.verifyAvailabilityOperation("route")){ + throw errorManager.createError(" Operation 'route' is not permitted on this resource ", 400); + } else { + LOGGER.debug("operation route valide on this resource"); + } + + } + } + + // On récupère l'opération route pour faire des vérifications + let routeOperation = resource.getOperationById("route"); + + + // Profile and Optimization + // --- + + if (!parameters.profile) { + throw errorManager.createError(" Parameter 'profileId' not found", 400); + } else { + LOGGER.debug("user profile:"); + LOGGER.debug(parameters.profile); + // Vérification de la validité du paramètre + let validity = routeOperation.getParameterById("profile").check(parameters.profile); + if (validity.code !== "ok") { + throw errorManager.createError(" Parameter 'profileId' is invalid: " + validity.message, 400); + } else { + profile = parameters.profile; + LOGGER.debug("user profile valide"); + } + } + + if (!parameters.optimization) { + throw errorManager.createError(" Parameter 'optimizationId' not found", 400); + } else { + LOGGER.debug("user optimization:"); + LOGGER.debug(parameters.optimization); + // Vérification de la validité du paramètre + let validity = routeOperation.getParameterById("optimization").check(parameters.optimization); + if (validity.code !== "ok") { + throw errorManager.createError(" Parameter 'optimizationId' is invalid: " + validity.message, 400); + } else { + optimization = parameters.optimization; + LOGGER.debug("user optimization is valid"); + } + } + + // Paramètres spécifiques à l'API OSRM + // coordinates (2 possible formats) + if (!parameters.coordinates) { + throw errorManager.createError(" Parameter 'coordinates' not found", 400); + } else { + LOGGER.debug("raw coordinates:"); + LOGGER.debug(parameters.coordinates); +y + let raw_string_pattern = /(-?\d+(\.\d+)?,-?\d+(\.\d+)?;){1,}-?\d+(\.\d+)?,-?\d+(\.\d+)?/; + let polyline_pattern = /polyline\(\S+\)/; + + // TODO : extract coordinates in a single format + if (raw_string_pattern.test(parameters.coordinates)) { + + } else if (polyline_pattern.test(parameters.coordinates)) { + + } else { + throw errorManager.createError(" Parameter 'coordinates' is invalid: does not match allowed formats", 400); + } + + } + + + // alternatives + + // steps + + // annotations + + // geometries + + // overview + + // continue_straight + + + // On définit la routeRequest avec les paramètres obligatoires + let routeRequest = new RouteRequest(parameters.resource, start, end, profile, optimization); + + LOGGER.debug(routeRequest); + + // Vérification de la validité du profile et de sa compatibilité avec l'optimisation + if (!resource.checkSourceAvailibilityFromRequest(routeRequest)) { + throw errorManager.createError(" Parameters 'profile' and 'optimization' are not compatible ", 400); + } else { + LOGGER.debug("profile et optimization compatibles"); + } + + + + }, + + /** + * + * @function + * @name writeRouteResponse + * @description Ré-écriture de la réponse d'un moteur pour une requête sur /route + * @param {object} RouteRequest - Instance de la classe RouteRequest + * @param {object} RouteResponse - Instance de la classe RouteResponse + * @param {object} service - Instance de la classe Service + * @return {object} userResponse - Réponse envoyée à l'utilisateur + * + */ + + writeRouteResponse: function(routeRequest, routeResponse, service) { + + + }, + + /** + * + * @function + * @name convertPostArrayToGetParameters + * @description Transformation d'un paramètre POST en chaîne de caractères pour avoir l'équivalent d'un paramètre GET. + * @param {object} userParameter - Paramètre POST donné par l'utilisateur + * @param {Parameter} serviceParameter - Instance de la classe Parameter + * @param {string} parameterName - Nom du paramètre converti + * @return {string|array} Paramètre en GET + * + */ + + convertPostArrayToGetParameters: function(userParameter, serviceParameter, parameterName) { + + LOGGER.debug("convertPostArrayToGetParameters() for " + parameterName); + + let finalParameter = ""; + let separator = ""; + + if (serviceParameter.explode === "false") { + if (serviceParameter.style === "pipeDelimited") { + separator = "|"; + LOGGER.debug("separateur trouve pour ce parametre"); + } else { + // ne doit pas arriver + throw errorManager.createError(" Error in parameter configuration. "); + } + } else { + // C'est déjà un tableau qu'on retourne car c'est ce qu'on attend pour le GET + LOGGER.debug("nothing to do for this parameter"); + return userParameter; + } + + if (!Array.isArray(userParameter)) { + throw errorManager.createError(" The parameter " + parameterName + " is not an array. ", 400); + } else { + LOGGER.debug("Le parametre est un tableau"); + } + if (userParameter.length === 0) { + throw errorManager.createError(" The parameter " + parameterName + " is an empty array. ", 400); + } else { + LOGGER.debug("Le parametre est un tableau non vide"); + } + + try { + if (typeof userParameter[0] !== "object") { + finalParameter = userParameter[0]; + } else { + finalParameter = JSON.stringify(userParameter[0]); + } + } catch(err) { + throw errorManager.createError(" The parameter " + parameterName + " can't be converted to a string. ", 400); + } + + for (let i = 1; i < userParameter.length; i++) { + try { + //TODO: vérifier que l'on peut mettre i à la place de 0 + if (typeof userParameter[0] !== "object") { + finalParameter = finalParameter + separator + userParameter[i]; + } else { + finalParameter = finalParameter + separator + JSON.stringify(userParameter[i]); + } + } catch(err) { + throw errorManager.createError(" The parameter " + parameterName + " can't be converted to a string. ", 400); + } + } + + return finalParameter; + + } + +} \ No newline at end of file diff --git a/src/js/apis/osrm/1.0.0/index.js b/src/js/apis/osrm/1.0.0/index.js new file mode 100644 index 00000000..dd449ea3 --- /dev/null +++ b/src/js/apis/osrm/1.0.0/index.js @@ -0,0 +1,234 @@ +'use strict'; + + +const path = require('path'); +const express = require('express'); +const log4js = require('log4js'); +const controller = require('./controller/controller'); +const errorManager = require('../../../utils/errorManager'); +const swaggerUi = require('swagger-ui-express'); + +var LOGGER = log4js.getLogger("OSRM"); +var router = express.Router(); + +// POST +// --- +// Pour cette API, on va permettre la lecture des requêtes POST en parsant les contenus du type application/json +router.use(express.json( + // Fonctions utilisées pour vérifier le body d'un POST et ainsi récupérer les erreurs + { + type: (req) => { + // Le seul content-type accepté a toujours été application/json, on rend cela plus explicite + // Cette fonction permet d'arrêter le traitement de la requête si le content-type n'est pas correct. + // Sans elle, le traitement continue. + if (req.get('Content-Type') !== "application/json") { + throw errorManager.createError(" Wrong Content-Type. Must be 'application/json' ", 400); + } else { + return true; + } + }, + verify: (req, res, buf, encoding) => { + // Cette fonction permet de vérifier que le JSON envoyé est valide. + // Si ce n'est pas le cas, le traitement de la requête est arrêté. + try { + JSON.parse(buf); + } catch (error) { + throw errorManager.createError("Invalid request body. Error during the parsing of the body: " + error.message, 400); + } + } + } +)); +// --- + +// Accueil de l'API +router.all("/", function(req, res) { + LOGGER.debug("requete sur /osrm/1.0.0/"); + res.send("Road2 via l'API OSRM 1.0.0"); +}); + + +// swagger-ui +var apiJsonPath = path.join(__dirname, '..', '..', '..','..','..', 'documentation','apis','osrm', '1.0.0', 'api.json'); +LOGGER.info("Utilisation fichier .json '"+ apiJsonPath + "' pour initialisation swagger-ui de l'API OSRM en version 1.0.0"); +var swaggerDocument = require(apiJsonPath); +router.use('/openapi', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); + +// GetCapabilities +router.all("/resources", function(req, res) { + + LOGGER.debug("requete sur /osrm/1.0.0/resources?"); + + // récupération du service + let service = req.app.get("service"); + + // récupération du uid + let uid = service.apisManager.getApi("osrm","1.0.0").uid; + LOGGER.debug(uid); + + // récupération du getCapabilities précalculé dans init.js + let getCapabilities = req.app.get(uid + "-getcap"); + LOGGER.debug(getCapabilities); + + // Modification si Host ou X-Forwarded-Host précisè dans l'en-tête de la requête + // il est récupéré par express dans req.host + if (req.hostname) { + + // TODO : corriger avec quelque chose du genre ^http(s)?:\/\/(.+) puis split au premier / + let regexpHost = /^http[s]?:\/\/[\w\d:-_\.]*\//; + + try { + getCapabilities.info.url = getCapabilities.info.url.replace(regexpHost, req.protocol + "://" + req.hostname + "/"); + } catch(error) { + // on renvoit le getcap par défaut + } + + } else { + // il y a déjà une valeur par défaut + } + + res.set('content-type', 'application/json'); + res.status(200).json(getCapabilities); + +}); + +// Route +// Pour effectuer un calcul d'itinéraire +router.route("/resource/:resource/:profile/:optimization/route/v1/profile/:coordinates") + + .get(async function(req, res, next) { + + LOGGER.debug("requete GET sur /osrm/1.0.0/resource/"); + LOGGER.debug(req.originalUrl); + + // On récupère l'instance de Service pour faire les calculs + let service = req.app.get("service"); + + // on vérifie que l'on peut faire cette opération sur l'instance du service + if (!service.verifyAvailabilityOperation("route")) { + return next(errorManager.createError(" Operation not permitted on this service ", 400)); + } + + // on récupère l'ensemble des paramètres de la requête + let path_parameters = req.params + let query_parameters = req.query; + let parameters = {} + for (const key in query_parameters) { + parameters[key] = query_parameters[key] + } + for (const key in path_parameters) { + parameters[key] = path_parameters[key] + } + LOGGER.debug(parameters); + + try { + + // Vérification des paramètres de la requête + const routeRequest = controller.checkRouteParameters(parameters, service, "GET"); + LOGGER.debug(routeRequest); + + } catch (error) { + return next(error); + } + + + return next(errorManager.createError(" Operation not implemented yet on this service ", 501)); + + }) + + .post(async function(req, res, next) { + + LOGGER.debug("requete POST sur /osrm/1.0.0/resource/"); + LOGGER.debug(req.originalUrl); + return next(errorManager.createError(" Operation not implemented yet on this service ", 501)); + }); + + +// Gestion des erreurs +// Cette partie doit être placée après la définition des routes normales +// --- +router.use(logError); +router.use(sendError); +// Celui-ci doit être le dernier pour renvoyer un 404 si toutes les autres routes font appel à next +router.use(notFoundError); +// --- + +/** +* +* @function +* @name logError +* @description Callback pour écrire l'erreur dans les logs +* +*/ + +function logError(err, req, res, next) { + + let message = { + request: req.originalUrl, + query: req.query, + body: req.body, + error: { + errorType: err.code, + message: err.message, + stack: err.stack + } + }; + + if (err.status) { + LOGGER.debug(message); + } else { + LOGGER.error(message); + } + + next(err); +} + +/** +* +* @function +* @name sendError +* @description Callback pour envoyer l'erreur au client +* +*/ + +function sendError(err, req, res, next) { + // On ne veut pas le même comportement en prod et en dev + if (process.env.NODE_ENV === "production") { + if (err.status) { + // S'il y a un status dans le code, alors cela veut dire qu'on veut remonter l'erreur au client + res.status(err.status); + res.json({ error: {errorType: err.code, message: err.message}}); + } else { + // S'il n'y a pas de status dans le code alors on ne veut pas remonter l'erreur + res.status(500); + res.json({ error: {errorType: "internal", message: "Internal Server Error"}}); + } + } else if ((process.env.NODE_ENV === "debug")) { + res.status(err.status || 500); + res.json({ error: {errorType: err.code, + message: err.message, + stack: err.stack, + // utile lorsqu'une erreur sql remonte + more: err + }}); + } else { + // En dev, on veut faire remonter n'importe quelle erreur + res.status(err.status || 500); + res.json({ error: {errorType: err.code, message: err.message}}); + } + +} + +/** +* +* @function +* @name sendError +* @description Callback pour envoyer l'erreur au client +* +*/ + +function notFoundError(req, res) { + res.status(404); + res.send({ error: "Not found" }); +} + +module.exports = router; diff --git a/src/js/apis/osrm/1.0.0/init.js b/src/js/apis/osrm/1.0.0/init.js new file mode 100644 index 00000000..28f1fc5a --- /dev/null +++ b/src/js/apis/osrm/1.0.0/init.js @@ -0,0 +1,1017 @@ +'use strict'; + +const log4js = require('log4js'); + +var LOGGER = log4js.getLogger("INIT"); + +module.exports = { + + /** + * + * @function + * @name createGetCapabilities + * @description Fonction utilisée pour créer le GetCapabilities + * @param {object} app - App ExpressJS + * @param {string} uid - uid de l'api. Il permet de stocker des objets dans app. + * @return {boolean} True si tout s'est bien passé et False sinon + * + */ + + createGetCapabilities: function(app, uid) { + + // récupération du service + let service = app.get("service"); + + // récupération de la configuration de l'application + let globalConfiguration = service.configuration; + + //création du getCapabilities + let getCapabilities = {}; + + // info + getCapabilities.info = {}; + // info.name + getCapabilities.info.name = globalConfiguration.application.name; + // info.title + getCapabilities.info.title = globalConfiguration.application.title; + // info.description + getCapabilities.info.description = globalConfiguration.application.description; + // info.url + getCapabilities.info.url = globalConfiguration.application.url; + + // provider + getCapabilities.provider = {}; + + if (globalConfiguration.application.provider) { + + // provider.name + getCapabilities.provider.name = globalConfiguration.application.provider.name; + // provider.site + if (globalConfiguration.application.provider.site) { + getCapabilities.provider.site = globalConfiguration.application.provider.site; + } else { + getCapabilities.provider.site = ""; + } + // provider.mail + getCapabilities.provider.mail = globalConfiguration.application.provider.mail; + + } else { + getCapabilities.provider.name = ""; + getCapabilities.provider.site = ""; + getCapabilities.provider.mail = ""; + } + + // api + getCapabilities.api = {}; + // api.name + getCapabilities.api.name = "osrm"; + // api.version + getCapabilities.api.version = "1.0.0"; + + // --- operations + getCapabilities.operations = new Array(); + + // route + + // On vérifie que l'opération route est disponible et on l'intégre seulement si elle est + if (service.verifyAvailabilityOperation("route")) { + + // récupération de l'opération route du service + let serviceOpRoute = service.getOperationById("route"); + + let routeDescription = {}; + // route.id + routeDescription.id = "route"; + // route.description + routeDescription.description = serviceOpRoute.description; + // route.url + routeDescription.url = "/route?"; + // route.methods + routeDescription.methods = new Array(); + routeDescription.methods.push("GET"); + routeDescription.methods.push("POST"); + + // -- route.parameters + routeDescription.parameters = new Array(); + + // TODO: refactorer tout ce code. Une fonction qui prend en argument un paramètre et créer l'objet getCap + // route.parameters.resource + let resourceServiceParameter = serviceOpRoute.getParameterById("resource"); + let resourceParameterDescription = {}; + resourceParameterDescription.name = "resource"; + resourceParameterDescription.in = "query"; + resourceParameterDescription.description = resourceServiceParameter.description; + resourceParameterDescription.required = resourceServiceParameter.required; + resourceParameterDescription.default = resourceServiceParameter.defaultValue; + resourceParameterDescription.schema = {}; + resourceParameterDescription.schema.type = "string"; + resourceParameterDescription.example = "bduni"; + routeDescription.parameters.push(resourceParameterDescription); + + // route.parameters.start + let startServiceParameter = serviceOpRoute.getParameterById("start"); + let startParameterDescription = {}; + startParameterDescription.name = "start"; + startParameterDescription.in = "query"; + startParameterDescription.description = startServiceParameter.description; + startParameterDescription.required = startServiceParameter.required; + startParameterDescription.default = startServiceParameter.defaultValue; + startParameterDescription.schema = {}; + startParameterDescription.schema.type = "string"; + startParameterDescription.example = "2.337306,48.849319"; + routeDescription.parameters.push(startParameterDescription); + + // route.parameters.end + let endServiceParameter = serviceOpRoute.getParameterById("end"); + let endParameterDescription = {}; + endParameterDescription.name = "end"; + endParameterDescription.in = "query"; + endParameterDescription.description = endServiceParameter.description; + endParameterDescription.required = endServiceParameter.required; + endParameterDescription.default = endServiceParameter.defaultValue; + endParameterDescription.schema = {}; + endParameterDescription.schema.type = "string"; + endParameterDescription.example = "2.367776,48.852891"; + routeDescription.parameters.push(endParameterDescription); + + // route.parameters.intermediates + let intermediatesServiceParameter = serviceOpRoute.getParameterById("intermediates"); + let intermediatesParameterDescription = {}; + intermediatesParameterDescription.name = "intermediates"; + intermediatesParameterDescription.in = "query"; + intermediatesParameterDescription.description = intermediatesServiceParameter.description; + intermediatesParameterDescription.required = intermediatesServiceParameter.required; + intermediatesParameterDescription.default = intermediatesServiceParameter.defaultValue; + intermediatesParameterDescription.schema = {}; + intermediatesParameterDescription.schema.type = "array"; + intermediatesParameterDescription.schema.items = {}; + intermediatesParameterDescription.schema.items.type = "string"; + intermediatesParameterDescription.min = intermediatesServiceParameter.min; + intermediatesParameterDescription.max = intermediatesServiceParameter.max; + intermediatesParameterDescription.explode = "false"; + intermediatesParameterDescription.style = "pipeDelimited"; + intermediatesParameterDescription.example = "2.368776,48.852890|2.367976,48.842891"; + routeDescription.parameters.push(intermediatesParameterDescription); + + // route.parameters.profile + let profilesServiceParameter = serviceOpRoute.getParameterById("profile"); + let profileParameterDescription = {}; + profileParameterDescription.name = "profile"; + profileParameterDescription.in = "query"; + profileParameterDescription.description = profilesServiceParameter.description; + profileParameterDescription.required = profilesServiceParameter.required; + profileParameterDescription.default = profilesServiceParameter.defaultValue; + profileParameterDescription.schema = {}; + profileParameterDescription.schema.type = "enumeration"; + profileParameterDescription.example = "car"; + routeDescription.parameters.push(profileParameterDescription); + + // route.parameters.optimization + let optimizationServiceParameter = serviceOpRoute.getParameterById("optimization"); + let optimizationParameterDescription = {}; + optimizationParameterDescription.name = "optimization"; + optimizationParameterDescription.in = "query"; + optimizationParameterDescription.description = optimizationServiceParameter.description; + optimizationParameterDescription.required = optimizationServiceParameter.required; + optimizationParameterDescription.default = optimizationServiceParameter.defaultValue; + optimizationParameterDescription.schema = {}; + optimizationParameterDescription.schema.type = "enumeration"; + optimizationParameterDescription.example = "fastest"; + routeDescription.parameters.push(optimizationParameterDescription); + + // route.parameters.getSteps + let getStepsServiceParameter = serviceOpRoute.getParameterById("getSteps"); + let getStepsParameterDescription = {}; + getStepsParameterDescription.name = "getSteps"; + getStepsParameterDescription.in = "query"; + getStepsParameterDescription.description = getStepsServiceParameter.description; + getStepsParameterDescription.required = getStepsServiceParameter.required; + getStepsParameterDescription.default = getStepsServiceParameter.defaultValue; + getStepsParameterDescription.schema = {}; + getStepsParameterDescription.schema.type = "boolean"; + getStepsParameterDescription.example = "true"; + routeDescription.parameters.push(getStepsParameterDescription); + + // route.parameters.waysAttributes + let waysAttributesServiceParameter = serviceOpRoute.getParameterById("waysAttributes"); + let waysAttributesParameterDescription = {}; + waysAttributesParameterDescription.name = "waysAttributes"; + waysAttributesParameterDescription.in = "query"; + waysAttributesParameterDescription.description = waysAttributesServiceParameter.description; + waysAttributesParameterDescription.required = waysAttributesServiceParameter.required; + waysAttributesParameterDescription.default = waysAttributesServiceParameter.defaultValue; + waysAttributesParameterDescription.schema = {}; + waysAttributesParameterDescription.schema.type = "array"; + waysAttributesParameterDescription.schema.items = {}; + waysAttributesParameterDescription.schema.items.type = "string"; + waysAttributesParameterDescription.min = waysAttributesServiceParameter.min; + waysAttributesParameterDescription.max = waysAttributesServiceParameter.max; + waysAttributesParameterDescription.explode = "false"; + waysAttributesParameterDescription.style = "pipeDelimited"; + waysAttributesParameterDescription.example = "name|type"; + routeDescription.parameters.push(waysAttributesParameterDescription); + + // route.parameters.geometryFormat + let geometryFormatServiceParameter = serviceOpRoute.getParameterById("geometryFormat"); + let geometryFormatParameterDescription = {}; + geometryFormatParameterDescription.name = "geometryFormat"; + geometryFormatParameterDescription.in = "query"; + geometryFormatParameterDescription.description = geometryFormatServiceParameter.description; + geometryFormatParameterDescription.required = geometryFormatServiceParameter.required; + geometryFormatParameterDescription.default = geometryFormatServiceParameter.defaultValue; + geometryFormatParameterDescription.schema = {}; + geometryFormatParameterDescription.schema.type = "enumeration"; + geometryFormatParameterDescription.example = "geojson"; + routeDescription.parameters.push(geometryFormatParameterDescription); + + // route.parameters.getBbox + let getBboxServiceParameter = serviceOpRoute.getParameterById("bbox"); + let getBboxParameterDescription = {}; + getBboxParameterDescription.name = "getBbox"; + getBboxParameterDescription.in = "query"; + getBboxParameterDescription.description = getBboxServiceParameter.description; + getBboxParameterDescription.required = getBboxServiceParameter.required; + getBboxParameterDescription.default = getBboxServiceParameter.defaultValue; + getBboxParameterDescription.schema = {}; + getBboxParameterDescription.schema.type = "boolean"; + getBboxParameterDescription.example = "true"; + routeDescription.parameters.push(getBboxParameterDescription); + + // route.parameters.crs + let projectionServiceParameter = serviceOpRoute.getParameterById("projection"); + let crsParameterDescription = {}; + crsParameterDescription.name = "crs"; + crsParameterDescription.in = "query"; + crsParameterDescription.description = projectionServiceParameter.description; + crsParameterDescription.required = projectionServiceParameter.required; + crsParameterDescription.default = projectionServiceParameter.defaultValue; + crsParameterDescription.schema = {}; + crsParameterDescription.schema.type = "enumeration"; + crsParameterDescription.example = "EPSG:4326"; + routeDescription.parameters.push(crsParameterDescription); + + // route.parameters.timeUnit + let timeUnitServiceParameter = serviceOpRoute.getParameterById("timeUnit"); + let timeUnitParameterDescription = {}; + timeUnitParameterDescription.name = "timeUnit"; + timeUnitParameterDescription.in = "query"; + timeUnitParameterDescription.description = timeUnitServiceParameter.description; + timeUnitParameterDescription.required = timeUnitServiceParameter.required; + timeUnitParameterDescription.default = timeUnitServiceParameter.defaultValue; + timeUnitParameterDescription.schema = {}; + timeUnitParameterDescription.schema.type = "enumeration"; + timeUnitParameterDescription.example = "minute"; + routeDescription.parameters.push(timeUnitParameterDescription); + + // route.parameters.distanceUnit + let distanceUnitServiceParameter = serviceOpRoute.getParameterById("distanceUnit"); + let distanceUnitParameterDescription = {}; + distanceUnitParameterDescription.name = "distanceUnit"; + distanceUnitParameterDescription.in = "query"; + distanceUnitParameterDescription.description = distanceUnitServiceParameter.description; + distanceUnitParameterDescription.required = distanceUnitServiceParameter.required; + distanceUnitParameterDescription.default = distanceUnitServiceParameter.defaultValue; + distanceUnitParameterDescription.schema = {}; + distanceUnitParameterDescription.schema.type = "enumeration"; + distanceUnitParameterDescription.example = "meter"; + routeDescription.parameters.push(distanceUnitParameterDescription); + + // route.parameters.constraints + let constraintsServiceParameter = serviceOpRoute.getParameterById("constraints"); + let constraintsParameterDescription = {}; + constraintsParameterDescription.name = "constraints"; + constraintsParameterDescription.in = "query"; + constraintsParameterDescription.description = constraintsServiceParameter.description; + constraintsParameterDescription.required = constraintsServiceParameter.required; + constraintsParameterDescription.default = constraintsServiceParameter.defaultValue; + constraintsParameterDescription.schema = {}; + constraintsParameterDescription.schema.type = "array"; + constraintsParameterDescription.schema.items = {}; + constraintsParameterDescription.schema.items.type = "object"; + constraintsParameterDescription.schema.items.properties = {}; + constraintsParameterDescription.schema.items.properties.constraintType = {}; + constraintsParameterDescription.schema.items.properties.constraintType.type = "string"; + constraintsParameterDescription.schema.items.properties.key = {}; + constraintsParameterDescription.schema.items.properties.key.type = "string"; + constraintsParameterDescription.schema.items.properties.operator = {}; + constraintsParameterDescription.schema.items.properties.operator.type = "string"; + constraintsParameterDescription.schema.items.properties.value = {}; + constraintsParameterDescription.schema.items.properties.value.type = "string"; + constraintsParameterDescription.min = constraintsServiceParameter.min; + constraintsParameterDescription.max = constraintsServiceParameter.max; + constraintsParameterDescription.explode = "false"; + constraintsParameterDescription.style = "pipeDelimited"; + constraintsParameterDescription.example = "{'constraintType':'banned','key':'ways_type','operator':'=','value':'autoroute'}"; + routeDescription.parameters.push(constraintsParameterDescription); + + // -- end route.parameters + + getCapabilities.operations.push(routeDescription); + + } + // --- end route + + // isochrone + + // On vérifie que l'opération isochrone est disponible et on l'intégre seulement si elle est + if (service.verifyAvailabilityOperation("isochrone")) { + + // récupération de l'opération isochrone du service + let serviceOpIsochrone = service.getOperationById("isochrone"); + + let isochroneDescription = {}; + // isochrone.id + isochroneDescription.id = "isochrone"; + // isochrone.description + isochroneDescription.description = serviceOpIsochrone.description; + // isochrone.url + isochroneDescription.url = "/isochrone?"; + // isochrone.methods + isochroneDescription.methods = new Array(); + isochroneDescription.methods.push("GET"); + isochroneDescription.methods.push("POST"); + + // -- isochrone.parameters + isochroneDescription.parameters = new Array(); + + // isochrone.parameters.resource + let resourceServiceParameter = serviceOpIsochrone.getParameterById("resource"); + let resourceParameterDescription = {}; + resourceParameterDescription.name = "resource"; + resourceParameterDescription.in = "query"; + resourceParameterDescription.description = resourceServiceParameter.description; + resourceParameterDescription.required = resourceServiceParameter.required; + resourceParameterDescription.default = resourceServiceParameter.defaultValue; + resourceParameterDescription.schema = {}; + resourceParameterDescription.schema.type = "string"; + resourceParameterDescription.example = "bduni"; + isochroneDescription.parameters.push(resourceParameterDescription); + + // isochrone.parameters.point + let pointServiceParameter = serviceOpIsochrone.getParameterById("point"); + let pointParameterDescription = {}; + pointParameterDescription.name = "point"; + pointParameterDescription.in = "query"; + pointParameterDescription.description = pointServiceParameter.description; + pointParameterDescription.required = pointServiceParameter.required; + pointParameterDescription.default = pointServiceParameter.defaultValue; + pointParameterDescription.schema = {}; + pointParameterDescription.schema.type = "string"; + pointParameterDescription.example = "2.337306,48.849319"; + isochroneDescription.parameters.push(pointParameterDescription); + + // isochrone.parameters.costType + let costTypeServiceParameter = serviceOpIsochrone.getParameterById("costType"); + let costTypeParameterDescription = {}; + costTypeParameterDescription.name = "costType"; + costTypeParameterDescription.in = "query"; + costTypeParameterDescription.description = costTypeServiceParameter.description; + costTypeParameterDescription.required = costTypeServiceParameter.required; + costTypeParameterDescription.default = costTypeServiceParameter.defaultValue; + costTypeParameterDescription.schema = {}; + costTypeParameterDescription.schema.type = "string"; + costTypeParameterDescription.example = "time"; + isochroneDescription.parameters.push(costTypeParameterDescription); + + // isochrone.parameters.costValue + let costValueServiceParameter = serviceOpIsochrone.getParameterById("costValue"); + let costValueParameterDescription = {}; + costValueParameterDescription.name = "costValue"; + costValueParameterDescription.in = "query"; + costValueParameterDescription.description = costValueServiceParameter.description; + costValueParameterDescription.required = costValueServiceParameter.required; + costValueParameterDescription.default = costValueServiceParameter.defaultValue; + costValueParameterDescription.schema = {}; + costValueParameterDescription.schema.type = "float"; + costValueParameterDescription.example = "100"; + isochroneDescription.parameters.push(costValueParameterDescription); + + // isochrone.parameters.profile + let profileServiceParameter = serviceOpIsochrone.getParameterById("profile"); + let profileParameterDescription = {}; + profileParameterDescription.name = "profile"; + profileParameterDescription.in = "query"; + profileParameterDescription.description = profileServiceParameter.description; + profileParameterDescription.required = profileServiceParameter.required; + profileParameterDescription.default = profileServiceParameter.defaultValue; + profileParameterDescription.schema = {}; + profileParameterDescription.schema.type = "string"; + profileParameterDescription.example = "2.337306,48.849319"; + isochroneDescription.parameters.push(profileParameterDescription); + + // isochrone.parameters.direction + let directionServiceParameter = serviceOpIsochrone.getParameterById("direction"); + let directionParameterDescription = {}; + directionParameterDescription.name = "direction"; + directionParameterDescription.in = "query"; + directionParameterDescription.description = directionServiceParameter.description; + directionParameterDescription.required = directionServiceParameter.required; + directionParameterDescription.default = directionServiceParameter.defaultValue; + directionParameterDescription.schema = {}; + directionParameterDescription.schema.type = "string"; + directionParameterDescription.example = "departure"; + isochroneDescription.parameters.push(directionParameterDescription); + + // isochrone.parameters.crs + let projectionServiceParameter = serviceOpIsochrone.getParameterById("projection"); + let crsParameterDescription = {}; + crsParameterDescription.name = "crs"; + crsParameterDescription.in = "query"; + crsParameterDescription.description = projectionServiceParameter.description; + crsParameterDescription.required = projectionServiceParameter.required; + crsParameterDescription.default = projectionServiceParameter.defaultValue; + crsParameterDescription.schema = {}; + crsParameterDescription.schema.type = "enumeration"; + crsParameterDescription.example = "EPSG:4326"; + isochroneDescription.parameters.push(crsParameterDescription); + + // isochrone.parameters.geometryFormat + let geometryFormatServiceParameter = serviceOpIsochrone.getParameterById("geometryFormat"); + let geometryFormatParameterDescription = {}; + geometryFormatParameterDescription.name = "geometryFormat"; + geometryFormatParameterDescription.in = "query"; + geometryFormatParameterDescription.description = geometryFormatServiceParameter.description; + geometryFormatParameterDescription.required = geometryFormatServiceParameter.required; + geometryFormatParameterDescription.default = geometryFormatServiceParameter.defaultValue; + geometryFormatParameterDescription.schema = {}; + geometryFormatParameterDescription.schema.type = "enumeration"; + geometryFormatParameterDescription.example = "geojson"; + isochroneDescription.parameters.push(geometryFormatParameterDescription); + + // isochrone.parameters.timeUnit + let timeUnitServiceParameter = serviceOpIsochrone.getParameterById("timeUnit"); + let timeUnitParameterDescription = {}; + timeUnitParameterDescription.name = "timeUnit"; + timeUnitParameterDescription.in = "query"; + timeUnitParameterDescription.description = timeUnitServiceParameter.description; + timeUnitParameterDescription.required = timeUnitServiceParameter.required; + timeUnitParameterDescription.default = timeUnitServiceParameter.defaultValue; + timeUnitParameterDescription.schema = {}; + timeUnitParameterDescription.schema.type = "enumeration"; + timeUnitParameterDescription.example = "minute"; + isochroneDescription.parameters.push(timeUnitParameterDescription); + + // isochrone.parameters.distanceUnit + let distanceUnitServiceParameter = serviceOpIsochrone.getParameterById("distanceUnit"); + let distanceUnitParameterDescription = {}; + distanceUnitParameterDescription.name = "distanceUnit"; + distanceUnitParameterDescription.in = "query"; + distanceUnitParameterDescription.description = distanceUnitServiceParameter.description; + distanceUnitParameterDescription.required = distanceUnitServiceParameter.required; + distanceUnitParameterDescription.default = distanceUnitServiceParameter.defaultValue; + distanceUnitParameterDescription.schema = {}; + distanceUnitParameterDescription.schema.type = "enumeration"; + distanceUnitParameterDescription.example = "meter"; + isochroneDescription.parameters.push(distanceUnitParameterDescription); + + // isochrone.parameters.constraints + let constraintsServiceParameter = serviceOpIsochrone.getParameterById("constraints"); + let constraintsParameterDescription = {}; + constraintsParameterDescription.name = "constraints"; + constraintsParameterDescription.in = "query"; + constraintsParameterDescription.description = constraintsServiceParameter.description; + constraintsParameterDescription.required = constraintsServiceParameter.required; + constraintsParameterDescription.default = constraintsServiceParameter.defaultValue; + constraintsParameterDescription.schema = {}; + constraintsParameterDescription.schema.type = "array"; + constraintsParameterDescription.schema.items = {}; + constraintsParameterDescription.schema.items.type = "object"; + constraintsParameterDescription.schema.items.properties = {}; + constraintsParameterDescription.schema.items.properties.constraintType = {}; + constraintsParameterDescription.schema.items.properties.constraintType.type = "string"; + constraintsParameterDescription.schema.items.properties.key = {}; + constraintsParameterDescription.schema.items.properties.key.type = "string"; + constraintsParameterDescription.schema.items.properties.operator = {}; + constraintsParameterDescription.schema.items.properties.operator.type = "string"; + constraintsParameterDescription.schema.items.properties.value = {}; + constraintsParameterDescription.schema.items.properties.value.type = "string"; + constraintsParameterDescription.min = constraintsServiceParameter.min; + constraintsParameterDescription.max = constraintsServiceParameter.max; + constraintsParameterDescription.explode = "false"; + constraintsParameterDescription.style = "pipeDelimited"; + constraintsParameterDescription.example = "{'constraintType':'banned','key':'ways_type','operator':'=','value':'autoroute'}"; + isochroneDescription.parameters.push(constraintsParameterDescription); + + // -- end isochrone.parameters + + getCapabilities.operations.push(isochroneDescription); + + } + // -- end isochrone + + // nearest + + // On vérifie que l'opération nearest est disponible et on l'intégre seulement si elle est + if (service.verifyAvailabilityOperation("nearest")) { + + // récupération de l'opération nearest du service + let serviceOpNearest = service.getOperationById("nearest"); + + let nearestDescription = {}; + // nearest.id + nearestDescription.id = "nearest"; + // nearest.description + nearestDescription.description = serviceOpNearest.description; + // nearest.url + nearestDescription.url = "/nearest?"; + // nearest.methods + nearestDescription.methods = new Array(); + nearestDescription.methods.push("GET"); + nearestDescription.methods.push("POST"); + + // -- nearest.parameters + nearestDescription.parameters = new Array(); + + // TODO: refactorer tout ce code. Une fonction qui prend en argument un paramètre et créer l'objet getCap + // nearest.parameters.resource + let resourceServiceParameter = serviceOpNearest.getParameterById("resource"); + let resourceParameterDescription = {}; + resourceParameterDescription.name = "resource"; + resourceParameterDescription.in = "query"; + resourceParameterDescription.description = resourceServiceParameter.description; + resourceParameterDescription.required = resourceServiceParameter.required; + resourceParameterDescription.default = resourceServiceParameter.defaultValue; + resourceParameterDescription.schema = {}; + resourceParameterDescription.schema.type = "string"; + resourceParameterDescription.example = "bduni"; + nearestDescription.parameters.push(resourceParameterDescription); + + // nearest.parameters.coordinates + let coordinatesServiceParameter = serviceOpNearest.getParameterById("coordinates"); + let coordinatesParameterDescription = {}; + coordinatesParameterDescription.name = "coordinates"; + coordinatesParameterDescription.in = "query"; + coordinatesParameterDescription.description = coordinatesServiceParameter.description; + coordinatesParameterDescription.required = coordinatesServiceParameter.required; + coordinatesParameterDescription.default = coordinatesServiceParameter.defaultValue; + coordinatesParameterDescription.schema = {}; + coordinatesParameterDescription.schema.type = "string"; + coordinatesParameterDescription.example = "2.337306,48.849319"; + nearestDescription.parameters.push(coordinatesParameterDescription); + + // nearest.parameters.number + let numbersServiceParameter = serviceOpNearest.getParameterById("number"); + let numberParameterDescription = {}; + numberParameterDescription.name = "nbPoints"; + numberParameterDescription.in = "query"; + numberParameterDescription.description = numbersServiceParameter.description; + numberParameterDescription.required = numbersServiceParameter.required; + numberParameterDescription.default = numbersServiceParameter.defaultValue; + numberParameterDescription.schema = {}; + numberParameterDescription.schema.type = "integer"; + numberParameterDescription.example = 1; + nearestDescription.parameters.push(numberParameterDescription); + + // nearest.parameters.crs + let projectionServiceParameter = serviceOpNearest.getParameterById("projection"); + let crsParameterDescription = {}; + crsParameterDescription.name = "crs"; + crsParameterDescription.in = "query"; + crsParameterDescription.description = projectionServiceParameter.description; + crsParameterDescription.required = projectionServiceParameter.required; + crsParameterDescription.default = projectionServiceParameter.defaultValue; + crsParameterDescription.schema = {}; + crsParameterDescription.schema.type = "enumeration"; + crsParameterDescription.example = "EPSG:4326"; + nearestDescription.parameters.push(crsParameterDescription); + + + + // -- end nearest.parameters + + getCapabilities.operations.push(nearestDescription); + + } + // --- end nearest + + // --- end operations + + // --- resources + getCapabilities.resources = new Array(); + let resources = service.getResources(); + + for(let resourceId in resources) { + + let resourceDescription = {}; + let localResource = resources[resourceId]; + + // resource.id + resourceDescription.id = localResource.id; + + // resource.description + resourceDescription.description = localResource.configuration.description; + + // -- resource.availableOperations + resourceDescription.availableOperations = new Array(); + + // - route + + // On vérifie que l'opération route est disponible et on l'intégre seulement si elle est + if (service.verifyAvailabilityOperation("route")) { + + // on vérifie qu'elle est disponible sur la ressource + if (localResource.verifyAvailabilityOperation("route")) { + + + // on récupère l'opération de ressource + let resourceOperation = localResource.getOperationById("route"); + + let routeAvailableOperation = {}; + routeAvailableOperation.id = "route"; + routeAvailableOperation.availableParameters = new Array(); + + // route.resource + let resourceParameter = resourceOperation.getParameterById("resource"); + let routeResource = {}; + routeResource.id = "resource"; + routeResource.values = resourceParameter.values; + if (resourceParameter.serviceParameter.defaultValue === "true") { + routeResource.defaultValue = resourceParameter.defaultValueContent; + } + routeAvailableOperation.availableParameters.push(routeResource); + + // route.start + let startParameter = resourceOperation.getParameterById("start"); + let routeStart = {}; + routeStart.id = "start"; + routeStart.values = startParameter.values; + if (startParameter.serviceParameter.defaultValue === "true") { + routeStart.defaultValue = startParameter.defaultValueContent; + } + routeAvailableOperation.availableParameters.push(routeStart); + + // route.end + let endParameter = resourceOperation.getParameterById("end"); + let routeEnd = {}; + routeEnd.id = "end"; + routeEnd.values = endParameter.values; + if (endParameter.serviceParameter.defaultValue === "true") { + routeEnd.defaultValue = endParameter.defaultValueContent; + } + routeAvailableOperation.availableParameters.push(routeEnd); + + // route.intermediates + let intermediatesParameter = resourceOperation.getParameterById("intermediates"); + let routeIntermediates = {}; + routeIntermediates.id = "intermediates"; + routeIntermediates.values = intermediatesParameter.values; + if (intermediatesParameter.serviceParameter.defaultValue === "true") { + routeIntermediates.defaultValue = intermediatesParameter.defaultValueContent; + } + routeAvailableOperation.availableParameters.push(routeIntermediates); + + // route.profile + let profileParameter = resourceOperation.getParameterById("profile"); + let routeProfile = {}; + routeProfile.id = "profile"; + routeProfile.values = profileParameter.values; + if (profileParameter.serviceParameter.defaultValue === "true") { + routeProfile.defaultValue = profileParameter.defaultValueContent; + } + routeAvailableOperation.availableParameters.push(routeProfile); + + // route.optimization + let optimizationParameter = resourceOperation.getParameterById("optimization"); + let routeOptimization = {}; + routeOptimization.id = "optimization"; + routeOptimization.values = optimizationParameter.values; + if (optimizationParameter.serviceParameter.defaultValue === "true") { + routeOptimization.defaultValue = optimizationParameter.defaultValueContent; + } + routeAvailableOperation.availableParameters.push(routeOptimization); + + // route.getSteps + let getStepsParameter = resourceOperation.getParameterById("getSteps"); + let routeGetSteps = {}; + routeGetSteps.id = "getSteps"; + routeGetSteps.values = getStepsParameter.values; + if (getStepsParameter.serviceParameter.defaultValue === "true") { + routeGetSteps.defaultValue = getStepsParameter.defaultValueContent; + } + routeAvailableOperation.availableParameters.push(routeGetSteps); + + // route.waysAttributes + let waysAttributesParameter = resourceOperation.getParameterById("waysAttributes"); + let routeWaysAttributes = {}; + routeWaysAttributes.id = "waysAttributes"; + routeWaysAttributes.values = waysAttributesParameter.values; + if (waysAttributesParameter.serviceParameter.defaultValue === "true") { + routeWaysAttributes.defaultValue = waysAttributesParameter.defaultValueContent; + } + routeAvailableOperation.availableParameters.push(routeWaysAttributes); + + // route.geometryFormat + let geometryFormatParameter = resourceOperation.getParameterById("geometryFormat"); + let routeGeometriesFormat = {}; + routeGeometriesFormat.id = "geometryFormat"; + routeGeometriesFormat.values = geometryFormatParameter.values; + if (geometryFormatParameter.serviceParameter.defaultValue === "true") { + routeGeometriesFormat.defaultValue = geometryFormatParameter.defaultValueContent; + } + routeAvailableOperation.availableParameters.push(routeGeometriesFormat); + + // route.getBbox + let bboxParameter = resourceOperation.getParameterById("bbox"); + let routeGetBbox = {}; + routeGetBbox.id = "getBbox"; + routeGetBbox.values = bboxParameter.values; + if (bboxParameter.serviceParameter.defaultValue === "true") { + routeGetBbox.defaultValue = bboxParameter.defaultValueContent; + } + routeAvailableOperation.availableParameters.push(routeGetBbox); + + // route.crs + let projectionParameter = resourceOperation.getParameterById("projection"); + let routeCrs = {}; + routeCrs.id = "crs"; + routeCrs.values = projectionParameter.values; + if (projectionParameter.serviceParameter.defaultValue === "true") { + routeCrs.defaultValue = projectionParameter.defaultValueContent; + } + routeAvailableOperation.availableParameters.push(routeCrs); + + // route.timeUnit + let timeUnitParameter = resourceOperation.getParameterById("timeUnit"); + let routeTimeUnit = {}; + routeTimeUnit.id = "timeUnit"; + routeTimeUnit.values = timeUnitParameter.values; + if (timeUnitParameter.serviceParameter.defaultValue === "true") { + routeTimeUnit.defaultValue = timeUnitParameter.defaultValueContent; + } + routeAvailableOperation.availableParameters.push(routeTimeUnit); + + // route.distanceUnit + let distanceUnitParameter = resourceOperation.getParameterById("distanceUnit"); + let routeDistanceUnit = {}; + routeDistanceUnit.id = "distanceUnit"; + routeDistanceUnit.values = distanceUnitParameter.values; + if (distanceUnitParameter.serviceParameter.defaultValue === "true") { + routeDistanceUnit.defaultValue = distanceUnitParameter.defaultValueContent; + } + routeAvailableOperation.availableParameters.push(routeDistanceUnit); + + // route.constraints + let constraintsParameter = resourceOperation.getParameterById("constraints"); + let routeConstraints = {}; + routeConstraints.id = "constraints"; + routeConstraints.values = constraintsParameter.getcapabilities; + if (constraintsParameter.serviceParameter.defaultValue === "true") { + routeConstraints.defaultValue = constraintsParameter.defaultValueContent; + } + routeAvailableOperation.availableParameters.push(routeConstraints); + + resourceDescription.availableOperations.push(routeAvailableOperation); + + } + + } + // - end route + + // - isochrone + + // On vérifie que l'opération isochrone est disponible et on l'intégre seulement si elle est + if (service.verifyAvailabilityOperation("isochrone")) { + + // on vérifie qu'elle est disponible sur la ressource + if (localResource.verifyAvailabilityOperation("isochrone")) { + + + // on récupère l'opération de ressource + let resourceOperation = localResource.getOperationById("isochrone"); + + let isochroneAvailableOperation = {}; + isochroneAvailableOperation.id = "isochrone"; + isochroneAvailableOperation.availableParameters = new Array(); + + // isochrone.resource + let resourceParameter = resourceOperation.getParameterById("resource"); + let isochroneResource = {}; + isochroneResource.id = "resource"; + isochroneResource.values = resourceParameter.values; + if (resourceParameter.serviceParameter.defaultValue === "true") { + isochroneResource.defaultValue = resourceParameter.defaultValueContent; + } + isochroneAvailableOperation.availableParameters.push(isochroneResource); + + // isochrone.point + let pointParameter = resourceOperation.getParameterById("point"); + let isochronePoint = {}; + isochronePoint.id = "point"; + isochronePoint.values = pointParameter.values; + if (pointParameter.serviceParameter.defaultValue === "true") { + isochronePoint.defaultValue = pointParameter.defaultValueContent; + } + isochroneAvailableOperation.availableParameters.push(isochronePoint); + + // isochrone.costType + let costTypeParameter = resourceOperation.getParameterById("costType"); + let isochroneCostType = {}; + isochroneCostType.id = "costType"; + isochroneCostType.values = costTypeParameter.values; + if (costTypeParameter.serviceParameter.defaultValue === "true") { + isochroneCostType.defaultValue = costTypeParameter.defaultValueContent; + } + isochroneAvailableOperation.availableParameters.push(isochroneCostType); + + // isochrone.costValue + let costValueParameter = resourceOperation.getParameterById("costValue"); + let isochroneCostValue = {}; + isochroneCostValue.id = "costValue"; + isochroneCostValue.values = costValueParameter.values; + if (costValueParameter.serviceParameter.defaultValue === "true") { + isochroneCostValue.defaultValue = costValueParameter.defaultValueContent; + } + isochroneAvailableOperation.availableParameters.push(isochroneCostValue); + + // isochrone.profile + let profileParameter = resourceOperation.getParameterById("profile"); + let isochroneProfile = {}; + isochroneProfile.id = "profile"; + isochroneProfile.values = profileParameter.values; + if (profileParameter.serviceParameter.defaultValue === "true") { + isochroneProfile.defaultValue = profileParameter.defaultValueContent; + } + isochroneAvailableOperation.availableParameters.push(isochroneProfile); + + // isochrone.direction + let directionParameter = resourceOperation.getParameterById("direction"); + let isochroneDirection = {}; + isochroneDirection.id = "direction"; + isochroneDirection.values = directionParameter.values; + if (directionParameter.serviceParameter.defaultValue === "true") { + isochroneDirection.defaultValue = directionParameter.defaultValueContent; + } + isochroneAvailableOperation.availableParameters.push(isochroneDirection); + + // isochrone.crs + let projectionParameter = resourceOperation.getParameterById("projection"); + let isochroneProjection = {}; + isochroneProjection.id = "projection"; + isochroneProjection.values = projectionParameter.values; + if (projectionParameter.serviceParameter.defaultValue === "true") { + isochroneProjection.defaultValue = projectionParameter.defaultValueContent; + } + isochroneAvailableOperation.availableParameters.push(isochroneProjection); + + + // isochrone.geometryFormat + let geometryFormatParameter = resourceOperation.getParameterById("geometryFormat"); + let isochroneGeometriesFormat = {}; + isochroneGeometriesFormat.id = "geometryFormat"; + isochroneGeometriesFormat.values = geometryFormatParameter.values; + if (geometryFormatParameter.serviceParameter.defaultValue === "true") { + isochroneGeometriesFormat.defaultValue = geometryFormatParameter.defaultValueContent; + } + isochroneAvailableOperation.availableParameters.push(isochroneGeometriesFormat); + + // isochrone.timeUnit + let timeUnitParameter = resourceOperation.getParameterById("timeUnit"); + let isochroneTimeUnit = {}; + isochroneTimeUnit.id = "timeUnit"; + isochroneTimeUnit.values = timeUnitParameter.values; + if (timeUnitParameter.serviceParameter.defaultValue === "true") { + isochroneTimeUnit.defaultValue = timeUnitParameter.defaultValueContent; + } + isochroneAvailableOperation.availableParameters.push(isochroneTimeUnit); + + // isochrone.distanceUnit + let distanceUnitParameter = resourceOperation.getParameterById("distanceUnit"); + let isochroneDistanceUnit = {}; + isochroneDistanceUnit.id = "distanceUnit"; + isochroneDistanceUnit.values = distanceUnitParameter.values; + if (distanceUnitParameter.serviceParameter.defaultValue === "true") { + isochroneDistanceUnit.defaultValue = distanceUnitParameter.defaultValueContent; + } + isochroneAvailableOperation.availableParameters.push(isochroneDistanceUnit); + + // isochrone.constraints + let constraintsParameter = resourceOperation.getParameterById("constraints"); + let isochroneConstraints = {}; + isochroneConstraints.id = "constraints"; + isochroneConstraints.values = constraintsParameter.getcapabilities; + if (constraintsParameter.serviceParameter.defaultValue === "true") { + isochroneConstraints.defaultValue = constraintsParameter.defaultValueContent; + } + isochroneAvailableOperation.availableParameters.push(isochroneConstraints); + + resourceDescription.availableOperations.push(isochroneAvailableOperation); + + } + + } + // - end isochrone + + // - nearest + + // On vérifie que l'opération nearest est disponible et on l'intégre seulement si elle est + if (service.verifyAvailabilityOperation("nearest")) { + + // on vérifie qu'elle est disponible sur la ressource + if (localResource.verifyAvailabilityOperation("nearest")) { + + + // on récupère l'opération de ressource + let resourceOperation = localResource.getOperationById("nearest"); + + let nearestAvailableOperation = {}; + nearestAvailableOperation.id = "nearest"; + nearestAvailableOperation.availableParameters = new Array(); + + // nearest.resource + let resourceParameter = resourceOperation.getParameterById("resource"); + let nearestResource = {}; + nearestResource.id = "resource"; + nearestResource.values = resourceParameter.values; + if (resourceParameter.serviceParameter.defaultValue === "true") { + nearestResource.defaultValue = resourceParameter.defaultValueContent; + } + nearestAvailableOperation.availableParameters.push(nearestResource); + + // nearest.coordinates + let coordinatesParameter = resourceOperation.getParameterById("coordinates"); + let nearestCoordinates = {}; + nearestCoordinates.id = "coordinates"; + nearestCoordinates.values = coordinatesParameter.values; + if (coordinatesParameter.serviceParameter.defaultValue === "true") { + nearestCoordinates.defaultValue = coordinatesParameter.defaultValueContent; + } + nearestAvailableOperation.availableParameters.push(nearestCoordinates); + + // nearest.number + let numberParameter = resourceOperation.getParameterById("number"); + let nearestNumber = {}; + nearestNumber.id = "nbPoints"; + nearestNumber.values = numberParameter.values; + if (numberParameter.serviceParameter.defaultValue === "true") { + nearestNumber.defaultValue = numberParameter.defaultValueContent; + } + nearestAvailableOperation.availableParameters.push(nearestNumber); + + // nearest.crs + let projectionParameter = resourceOperation.getParameterById("projection"); + let nearestCrs = {}; + nearestCrs.id = "crs"; + nearestCrs.values = projectionParameter.values; + if (projectionParameter.serviceParameter.defaultValue === "true") { + nearestCrs.defaultValue = projectionParameter.defaultValueContent; + } + nearestAvailableOperation.availableParameters.push(nearestCrs); + + resourceDescription.availableOperations.push(nearestAvailableOperation); + + } + + } + // - end nearest + + // -- end resource.availableOperations + + + + getCapabilities.resources.push(resourceDescription); + + } // end for(let resourceId in resources) + + // --- end resources + + // sauvegarde du getCapabilities + app.set(uid + "-getcap", getCapabilities); + + return true; + + }, + + /** + * + * @function + * @name run + * @description Fonction lancée avant la mise en service du serveur. + * @param {object} app - App ExpressJS + * @param {string} uid - uid de l'api. Il permet de stocker des objets dans app. + * @return {boolean} True si tout s'est bien passé et False sinon + * + */ + + run: function(app, uid) { + try { + + // TODO: vérification que l'ensemble des opérations et paramètres soient disponibles + // ils sont utilisés dans l'api mais leur existence n'est pas vérifiée + + // Création du GetCapabilities + if (!this.createGetCapabilities(app, uid)) { + LOGGER.error("Erreur lors de la creation du GetCapabilities."); + return false; + } else { + // tout s'est bien passé + } + return true; + + } catch (err) { + LOGGER.error("Erreur lors de la creation du GetCapabilities.", err); + return false; + } + + } + +} diff --git a/src/js/apis/osrm/1.0.0/update.js b/src/js/apis/osrm/1.0.0/update.js new file mode 100644 index 00000000..32cff8d2 --- /dev/null +++ b/src/js/apis/osrm/1.0.0/update.js @@ -0,0 +1,48 @@ +'use strict'; + +const log4js = require('log4js'); + +var LOGGER = log4js.getLogger("INIT"); + +module.exports = { + + /** + * + * @function + * @name updateGetCapabilities + * @description Fonction utilisée pour mettre à jour le GetCapabilities + * @param {object} app - App ExpressJS + * @return {boolean} True si tout s'est bien passé et False sinon + * + */ + + updateGetCapabilities: function(app) { + + return true; + + }, + + /** + * + * @function + * @name run + * @description Fonction lancée lors d'une MAJ sur le serveur. + * @param {object} app - App ExpressJS + * @param {string} uid - uid de l'api. Il permet de stocker des objets dans app. + * @return {boolean} True si tout s'est bien passé et False sinon + * + */ + + run: function(app, uid) { + + // Création du GetCapabilities + if (!this.updateGetCapabilities(app)) { + LOGGER.error("Erreur lors de la creation du GetCapabilities."); + return false; + } + + return true; + + } + +} diff --git a/src/js/apis/simple/1.0.0/update.js b/src/js/apis/simple/1.0.0/update.js index 141acd34..32cff8d2 100644 --- a/src/js/apis/simple/1.0.0/update.js +++ b/src/js/apis/simple/1.0.0/update.js @@ -9,8 +9,8 @@ module.exports = { /** * * @function - * @name createGetCapabilities - * @description Fonction utilisée pour créer le GetCapabilities + * @name updateGetCapabilities + * @description Fonction utilisée pour mettre à jour le GetCapabilities * @param {object} app - App ExpressJS * @return {boolean} True si tout s'est bien passé et False sinon * From 3fc1ae9b57ccaef22fb2152875e215f4cffcddaf Mon Sep 17 00:00:00 2001 From: Xav Dmz Date: Mon, 20 Nov 2023 11:38:14 +0100 Subject: [PATCH 04/12] WIP: OSRM native API: routing coordinates validation. --- .../apis/osrm/1.0.0/controller/controller.js | 85 +++++++++++++++++-- 1 file changed, 77 insertions(+), 8 deletions(-) diff --git a/src/js/apis/osrm/1.0.0/controller/controller.js b/src/js/apis/osrm/1.0.0/controller/controller.js index 89f94547..fe5f1a58 100644 --- a/src/js/apis/osrm/1.0.0/controller/controller.js +++ b/src/js/apis/osrm/1.0.0/controller/controller.js @@ -36,7 +36,8 @@ module.exports = { let profile; let optimization; let tmpStringCoordinates; - let askedProjection; + let coordinatesSequence; + let defaultProjection; LOGGER.debug("checkRouteParameters()"); @@ -66,6 +67,8 @@ module.exports = { // On récupère l'opération route pour faire des vérifications let routeOperation = resource.getOperationById("route"); + defaultProjection = routeOperation.getParameterById("projection").defaultValueContent; + LOGGER.debug("default crs: " + defaultProjection); // Profile and Optimization @@ -108,19 +111,85 @@ module.exports = { } else { LOGGER.debug("raw coordinates:"); LOGGER.debug(parameters.coordinates); -y - let raw_string_pattern = /(-?\d+(\.\d+)?,-?\d+(\.\d+)?;){1,}-?\d+(\.\d+)?,-?\d+(\.\d+)?/; - let polyline_pattern = /polyline\(\S+\)/; - // TODO : extract coordinates in a single format - if (raw_string_pattern.test(parameters.coordinates)) { - - } else if (polyline_pattern.test(parameters.coordinates)) { + let rawStringPattern = /^(-?\d+(\.\d+)?,-?\d+(\.\d+)?;){1,}-?\d+(\.\d+)?,-?\d+(\.\d+)?$/; + let polylinePattern = /^polyline\(\S+\)$/; + // TODO : extract coordinates in a single format + if (rawStringPattern.test(parameters.coordinates)) { + coordinatesSequence = [] + const coordinatesMatchList = parameters.coordinates.matchAll(/(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?)/g) + for (const matchItem in coordinatesMatchList) { + coordinatesSequence.push([parseFloat(matchItem[1]), parseFloat(matchItem[2])]); + } + } else if (polylinePattern.test(parameters.coordinates)) { + coordinatesSequence = polyline.decode(parameters.coordinates); } else { throw errorManager.createError(" Parameter 'coordinates' is invalid: does not match allowed formats", 400); } + LOGGER.debug("coordinates sequence:"); + LOGGER.debug(coordinatesSequence); + + if (coordinatesSequence.length >= 2) { + // Route start point + parameters.start = coordinatesSequence[0].join(","); + LOGGER.debug("user start:"); + LOGGER.debug(parameters.start); + let validity = routeOperation.getParameterById("start").check(parameters.start, defaultProjection); + if (validity.code !== "ok") { + throw errorManager.createError(" Parameter 'start' is invalid: " + validity.message, 400); + } else { + LOGGER.debug("user start valide") + start = new Point(coordinatesSequence[0][0], coordinatesSequence[0][1], defaultProjection); + LOGGER.debug("user start in road2' object:"); + LOGGER.debug(start); + } + validity = null; + + // Route end point + parameters.end = coordinatesSequence[coordinatesSequence.length-1].join(","); + LOGGER.debug("user end:"); + LOGGER.debug(parameters.end); + validity = routeOperation.getParameterById("end").check(parameters.end, askedProjection); + if (validity.code !== "ok") { + throw errorManager.createError(" Parameter 'end' is invalid: " + validity.message, 400); + } else { + LOGGER.debug("user end valide") + end = new Point(coordinatesSequence[coordinatesSequence.length-1][0], coordinatesSequence[coordinatesSequence.length-1][1], defaultProjection); + LOGGER.debug("user end in road2' object:"); + LOGGER.debug(end); + } + } else { + throw errorManager.createError(" Parameter 'coordinates' is invalid: it must contain at least two points"); + } + if (coordinatesSequence.length > 2) { + + LOGGER.debug("user intermediates:"); + LOGGER.debug(coordinatesSequence.slice(1, coordinatesSequence.length-1)); + + let finalIntermediates = ""; + for (let i = 1; i < (coordinatesSequence.length - 2); i++) { + finalIntermediates = finalIntermediates.concat(coordinatesSequence[i].join(","), "|"); + } + finalIntermediates = finalIntermediates.concat(coordinatesSequence[coordinatesSequence.length - 2].join(",")); + + // Vérification de la validité des coordonnées fournies + let validity = routeOperation.getParameterById("intermediates").check(finalIntermediates, defaultProjection); + if (validity.code !== "ok") { + throw errorManager.createError(" Parameter 'coordinates' is invalid: " + validity.message, 400); + } else { + + LOGGER.debug("intermediates valides"); + + if (!routeOperation.getParameterById("intermediates").convertIntoTable(finalIntermediates, routeRequest.intermediates, defaultProjection)) { + throw errorManager.createError(" Parameter 'intermediates' is invalid. Wrong format or out of the bbox. ", 400); + } else { + LOGGER.debug("intermediates in a table:"); + LOGGER.debug(routeRequest.intermediates); + } + } + } } From ae726d81634788843d5b18544c74cfe578d19f4e Mon Sep 17 00:00:00 2001 From: Xav Dmz Date: Thu, 11 Jan 2024 17:33:28 +0100 Subject: [PATCH 05/12] WIP: Request parameters implemented when they match routeRequest's parameters, and ignored when not. OSRM controller's writeRouteResponse is being implemnted, but it will need modifications of RouteResponse class. --- documentation/apis/osrm/1.0.0/api.json | 2 +- .../apis/osrm/1.0.0/controller/controller.js | 254 ++++++++++-------- src/js/apis/osrm/1.0.0/index.js | 6 +- 3 files changed, 146 insertions(+), 116 deletions(-) diff --git a/documentation/apis/osrm/1.0.0/api.json b/documentation/apis/osrm/1.0.0/api.json index d2d7a1de..3ccd6078 100644 --- a/documentation/apis/osrm/1.0.0/api.json +++ b/documentation/apis/osrm/1.0.0/api.json @@ -50,7 +50,7 @@ } } }, - "/resource/{resourceId}/{profileId}/{optimizationId}/route/v1/profile/{coordinates}": { + "/{resourceId}/{profileId}/{optimizationId}/route/v1/_/{coordinates}": { "get": { "tags": [ "Route" diff --git a/src/js/apis/osrm/1.0.0/controller/controller.js b/src/js/apis/osrm/1.0.0/controller/controller.js index fe5f1a58..892db8c7 100644 --- a/src/js/apis/osrm/1.0.0/controller/controller.js +++ b/src/js/apis/osrm/1.0.0/controller/controller.js @@ -20,11 +20,11 @@ module.exports = { * * @function * @name checkRouteParameters - * @description Vérification des paramètres d'une requête sur /route - * @param {object} parameters - ensemble des paramètres de la requête - * @param {object} service - Instance de la classe Service - * @param {string} method - Méthode de la requête - * @return {object} RouteRequest - Instance de la classe RouteRequest + * @description Check parameters for a request on /route + * @param {object} parameters - request parameters + * @param {object} service - Service class' instance + * @param {string} method - request method + * @return {object} RouteRequest - RouteRequest class' instance * */ @@ -49,13 +49,13 @@ module.exports = { LOGGER.debug("user resource:"); LOGGER.debug(parameters.resource); - // Vérification de la disponibilité de la ressource et de la compatibilité de son type avec la requête + // Check resource availability, and compatibility between its type and the request if (!service.verifyResourceExistenceById(parameters.resource)) { throw errorManager.createError(" Parameter 'resourceId' is invalid: it does not exist on this service ", 400); } else { resource = service.getResourceById(parameters.resource); - // On vérifie que la ressource peut accepter cette opération + // Check if this operation is allowed for this resource if (!resource.verifyAvailabilityOperation("route")){ throw errorManager.createError(" Operation 'route' is not permitted on this resource ", 400); } else { @@ -65,7 +65,7 @@ module.exports = { } } - // On récupère l'opération route pour faire des vérifications + // Get route operation to check some things let routeOperation = resource.getOperationById("route"); defaultProjection = routeOperation.getParameterById("projection").defaultValueContent; LOGGER.debug("default crs: " + defaultProjection); @@ -79,7 +79,7 @@ module.exports = { } else { LOGGER.debug("user profile:"); LOGGER.debug(parameters.profile); - // Vérification de la validité du paramètre + // Parameter's validity check let validity = routeOperation.getParameterById("profile").check(parameters.profile); if (validity.code !== "ok") { throw errorManager.createError(" Parameter 'profileId' is invalid: " + validity.message, 400); @@ -94,7 +94,7 @@ module.exports = { } else { LOGGER.debug("user optimization:"); LOGGER.debug(parameters.optimization); - // Vérification de la validité du paramètre + // Parameter's validity check let validity = routeOperation.getParameterById("optimization").check(parameters.optimization); if (validity.code !== "ok") { throw errorManager.createError(" Parameter 'optimizationId' is invalid: " + validity.message, 400); @@ -104,7 +104,7 @@ module.exports = { } } - // Paramètres spécifiques à l'API OSRM + // OSRM API specific parameters // coordinates (2 possible formats) if (!parameters.coordinates) { throw errorManager.createError(" Parameter 'coordinates' not found", 400); @@ -162,63 +162,117 @@ module.exports = { } else { throw errorManager.createError(" Parameter 'coordinates' is invalid: it must contain at least two points"); } + } - if (coordinatesSequence.length > 2) { + // Instanciate routeRequest with mandatory parameters + let routeRequest = new RouteRequest(parameters.resource, start, end, profile, optimization); - LOGGER.debug("user intermediates:"); - LOGGER.debug(coordinatesSequence.slice(1, coordinatesSequence.length-1)); + LOGGER.debug(routeRequest); - let finalIntermediates = ""; - for (let i = 1; i < (coordinatesSequence.length - 2); i++) { - finalIntermediates = finalIntermediates.concat(coordinatesSequence[i].join(","), "|"); - } - finalIntermediates = finalIntermediates.concat(coordinatesSequence[coordinatesSequence.length - 2].join(",")); + // Check profile's validity and compatibility with the chosen optimization + if (!resource.checkSourceAvailibilityFromRequest(routeRequest)) { + throw errorManager.createError(" Parameters 'profile' and 'optimization' are not compatible ", 400); + } else { + LOGGER.debug("profile et optimization compatibles"); + } - // Vérification de la validité des coordonnées fournies - let validity = routeOperation.getParameterById("intermediates").check(finalIntermediates, defaultProjection); - if (validity.code !== "ok") { - throw errorManager.createError(" Parameter 'coordinates' is invalid: " + validity.message, 400); - } else { + // Intermediate points + if (coordinatesSequence.length > 2) { - LOGGER.debug("intermediates valides"); + LOGGER.debug("user intermediates:"); + LOGGER.debug(coordinatesSequence.slice(1, coordinatesSequence.length-1)); - if (!routeOperation.getParameterById("intermediates").convertIntoTable(finalIntermediates, routeRequest.intermediates, defaultProjection)) { - throw errorManager.createError(" Parameter 'intermediates' is invalid. Wrong format or out of the bbox. ", 400); - } else { - LOGGER.debug("intermediates in a table:"); - LOGGER.debug(routeRequest.intermediates); - } + let finalIntermediates = ""; + for (let i = 1; i < (coordinatesSequence.length - 2); i++) { + finalIntermediates = finalIntermediates.concat(coordinatesSequence[i].join(","), "|"); + } + finalIntermediates = finalIntermediates.concat(coordinatesSequence[coordinatesSequence.length - 2].join(",")); + + // Check coordinates validity + let validity = routeOperation.getParameterById("intermediates").check(finalIntermediates, defaultProjection); + if (validity.code !== "ok") { + throw errorManager.createError(" Parameter 'coordinates' is invalid: " + validity.message, 400); + } else { + + LOGGER.debug("valid intermediates"); + + if (!routeOperation.getParameterById("intermediates").convertIntoTable(finalIntermediates, routeRequest.intermediates, defaultProjection)) { + throw errorManager.createError(" Parameter 'intermediates' is invalid. Wrong format or out of the bbox. ", 400); + } else { + LOGGER.debug("intermediates in a table:"); + LOGGER.debug(routeRequest.intermediates); } } } + // steps (OSRM) / getSteps (Road2) + if (parameters.steps) { + LOGGER.debug("user getSteps:"); + LOGGER.debug(parameters.steps); - // alternatives + // Check coordinates validity + let validity = routeOperation.getParameterById("getSteps").check(parameters.steps); + if (validity.code !== "ok") { + throw errorManager.createError(" Parameter 'steps' is invalid: " + validity.message, 400); + } else { - // steps + routeRequest.computeSteps = routeOperation.getParameterById("getSteps").specificConvertion(parameters.steps) + if (routeRequest.computeSteps === null) { + throw errorManager.createError(" Parameter 'steps' is invalid ", 400); + } else { + LOGGER.debug("converted getSteps: " + routeRequest.computeSteps); + } - // annotations + } else if ("defaultValueContent" in routeOperation.getParameterById("getSteps") && typeof(routeOperation.getParameterById("getSteps").defaultValueContent) !== "undefined") { + // Default value from configuration + routeRequest.computeSteps = routeOperation.getParameterById("getSteps").defaultValueContent; + LOGGER.debug("configuration default getSteps: " + routeRequest.computeSteps); + } else { + routeRequest.computeSteps = false; + LOGGER.debug("general default getSteps: " + routeRequest.computeSteps); + } - // geometries + // geometries (OSRM) / geometryFormat (Road2) + if (parameters.geometries) { - // overview + LOGGER.debug("user geometryFormat:"); + LOGGER.debug(parameters.geometries); - // continue_straight + // Check coordinates validity + let validity = routeOperation.getParameterById("geometryFormat").check(parameters.geometries); + if (validity.code !== "ok") { + throw errorManager.createError(" Parameter 'geometries' is invalid: " + validity.message, 400); + } else { + LOGGER.debug("geometryFormat valide"); + } + routeRequest.geometryFormat = parameters.geometries; - // On définit la routeRequest avec les paramètres obligatoires - let routeRequest = new RouteRequest(parameters.resource, start, end, profile, optimization); + } else { - LOGGER.debug(routeRequest); + // Default value from configuration + routeRequest.geometryFormat = routeOperation.getParameterById("geometryFormat").defaultValueContent; + LOGGER.debug("default geometryFormat used: " + routeRequest.geometryFormat); - // Vérification de la validité du profile et de sa compatibilité avec l'optimisation - if (!resource.checkSourceAvailibilityFromRequest(routeRequest)) { - throw errorManager.createError(" Parameters 'profile' and 'optimization' are not compatible ", 400); - } else { - LOGGER.debug("profile et optimization compatibles"); } + /* The following OSRM optional parameters are ignored for now, as they don't seem to match any routeRequest property: + - alternatives + - annotations + - bearings + - continue_straight (can maybe be converted to a "constraints" routeRequest property) + - format + - hints + - overview (always "full" in road2) + - radiuses + */ + // optional routeRequest parameters with no OSRM equivalent are ignored or fixed + + routeRequest.bbox = false + routeRequest.distanceUnit = "meter" + routeRequest.timeUnit = "second" + return routeRequest; }, @@ -236,77 +290,53 @@ module.exports = { writeRouteResponse: function(routeRequest, routeResponse, service) { - - }, - - /** - * - * @function - * @name convertPostArrayToGetParameters - * @description Transformation d'un paramètre POST en chaîne de caractères pour avoir l'équivalent d'un paramètre GET. - * @param {object} userParameter - Paramètre POST donné par l'utilisateur - * @param {Parameter} serviceParameter - Instance de la classe Parameter - * @param {string} parameterName - Nom du paramètre converti - * @return {string|array} Paramètre en GET - * - */ - - convertPostArrayToGetParameters: function(userParameter, serviceParameter, parameterName) { - - LOGGER.debug("convertPostArrayToGetParameters() for " + parameterName); - - let finalParameter = ""; - let separator = ""; - - if (serviceParameter.explode === "false") { - if (serviceParameter.style === "pipeDelimited") { - separator = "|"; - LOGGER.debug("separateur trouve pour ce parametre"); - } else { - // ne doit pas arriver - throw errorManager.createError(" Error in parameter configuration. "); - } - } else { - // C'est déjà un tableau qu'on retourne car c'est ce qu'on attend pour le GET - LOGGER.debug("nothing to do for this parameter"); - return userParameter; - } - - if (!Array.isArray(userParameter)) { - throw errorManager.createError(" The parameter " + parameterName + " is not an array. ", 400); - } else { - LOGGER.debug("Le parametre est un tableau"); - } - if (userParameter.length === 0) { - throw errorManager.createError(" The parameter " + parameterName + " is an empty array. ", 400); - } else { - LOGGER.debug("Le parametre est un tableau non vide"); - } - - try { - if (typeof userParameter[0] !== "object") { - finalParameter = userParameter[0]; - } else { - finalParameter = JSON.stringify(userParameter[0]); - } - } catch(err) { - throw errorManager.createError(" The parameter " + parameterName + " can't be converted to a string. ", 400); - } - - for (let i = 1; i < userParameter.length; i++) { - try { - //TODO: vérifier que l'on peut mettre i à la place de 0 - if (typeof userParameter[0] !== "object") { - finalParameter = finalParameter + separator + userParameter[i]; - } else { - finalParameter = finalParameter + separator + JSON.stringify(userParameter[i]); + LOGGER.debug("writeRouteResponse()"); + + let userResponse = { + "code": "", + "routes": [], + "waypoints": [] + }; + for (let route in routeResponse.routes) { + let outputRoute = { + "distance": route.distance, + "duration": route.duration, + "geometry": route.geometry.getGeometryWithFormat(routeRequest.geometryFormat), + "legs": [] + }; + + for (let i = 0; i < route.portions.length; i++) { + let portion = route.portions[i] + let leg = { + "distance": portion.distance, + "duration": portion.duration, + "steps": [] + }; + if (userResponse.waypoints.length < (route.portions.length + 1)) { + let coordsPattern = /^(\-?\d+(?:\.\d+)?),\s*(\-?\d+(?:\.\d+)?)$/; + let waypoint = { + "hint": "", + "distance": NaN, + "name": "", + "location": portion.start.match(coordsPattern).slice(1, 2) + }; + userResponse.waypoints.push(waypoint); + + if (i == (route.portions.length - 1)) { + let finalWaypoint= { + "hint": "", + "distance": NaN, + "name": "", + "location": portion.end.match(coordsPattern).slice(1, 2) + }; + userResponse.waypoints.push(finalWaypoint); + } } - } catch(err) { - throw errorManager.createError(" The parameter " + parameterName + " can't be converted to a string. ", 400); } + + userResponse.routes.push(outputRoute) } - return finalParameter; } diff --git a/src/js/apis/osrm/1.0.0/index.js b/src/js/apis/osrm/1.0.0/index.js index dd449ea3..36945710 100644 --- a/src/js/apis/osrm/1.0.0/index.js +++ b/src/js/apis/osrm/1.0.0/index.js @@ -93,11 +93,11 @@ router.all("/resources", function(req, res) { // Route // Pour effectuer un calcul d'itinéraire -router.route("/resource/:resource/:profile/:optimization/route/v1/profile/:coordinates") +router.route("/:resource/:profile/:optimization/route/v1/_/:coordinates") .get(async function(req, res, next) { - LOGGER.debug("requete GET sur /osrm/1.0.0/resource/"); + LOGGER.debug("requete GET sur /osrm/1.0.0/:resource"); LOGGER.debug(req.originalUrl); // On récupère l'instance de Service pour faire les calculs @@ -137,7 +137,7 @@ router.route("/resource/:resource/:profile/:optimization/route/v1/profile/:coord .post(async function(req, res, next) { - LOGGER.debug("requete POST sur /osrm/1.0.0/resource/"); + LOGGER.debug("requete POST sur /osrm/1.0.0/:resource/"); LOGGER.debug(req.originalUrl); return next(errorManager.createError(" Operation not implemented yet on this service ", 501)); }); From 72dfcf7ddf0b052220032527bbf9431c1712900c Mon Sep 17 00:00:00 2001 From: Xav Dmz Date: Mon, 15 Jan 2024 18:03:24 +0100 Subject: [PATCH 06/12] WIP: implementing response writing for an OSRM native routing request. Slight edition of routeResponse and osrmSource classes to do so. --- .../apis/osrm/1.0.0/controller/controller.js | 71 +++++++++---------- src/js/apis/osrm/1.0.0/index.js | 6 +- src/js/responses/routeResponse.js | 28 +++++++- src/js/sources/osrmSource.js | 42 ++++++++++- 4 files changed, 104 insertions(+), 43 deletions(-) diff --git a/src/js/apis/osrm/1.0.0/controller/controller.js b/src/js/apis/osrm/1.0.0/controller/controller.js index 892db8c7..cc0fb2c2 100644 --- a/src/js/apis/osrm/1.0.0/controller/controller.js +++ b/src/js/apis/osrm/1.0.0/controller/controller.js @@ -280,11 +280,11 @@ module.exports = { * * @function * @name writeRouteResponse - * @description Ré-écriture de la réponse d'un moteur pour une requête sur /route - * @param {object} RouteRequest - Instance de la classe RouteRequest - * @param {object} RouteResponse - Instance de la classe RouteResponse - * @param {object} service - Instance de la classe Service - * @return {object} userResponse - Réponse envoyée à l'utilisateur + * @description Rewrite engine's response to respond to a /route request + * @param {object} RouteRequest - RouteRequest class instance + * @param {object} RouteResponse - RouteResponse class instance + * @param {object} service - Service class instance + * @return {object} userResponse - Response body to serialize for the user * */ @@ -292,49 +292,44 @@ module.exports = { LOGGER.debug("writeRouteResponse()"); + // Initialize userResponse let userResponse = { - "code": "", - "routes": [], - "waypoints": [] + "code": routeResponse.engineExtras.code }; - for (let route in routeResponse.routes) { - let outputRoute = { - "distance": route.distance, - "duration": route.duration, - "geometry": route.geometry.getGeometryWithFormat(routeRequest.geometryFormat), + + let askedProjection = routeRequest.start.projection; + + // Waypoints + let waypointArray = JSON.parse(JSON.stringify(routeResponse.engineExtras.waypoints)); + let startingPoint = routeResponse.routes[0].portions[0].start; + waypointArray[0].location = [startingPoint.x, startingPoint.y]; + for (let i = 1; i < waypointArray.length; i++) { + let point = routeResponse.routes[0].portions[i-1].end; + waypointArray[i].location = [point.x, point.y]; + } + userResponse.waypoints = waypointArray; + + let routeArray = new Array(); + for (let routeIdx = 0; routeIdx < routeResponse.routes.length; routeIdx++) { + // for (let route in routeResponse.routes) { + let simpleRoute = routeResponse.routes[routeIdx]; + let extraRoute = routeResponse.engineExtras.routes[routeIdx]; + routeArray[routeIdx] = { + "distance": simpleRoute.distance, + "duration": simpleRoute.duration, + "geometry": simpleRoute.geometry.getGeometryWithFormat(routeRequest.geometryFormat), "legs": [] }; - for (let i = 0; i < route.portions.length; i++) { - let portion = route.portions[i] - let leg = { + let legArray = new Array(); + for (let legIdx = 0; legIdx < route.portions.length; legIdx++) { + let portion = route.portions[legIdx] + legArray = { "distance": portion.distance, "duration": portion.duration, "steps": [] }; - if (userResponse.waypoints.length < (route.portions.length + 1)) { - let coordsPattern = /^(\-?\d+(?:\.\d+)?),\s*(\-?\d+(?:\.\d+)?)$/; - let waypoint = { - "hint": "", - "distance": NaN, - "name": "", - "location": portion.start.match(coordsPattern).slice(1, 2) - }; - userResponse.waypoints.push(waypoint); - - if (i == (route.portions.length - 1)) { - let finalWaypoint= { - "hint": "", - "distance": NaN, - "name": "", - "location": portion.end.match(coordsPattern).slice(1, 2) - }; - userResponse.waypoints.push(finalWaypoint); - } - } } - - userResponse.routes.push(outputRoute) } diff --git a/src/js/apis/osrm/1.0.0/index.js b/src/js/apis/osrm/1.0.0/index.js index 36945710..ca584114 100644 --- a/src/js/apis/osrm/1.0.0/index.js +++ b/src/js/apis/osrm/1.0.0/index.js @@ -126,9 +126,7 @@ router.route("/:resource/:profile/:optimization/route/v1/_/:coordinates") const routeRequest = controller.checkRouteParameters(parameters, service, "GET"); LOGGER.debug(routeRequest); - } catch (error) { - return next(error); - } + } return next(errorManager.createError(" Operation not implemented yet on this service ", 501)); @@ -137,6 +135,8 @@ router.route("/:resource/:profile/:optimization/route/v1/_/:coordinates") .post(async function(req, res, next) { + } catch (error) { + return next(error); LOGGER.debug("requete POST sur /osrm/1.0.0/:resource/"); LOGGER.debug(req.originalUrl); return next(errorManager.createError(" Operation not implemented yet on this service ", 501)); diff --git a/src/js/responses/routeResponse.js b/src/js/responses/routeResponse.js index 5bcea103..53b878ec 100644 --- a/src/js/responses/routeResponse.js +++ b/src/js/responses/routeResponse.js @@ -42,13 +42,16 @@ module.exports = class routeResponse extends Response { // profile this._profile = profile; - // optmization + // optimization this._optimization = optimization; // Itinéraires //Tableau contenant l'ensemble des itinéraires calculés par le moteur this._routes = new Array(); + // Informations spécifiques à un moteur et son API native + this._engineExtras = {}; + } /** @@ -189,4 +192,27 @@ module.exports = class routeResponse extends Response { this._routes = st; } + /** + * + * @function + * @name get engineExtras + * @description Récupérer les propriétés non génériques, spécifiques au moteur et à son API + * + */ + get engineExtras () { + return this._engineExtras; + } + + /** + * + * @function + * @name set engineExtras + * @description Attribuer les propriétés non génériques, spécifiques au moteur et à son API + * @param {object} ee - Dictionnaire de propriétés spécifiques au moteur et à son API + * + */ + set engineExtras (ee) { + this._engineExtras = ee; + } + } diff --git a/src/js/sources/osrmSource.js b/src/js/sources/osrmSource.js index b163f569..0820a707 100644 --- a/src/js/sources/osrmSource.js +++ b/src/js/sources/osrmSource.js @@ -372,6 +372,11 @@ module.exports = class osrmSource extends Source { let profile; let optimization; let routes = new Array(); + let engineExtras = { + "code": "", + "routes": new Array(), + "waypoints": new Array() + }; // Récupération des paramètres de la requête que l'on veut transmettre dans la réponse // --- @@ -385,6 +390,11 @@ module.exports = class osrmSource extends Source { optimization = routeRequest.optimization; // --- + // Récupération des paramètres de la réponse OSRM pour une éventuelle réponse avec son API native + engineExtras.code = osrmResponse.code; + + // --- + // Lecture de la réponse OSRM // --- @@ -428,6 +438,15 @@ module.exports = class osrmSource extends Source { LOGGER.debug("osrm response has 1 or more routes"); } + for (let sourceWaypoint in osrmResponse.waypoints) { + let nativeWaypoint = { + "hint": sourceWaypoint.hint, + "distance": sourceWaypoint.distance, + "name": sourceWaypoint.name + }; + engineExtras.waypoints.push(nativeWaypoint); + } + // routes // Il peut y avoir plusieurs itinéraires for (let i = 0; i < osrmResponse.routes.length; i++) { @@ -436,6 +455,8 @@ module.exports = class osrmSource extends Source { let portions = new Array(); let currentOsrmRoute = osrmResponse.routes[i]; + engineExtras.routes[i] = {}; + nativeLegs = new Array(); // On commence par créer l'itinéraire avec les attributs obligatoires routes[i] = new Route( new Line(currentOsrmRoute.geometry, "geojson", super.projection) ); @@ -474,13 +495,15 @@ module.exports = class osrmSource extends Source { let legEnd = new Point(osrmResponse.waypoints[j+1].location[0], osrmResponse.waypoints[j+1].location[1], super.projection); if (!legEnd.transform(askedProjection)) { - throw errorManager.createError(" Error during reprojection of leg end in OSRM response. "); + throw errorManager.createError(" Error during reprojection of leg end in OSRM response. "); } else { LOGGER.debug("portion end in asked projection:"); LOGGER.debug(legEnd); } + portions[j] = new Portion(legStart, legEnd); + nativeLegs[j] = {}; // On récupère la distance et la durée portions[j].distance = new Distance(currentOsrmRouteLeg.distance,"meter"); @@ -488,6 +511,7 @@ module.exports = class osrmSource extends Source { // Steps let steps = new Array(); + let nativeSteps = new Array(); // On va associer les étapes à la portion concernée for (let k=0; k < currentOsrmRouteLeg.steps.length; k++) { @@ -524,17 +548,33 @@ module.exports = class osrmSource extends Source { if (currentOsrmRouteStep.maneuver.exit) { steps[k].instruction.exit = currentOsrmRouteStep.maneuver.exit; } + + nativeSteps[k] = {}; + nativeSteps[k].mode = currentOsrmRouteStep.mode; + nativeIntersections = new Array(); + for (let intersectionIndex = 0; intersectionIndex < currentOsrmRouteStep.intersections.length; intersectionIndex++) { + let currentIntersection = currentOsrmRouteStep.intersections[intersectionIndex]; + nativeIntersections[intersectionIndex] = JSON.parse(JSON.stringify(currentIntersection)); + nativeIntersections[intersectionIndex].location = new Point(currentIntersection.location[0], currentIntersection.location[1], super.projection) + if (!nativeIntersections[intersectionIndex].location.transform(askedProjection)) { + throw errorManager.createError(" Error during reprojection of intersection in OSRM response. "); + } + } + nativeSteps[k].intersections = nativeIntersections; } portions[j].steps = steps; + nativeLegs[j].steps = nativeSteps; } routes[i].portions = portions; + engineExtras.routes[i].legs = nativeLegs; } routeResponse.routes = routes; + routeResponse.engineExtras = engineExtras: return routeResponse; From d0cfe3b86d441fa54802782dce5556c7b5a0ad92 Mon Sep 17 00:00:00 2001 From: Xav Dmz Date: Tue, 16 Jan 2024 16:36:03 +0100 Subject: [PATCH 07/12] WIP: OSRM controller implemented, but unpolished and untested. TODO : * complete logging and varible check * test the code --- .../apis/osrm/1.0.0/controller/controller.js | 67 ++++++++--- src/js/apis/osrm/1.0.0/index.js | 111 ++++++------------ src/js/sources/osrmSource.js | 5 +- 3 files changed, 92 insertions(+), 91 deletions(-) diff --git a/src/js/apis/osrm/1.0.0/controller/controller.js b/src/js/apis/osrm/1.0.0/controller/controller.js index cc0fb2c2..977a7315 100644 --- a/src/js/apis/osrm/1.0.0/controller/controller.js +++ b/src/js/apis/osrm/1.0.0/controller/controller.js @@ -5,6 +5,7 @@ const log4js = require('log4js'); const polyline = require('@mapbox/polyline'); const Turf = require('@turf/turf'); +const copyManager = require('../../../../utils/copyManager') const Distance = require('../../../../geography/distance'); const Duration = require('../../../../time/duration'); const errorManager = require('../../../../utils/errorManager'); @@ -296,11 +297,10 @@ module.exports = { let userResponse = { "code": routeResponse.engineExtras.code }; - let askedProjection = routeRequest.start.projection; // Waypoints - let waypointArray = JSON.parse(JSON.stringify(routeResponse.engineExtras.waypoints)); + let waypointArray = copyManager.deepCopy(routeResponse.engineExtras.waypoints); let startingPoint = routeResponse.routes[0].portions[0].start; waypointArray[0].location = [startingPoint.x, startingPoint.y]; for (let i = 1; i < waypointArray.length; i++) { @@ -309,30 +309,65 @@ module.exports = { } userResponse.waypoints = waypointArray; + // Routes let routeArray = new Array(); for (let routeIdx = 0; routeIdx < routeResponse.routes.length; routeIdx++) { - // for (let route in routeResponse.routes) { - let simpleRoute = routeResponse.routes[routeIdx]; - let extraRoute = routeResponse.engineExtras.routes[routeIdx]; - routeArray[routeIdx] = { - "distance": simpleRoute.distance, - "duration": simpleRoute.duration, - "geometry": simpleRoute.geometry.getGeometryWithFormat(routeRequest.geometryFormat), - "legs": [] - }; + let simpleRoute = routeResponse.routes[routeIdx]; // from road2 standard response + let extraRoute = routeResponse.engineExtras.routes[routeIdx]; // from engine specific extras + // both sources will be fused to craft a response compliant with OSRM's official API definition + // Legs (Road2's "portions") let legArray = new Array(); - for (let legIdx = 0; legIdx < route.portions.length; legIdx++) { - let portion = route.portions[legIdx] - legArray = { + for (let legIdx = 0; legIdx < simpleRoute.portions.length; legIdx++) { + let portion = simpleRoute.portions[legIdx]; // from road2 standard response + let leg = extraRoute.legs[legIdx]; // from engine specific extras + legArray[legIdx] = { "distance": portion.distance, - "duration": portion.duration, - "steps": [] + "duration": portion.duration }; + + // Steps (optional) + let stepArray = new Array(); + if (routeRequest.computeSteps && portion.steps.length !== 0) { + for (let stepIdx = 0; stepIdx < portion.steps.length; stepIdx++) { + let simpleStep = portion.steps[stepIdx]; // from road2 standard response + let extraStep = leg.steps[stepIdx]; // from engine specific extras + stepArray[stepIdx] = { + "distance": simpleStep.distance, + "duration": simpleStep.duration, + "geometry": simpleStep.geometry.getGeometryWithFormat(routeRequest.geometryFormat), + "intersections": copyManager.deepCopy(extraStep.intersections), + "maneuver": { + "type": extraStep.instruction.type + }, + "mode": extraStep.mode, + "name": simpleStep.name + }; + if (simpleStep.instruction.modifier) { + stepArray[stepIdx].maneuver.modifier = simpleStep.instruction.modifier; + } + if (simpleStep.instruction.exit) { + stepArray[stepIdx].maneuver.exit = simpleStep.instruction.exit; + } + } + legArray[legIdx].steps = stepArray; + } else { + LOGGER.debug("no steps asked by user"); + } } + + routeArray[routeIdx] = { + "distance": simpleRoute.distance, + "duration": simpleRoute.duration, + "geometry": simpleRoute.geometry.getGeometryWithFormat(routeRequest.geometryFormat), + "legs": legArray + }; } + // Finalze userResponse + userResponse.routes = routeArray; + return userResponse; } } \ No newline at end of file diff --git a/src/js/apis/osrm/1.0.0/index.js b/src/js/apis/osrm/1.0.0/index.js index ca584114..eda1f6b7 100644 --- a/src/js/apis/osrm/1.0.0/index.js +++ b/src/js/apis/osrm/1.0.0/index.js @@ -11,79 +11,47 @@ const swaggerUi = require('swagger-ui-express'); var LOGGER = log4js.getLogger("OSRM"); var router = express.Router(); -// POST -// --- -// Pour cette API, on va permettre la lecture des requêtes POST en parsant les contenus du type application/json -router.use(express.json( - // Fonctions utilisées pour vérifier le body d'un POST et ainsi récupérer les erreurs - { - type: (req) => { - // Le seul content-type accepté a toujours été application/json, on rend cela plus explicite - // Cette fonction permet d'arrêter le traitement de la requête si le content-type n'est pas correct. - // Sans elle, le traitement continue. - if (req.get('Content-Type') !== "application/json") { - throw errorManager.createError(" Wrong Content-Type. Must be 'application/json' ", 400); - } else { - return true; - } - }, - verify: (req, res, buf, encoding) => { - // Cette fonction permet de vérifier que le JSON envoyé est valide. - // Si ce n'est pas le cas, le traitement de la requête est arrêté. - try { - JSON.parse(buf); - } catch (error) { - throw errorManager.createError("Invalid request body. Error during the parsing of the body: " + error.message, 400); - } - } - } -)); -// --- - -// Accueil de l'API +// API entrypoint router.all("/", function(req, res) { - LOGGER.debug("requete sur /osrm/1.0.0/"); - res.send("Road2 via l'API OSRM 1.0.0"); + LOGGER.debug("request on /osrm/1.0.0/"); + res.send("Road2 via OSRM API 1.0.0"); }); // swagger-ui var apiJsonPath = path.join(__dirname, '..', '..', '..','..','..', 'documentation','apis','osrm', '1.0.0', 'api.json'); -LOGGER.info("Utilisation fichier .json '"+ apiJsonPath + "' pour initialisation swagger-ui de l'API OSRM en version 1.0.0"); +LOGGER.info("using file '"+ apiJsonPath + "' to initialize swagger-ui for OSRM API version 1.0.0"); var swaggerDocument = require(apiJsonPath); router.use('/openapi', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); // GetCapabilities router.all("/resources", function(req, res) { - LOGGER.debug("requete sur /osrm/1.0.0/resources?"); + LOGGER.debug("request on /osrm/1.0.0/resources?"); - // récupération du service + // get service let service = req.app.get("service"); - // récupération du uid + // get uid let uid = service.apisManager.getApi("osrm","1.0.0").uid; LOGGER.debug(uid); - // récupération du getCapabilities précalculé dans init.js + // get getCapabilities from init.js let getCapabilities = req.app.get(uid + "-getcap"); LOGGER.debug(getCapabilities); - // Modification si Host ou X-Forwarded-Host précisè dans l'en-tête de la requête - // il est récupéré par express dans req.host + // Change base url in GetCapabilties if "Host" or "X-Forwarded-Host" is specified in request's headers + // Host is stored by expres in req.hostname if (req.hostname) { - - // TODO : corriger avec quelque chose du genre ^http(s)?:\/\/(.+) puis split au premier / let regexpHost = /^http[s]?:\/\/[\w\d:-_\.]*\//; - try { getCapabilities.info.url = getCapabilities.info.url.replace(regexpHost, req.protocol + "://" + req.hostname + "/"); } catch(error) { - // on renvoit le getcap par défaut + // apply default service url in GetCapabilities } } else { - // il y a déjà une valeur par défaut + // apply default service url in GetCapabilities } res.set('content-type', 'application/json'); @@ -91,24 +59,23 @@ router.all("/resources", function(req, res) { }); -// Route -// Pour effectuer un calcul d'itinéraire +// Route: routing request router.route("/:resource/:profile/:optimization/route/v1/_/:coordinates") .get(async function(req, res, next) { - LOGGER.debug("requete GET sur /osrm/1.0.0/:resource"); + LOGGER.debug("GET request on /osrm/1.0.0/:resource"); LOGGER.debug(req.originalUrl); - // On récupère l'instance de Service pour faire les calculs + // get service instance let service = req.app.get("service"); - // on vérifie que l'on peut faire cette opération sur l'instance du service + // check if operation is permitted on this service instance if (!service.verifyAvailabilityOperation("route")) { return next(errorManager.createError(" Operation not permitted on this service ", 400)); } - // on récupère l'ensemble des paramètres de la requête + // get request parameters, both from path and query let path_parameters = req.params let query_parameters = req.query; let parameters = {} @@ -122,33 +89,31 @@ router.route("/:resource/:profile/:optimization/route/v1/_/:coordinates") try { - // Vérification des paramètres de la requête + // Check request parameters const routeRequest = controller.checkRouteParameters(parameters, service, "GET"); LOGGER.debug(routeRequest); + // Send to service and get response object + const routeResponse = await service.computeRequest(routeRequest); + LOGGER.debug(routeResponse); + // Format response + const userResponse = controller.writeRouteResponse(routeRequest, routeResponse, service); + LOGGER.debug(userResponse); - } - - - return next(errorManager.createError(" Operation not implemented yet on this service ", 501)); - - }) - - .post(async function(req, res, next) { + res.set('content-type', 'application/json'); + res.status(200).json(userResponse); } catch (error) { return next(error); - LOGGER.debug("requete POST sur /osrm/1.0.0/:resource/"); - LOGGER.debug(req.originalUrl); - return next(errorManager.createError(" Operation not implemented yet on this service ", 501)); + } }); -// Gestion des erreurs -// Cette partie doit être placée après la définition des routes normales +// Error management +// This part must be placed after normal routes definitions // --- router.use(logError); router.use(sendError); -// Celui-ci doit être le dernier pour renvoyer un 404 si toutes les autres routes font appel à next +// This must be the last item to send an HTTP 404 error if evry route calls next. router.use(notFoundError); // --- @@ -156,7 +121,7 @@ router.use(notFoundError); * * @function * @name logError -* @description Callback pour écrire l'erreur dans les logs +* @description Callback to log error * */ @@ -186,19 +151,19 @@ function logError(err, req, res, next) { * * @function * @name sendError -* @description Callback pour envoyer l'erreur au client +* @description Callback to send error to client * */ function sendError(err, req, res, next) { - // On ne veut pas le même comportement en prod et en dev + // Behaviour should differ between production and development environments if (process.env.NODE_ENV === "production") { if (err.status) { - // S'il y a un status dans le code, alors cela veut dire qu'on veut remonter l'erreur au client + // if error has a status, this error should be sent to the client res.status(err.status); res.json({ error: {errorType: err.code, message: err.message}}); } else { - // S'il n'y a pas de status dans le code alors on ne veut pas remonter l'erreur + // if error has no status, this error's details should not be sent to the client res.status(500); res.json({ error: {errorType: "internal", message: "Internal Server Error"}}); } @@ -207,11 +172,11 @@ function sendError(err, req, res, next) { res.json({ error: {errorType: err.code, message: err.message, stack: err.stack, - // utile lorsqu'une erreur sql remonte + // useful for SQL errors more: err }}); } else { - // En dev, on veut faire remonter n'importe quelle erreur + // in the development environment, every error should be sent to clients res.status(err.status || 500); res.json({ error: {errorType: err.code, message: err.message}}); } @@ -222,7 +187,7 @@ function sendError(err, req, res, next) { * * @function * @name sendError -* @description Callback pour envoyer l'erreur au client +* @description Callback to send HTTP "Not Found" error to client * */ diff --git a/src/js/sources/osrmSource.js b/src/js/sources/osrmSource.js index 0820a707..9db9ed7b 100644 --- a/src/js/sources/osrmSource.js +++ b/src/js/sources/osrmSource.js @@ -10,6 +10,7 @@ const Point = require('../geometry/point'); const Step = require('../responses/step'); const Distance = require('../geography/distance'); const Duration = require('../time/duration'); +const copyManager = require('../../../../utils/copyManager') const errorManager = require('../utils/errorManager'); const log4js = require('log4js'); @@ -554,7 +555,7 @@ module.exports = class osrmSource extends Source { nativeIntersections = new Array(); for (let intersectionIndex = 0; intersectionIndex < currentOsrmRouteStep.intersections.length; intersectionIndex++) { let currentIntersection = currentOsrmRouteStep.intersections[intersectionIndex]; - nativeIntersections[intersectionIndex] = JSON.parse(JSON.stringify(currentIntersection)); + nativeIntersections[intersectionIndex] = copyManager.deepCopy(currentIntersection); nativeIntersections[intersectionIndex].location = new Point(currentIntersection.location[0], currentIntersection.location[1], super.projection) if (!nativeIntersections[intersectionIndex].location.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of intersection in OSRM response. "); @@ -574,7 +575,7 @@ module.exports = class osrmSource extends Source { } routeResponse.routes = routes; - routeResponse.engineExtras = engineExtras: + routeResponse.engineExtras = engineExtras; return routeResponse; From 724e6a51bb12773497cd4df1081fcd7df1ce0534 Mon Sep 17 00:00:00 2001 From: Xav Dmz Date: Wed, 17 Jan 2024 16:39:48 +0100 Subject: [PATCH 08/12] OSRM routing request seems to work. TODO: * check result to see if every property is valid and exact. * adapt getcapabilities to OSRM API capabilities. --- docker/distributions/debian/Dockerfile | 2 +- .../apis/osrm/1.0.0/controller/controller.js | 117 +++++++++++++----- src/js/apis/osrm/1.0.0/index.js | 10 +- src/js/apis/osrm/1.0.0/init.js | 2 +- src/js/apis/osrm/1.0.0/update.js | 2 +- src/js/sources/osrmSource.js | 6 +- 6 files changed, 94 insertions(+), 45 deletions(-) diff --git a/docker/distributions/debian/Dockerfile b/docker/distributions/debian/Dockerfile index eb8dbc8d..07b6feaa 100644 --- a/docker/distributions/debian/Dockerfile +++ b/docker/distributions/debian/Dockerfile @@ -28,7 +28,7 @@ RUN apt-get update && \ libsqlite3-mod-spatialite libzmq3-dev libczmq-dev ### Installation prime-server -COPY --from=build /usr/local/lib/libprime_server.so.0.7.0 /usr/lib/libprime_server.so.0.0.0 +COPY --from=build /usr/local/lib/libprime_server.so.0.7.1 /usr/lib/libprime_server.so.0.0.0 COPY --from=build /usr/local/lib/libprime_server.so.0 /usr/lib/libprime_server.so.0 COPY --from=build /usr/local/lib/libprime_server.so /usr/lib/libprime_server.so diff --git a/src/js/apis/osrm/1.0.0/controller/controller.js b/src/js/apis/osrm/1.0.0/controller/controller.js index 977a7315..522d195d 100644 --- a/src/js/apis/osrm/1.0.0/controller/controller.js +++ b/src/js/apis/osrm/1.0.0/controller/controller.js @@ -3,17 +3,15 @@ const log4js = require('log4js'); const polyline = require('@mapbox/polyline'); -const Turf = require('@turf/turf'); -const copyManager = require('../../../../utils/copyManager') +const copyManager = require('../../../../utils/copyManager'); const Distance = require('../../../../geography/distance'); const Duration = require('../../../../time/duration'); const errorManager = require('../../../../utils/errorManager'); -const NearestRequest = require('../../../../requests/nearestRequest'); const Point = require('../../../../geometry/point'); const RouteRequest = require('../../../../requests/routeRequest'); -var LOGGER = log4js.getLogger("CONTROLLER"); +let LOGGER = log4js.getLogger("CONTROLLER"); module.exports = { @@ -36,9 +34,8 @@ module.exports = { let end = {}; let profile; let optimization; - let tmpStringCoordinates; let coordinatesSequence; - let defaultProjection; + let askedProjection; LOGGER.debug("checkRouteParameters()"); @@ -68,8 +65,8 @@ module.exports = { // Get route operation to check some things let routeOperation = resource.getOperationById("route"); - defaultProjection = routeOperation.getParameterById("projection").defaultValueContent; - LOGGER.debug("default crs: " + defaultProjection); + askedProjection = routeOperation.getParameterById("projection").defaultValueContent; + LOGGER.debug("default crs: " + askedProjection); // Profile and Optimization @@ -113,17 +110,22 @@ module.exports = { LOGGER.debug("raw coordinates:"); LOGGER.debug(parameters.coordinates); - let rawStringPattern = /^(-?\d+(\.\d+)?,-?\d+(\.\d+)?;){1,}-?\d+(\.\d+)?,-?\d+(\.\d+)?$/; + let rawStringPattern = /^-?\d+(\.\d+)?,-?\d+(\.\d+)?(;-?\d+(\.\d+)?,-?\d+(\.\d+)?)+$/; let polylinePattern = /^polyline\(\S+\)$/; - // TODO : extract coordinates in a single format if (rawStringPattern.test(parameters.coordinates)) { - coordinatesSequence = [] - const coordinatesMatchList = parameters.coordinates.matchAll(/(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?)/g) - for (const matchItem in coordinatesMatchList) { + LOGGER.debug("coordinates are expressed in list format"); + coordinatesSequence = []; + const coordinatesMatchList = parameters.coordinates.matchAll(/(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?)/g); + LOGGER.debug("coordinates matches list:"); + LOGGER.debug(coordinatesMatchList); + for (const matchItem of coordinatesMatchList) { + LOGGER.debug("coordinates match:"); + LOGGER.debug(matchItem); coordinatesSequence.push([parseFloat(matchItem[1]), parseFloat(matchItem[2])]); } } else if (polylinePattern.test(parameters.coordinates)) { + LOGGER.debug("coordinates are expressed in polyline format"); coordinatesSequence = polyline.decode(parameters.coordinates); } else { throw errorManager.createError(" Parameter 'coordinates' is invalid: does not match allowed formats", 400); @@ -136,12 +138,12 @@ module.exports = { parameters.start = coordinatesSequence[0].join(","); LOGGER.debug("user start:"); LOGGER.debug(parameters.start); - let validity = routeOperation.getParameterById("start").check(parameters.start, defaultProjection); + let validity = routeOperation.getParameterById("start").check(parameters.start, askedProjection); if (validity.code !== "ok") { throw errorManager.createError(" Parameter 'start' is invalid: " + validity.message, 400); } else { LOGGER.debug("user start valide") - start = new Point(coordinatesSequence[0][0], coordinatesSequence[0][1], defaultProjection); + start = new Point(coordinatesSequence[0][0], coordinatesSequence[0][1], askedProjection); LOGGER.debug("user start in road2' object:"); LOGGER.debug(start); } @@ -156,7 +158,7 @@ module.exports = { throw errorManager.createError(" Parameter 'end' is invalid: " + validity.message, 400); } else { LOGGER.debug("user end valide") - end = new Point(coordinatesSequence[coordinatesSequence.length-1][0], coordinatesSequence[coordinatesSequence.length-1][1], defaultProjection); + end = new Point(coordinatesSequence[coordinatesSequence.length-1][0], coordinatesSequence[coordinatesSequence.length-1][1], askedProjection); LOGGER.debug("user end in road2' object:"); LOGGER.debug(end); } @@ -190,14 +192,14 @@ module.exports = { finalIntermediates = finalIntermediates.concat(coordinatesSequence[coordinatesSequence.length - 2].join(",")); // Check coordinates validity - let validity = routeOperation.getParameterById("intermediates").check(finalIntermediates, defaultProjection); + let validity = routeOperation.getParameterById("intermediates").check(finalIntermediates, askedProjection); if (validity.code !== "ok") { throw errorManager.createError(" Parameter 'coordinates' is invalid: " + validity.message, 400); } else { LOGGER.debug("valid intermediates"); - if (!routeOperation.getParameterById("intermediates").convertIntoTable(finalIntermediates, routeRequest.intermediates, defaultProjection)) { + if (!routeOperation.getParameterById("intermediates").convertIntoTable(finalIntermediates, routeRequest.intermediates, askedProjection)) { throw errorManager.createError(" Parameter 'intermediates' is invalid. Wrong format or out of the bbox. ", 400); } else { LOGGER.debug("intermediates in a table:"); @@ -223,7 +225,7 @@ module.exports = { } else { LOGGER.debug("converted getSteps: " + routeRequest.computeSteps); } - + } } else if ("defaultValueContent" in routeOperation.getParameterById("getSteps") && typeof(routeOperation.getParameterById("getSteps").defaultValueContent) !== "undefined") { // Default value from configuration routeRequest.computeSteps = routeOperation.getParameterById("getSteps").defaultValueContent; @@ -297,7 +299,8 @@ module.exports = { let userResponse = { "code": routeResponse.engineExtras.code }; - let askedProjection = routeRequest.start.projection; + LOGGER.debug("Engine response code :"); + LOGGER.debug(userResponse.code); // Waypoints let waypointArray = copyManager.deepCopy(routeResponse.engineExtras.waypoints); @@ -308,23 +311,58 @@ module.exports = { waypointArray[i].location = [point.x, point.y]; } userResponse.waypoints = waypointArray; + LOGGER.debug("Waypoints :"); + LOGGER.debug(userResponse.waypoints); // Routes let routeArray = new Array(); for (let routeIdx = 0; routeIdx < routeResponse.routes.length; routeIdx++) { let simpleRoute = routeResponse.routes[routeIdx]; // from road2 standard response - let extraRoute = routeResponse.engineExtras.routes[routeIdx]; // from engine specific extras + let extraRoute = routeResponse.engineExtras.routes[routeIdx]; // from engine specific extrasz // both sources will be fused to craft a response compliant with OSRM's official API definition + // Geometry + routeArray[routeIdx] = {}; + routeArray[routeIdx].geometry = simpleRoute.geometry.getGeometryWithFormat(routeRequest.geometryFormat); + LOGGER.debug("Route " + routeIdx + "'s geometry: " + userResponse.geometry); + + // Distance + if (!simpleRoute.distance.convert(routeRequest.distanceUnit)) { + throw errorManager.createError(" Error during conversion of route distance in response. ", 400); + } else { + routeArray[routeIdx].distance = simpleRoute.distance.value; + } + + // Duration + if (!simpleRoute.duration.convert(routeRequest.timeUnit)) { + throw errorManager.createError(" Error during conversion of route duration in response. ", 400); + } else { + routeArray[routeIdx].duration = simpleRoute.duration.value; + } + // Legs (Road2's "portions") let legArray = new Array(); for (let legIdx = 0; legIdx < simpleRoute.portions.length; legIdx++) { + LOGGER.debug("Computing leg " + legIdx + " of route " + routeIdx); let portion = simpleRoute.portions[legIdx]; // from road2 standard response let leg = extraRoute.legs[legIdx]; // from engine specific extras - legArray[legIdx] = { - "distance": portion.distance, - "duration": portion.duration - }; + legArray[legIdx] = {}; + + // Distance + if (!portion.distance.convert(routeRequest.distanceUnit)) { + LOGGER.debug("error during distance conversion: distance " + portion.distance); + throw errorManager.createError(" Error during convertion of portion distance in response. ", 400); + } else { + legArray[legIdx].distance = portion.distance.value; + } + + // Duration + if (!portion.duration.convert(routeRequest.timeUnit)) { + LOGGER.debug("error during duration conversion: duration " + portion.duration); + throw errorManager.createError(" Error during convertion of portion duration in response. ", 400); + } else { + legArray[legIdx].duration = portion.duration.value; + } // Steps (optional) let stepArray = new Array(); @@ -332,17 +370,33 @@ module.exports = { for (let stepIdx = 0; stepIdx < portion.steps.length; stepIdx++) { let simpleStep = portion.steps[stepIdx]; // from road2 standard response let extraStep = leg.steps[stepIdx]; // from engine specific extras + stepArray[stepIdx] = { - "distance": simpleStep.distance, - "duration": simpleStep.duration, "geometry": simpleStep.geometry.getGeometryWithFormat(routeRequest.geometryFormat), "intersections": copyManager.deepCopy(extraStep.intersections), "maneuver": { - "type": extraStep.instruction.type + "type": simpleStep.instruction.type }, "mode": extraStep.mode, "name": simpleStep.name }; + + // Distance + if (!simpleStep.distance.convert(routeRequest.distanceUnit)) { + LOGGER.debug("error during distance conversion: distance " + simpleStep.distance); + throw errorManager.createError(" Error during convertion of portion distance in response. ", 400); + } else { + stepArray[stepIdx].distance = simpleStep.distance.value; + } + + // Duration + if (!simpleStep.duration.convert(routeRequest.timeUnit)) { + LOGGER.debug("error during duration conversion: duration " + simpleStep.duration); + throw errorManager.createError(" Error during convertion of portion duration in response. ", 400); + } else { + stepArray[stepIdx].duration = simpleStep.duration.value; + } + if (simpleStep.instruction.modifier) { stepArray[stepIdx].maneuver.modifier = simpleStep.instruction.modifier; } @@ -356,12 +410,7 @@ module.exports = { } } - routeArray[routeIdx] = { - "distance": simpleRoute.distance, - "duration": simpleRoute.duration, - "geometry": simpleRoute.geometry.getGeometryWithFormat(routeRequest.geometryFormat), - "legs": legArray - }; + routeArray[routeIdx].legs = legArray; } // Finalze userResponse diff --git a/src/js/apis/osrm/1.0.0/index.js b/src/js/apis/osrm/1.0.0/index.js index eda1f6b7..f089c890 100644 --- a/src/js/apis/osrm/1.0.0/index.js +++ b/src/js/apis/osrm/1.0.0/index.js @@ -8,8 +8,8 @@ const controller = require('./controller/controller'); const errorManager = require('../../../utils/errorManager'); const swaggerUi = require('swagger-ui-express'); -var LOGGER = log4js.getLogger("OSRM"); -var router = express.Router(); +let LOGGER = log4js.getLogger("OSRM"); +let router = express.Router(); // API entrypoint router.all("/", function(req, res) { @@ -19,9 +19,9 @@ router.all("/", function(req, res) { // swagger-ui -var apiJsonPath = path.join(__dirname, '..', '..', '..','..','..', 'documentation','apis','osrm', '1.0.0', 'api.json'); +let apiJsonPath = path.join(__dirname, '..', '..', '..','..','..', 'documentation','apis','osrm', '1.0.0', 'api.json'); LOGGER.info("using file '"+ apiJsonPath + "' to initialize swagger-ui for OSRM API version 1.0.0"); -var swaggerDocument = require(apiJsonPath); +let swaggerDocument = require(apiJsonPath); router.use('/openapi', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); // GetCapabilities @@ -43,7 +43,7 @@ router.all("/resources", function(req, res) { // Change base url in GetCapabilties if "Host" or "X-Forwarded-Host" is specified in request's headers // Host is stored by expres in req.hostname if (req.hostname) { - let regexpHost = /^http[s]?:\/\/[\w\d:-_\.]*\//; + let regexpHost = /^https?:\/\/[\w\d:-_.]*\//; try { getCapabilities.info.url = getCapabilities.info.url.replace(regexpHost, req.protocol + "://" + req.hostname + "/"); } catch(error) { diff --git a/src/js/apis/osrm/1.0.0/init.js b/src/js/apis/osrm/1.0.0/init.js index 28f1fc5a..4eb68f2b 100644 --- a/src/js/apis/osrm/1.0.0/init.js +++ b/src/js/apis/osrm/1.0.0/init.js @@ -2,7 +2,7 @@ const log4js = require('log4js'); -var LOGGER = log4js.getLogger("INIT"); +let LOGGER = log4js.getLogger("INIT"); module.exports = { diff --git a/src/js/apis/osrm/1.0.0/update.js b/src/js/apis/osrm/1.0.0/update.js index 32cff8d2..b367a955 100644 --- a/src/js/apis/osrm/1.0.0/update.js +++ b/src/js/apis/osrm/1.0.0/update.js @@ -2,7 +2,7 @@ const log4js = require('log4js'); -var LOGGER = log4js.getLogger("INIT"); +let LOGGER = log4js.getLogger("INIT"); module.exports = { diff --git a/src/js/sources/osrmSource.js b/src/js/sources/osrmSource.js index 9db9ed7b..d9c487ef 100644 --- a/src/js/sources/osrmSource.js +++ b/src/js/sources/osrmSource.js @@ -10,7 +10,7 @@ const Point = require('../geometry/point'); const Step = require('../responses/step'); const Distance = require('../geography/distance'); const Duration = require('../time/duration'); -const copyManager = require('../../../../utils/copyManager') +const copyManager = require('../utils/copyManager') const errorManager = require('../utils/errorManager'); const log4js = require('log4js'); @@ -457,7 +457,7 @@ module.exports = class osrmSource extends Source { let portions = new Array(); let currentOsrmRoute = osrmResponse.routes[i]; engineExtras.routes[i] = {}; - nativeLegs = new Array(); + let nativeLegs = new Array(); // On commence par créer l'itinéraire avec les attributs obligatoires routes[i] = new Route( new Line(currentOsrmRoute.geometry, "geojson", super.projection) ); @@ -552,7 +552,7 @@ module.exports = class osrmSource extends Source { nativeSteps[k] = {}; nativeSteps[k].mode = currentOsrmRouteStep.mode; - nativeIntersections = new Array(); + let nativeIntersections = new Array(); for (let intersectionIndex = 0; intersectionIndex < currentOsrmRouteStep.intersections.length; intersectionIndex++) { let currentIntersection = currentOsrmRouteStep.intersections[intersectionIndex]; nativeIntersections[intersectionIndex] = copyManager.deepCopy(currentIntersection); From 9ff7b3f0f5bf92e1a3bb0b9af62e3b3b287c8af2 Mon Sep 17 00:00:00 2001 From: Xav Dmz Date: Thu, 18 Jan 2024 16:16:16 +0100 Subject: [PATCH 09/12] Fixed OSRM native response's waypoints, and intersections geometry format. --- .../apis/osrm/1.0.0/controller/controller.js | 2 +- src/js/sources/osrmSource.js | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/js/apis/osrm/1.0.0/controller/controller.js b/src/js/apis/osrm/1.0.0/controller/controller.js index 522d195d..56dcfdff 100644 --- a/src/js/apis/osrm/1.0.0/controller/controller.js +++ b/src/js/apis/osrm/1.0.0/controller/controller.js @@ -419,4 +419,4 @@ module.exports = { return userResponse; } -} \ No newline at end of file +} diff --git a/src/js/sources/osrmSource.js b/src/js/sources/osrmSource.js index d9c487ef..12174fc4 100644 --- a/src/js/sources/osrmSource.js +++ b/src/js/sources/osrmSource.js @@ -439,14 +439,15 @@ module.exports = class osrmSource extends Source { LOGGER.debug("osrm response has 1 or more routes"); } - for (let sourceWaypoint in osrmResponse.waypoints) { - let nativeWaypoint = { - "hint": sourceWaypoint.hint, - "distance": sourceWaypoint.distance, - "name": sourceWaypoint.name + for (let waypointIdx = 0; waypointIdx < osrmResponse.waypoints.length; waypointIdx++) { + engineExtras.waypoints[waypointIdx] = { + "hint": osrmResponse.waypoints[waypointIdx].hint, + "distance": osrmResponse.waypoints[waypointIdx].distance, + "name": osrmResponse.waypoints[waypointIdx].name }; - engineExtras.waypoints.push(nativeWaypoint); } + LOGGER.debug("OSRM engineExtras waypoints (before adding to routeResponse:"); + LOGGER.debug(engineExtras.waypoints); // routes // Il peut y avoir plusieurs itinéraires @@ -556,10 +557,11 @@ module.exports = class osrmSource extends Source { for (let intersectionIndex = 0; intersectionIndex < currentOsrmRouteStep.intersections.length; intersectionIndex++) { let currentIntersection = currentOsrmRouteStep.intersections[intersectionIndex]; nativeIntersections[intersectionIndex] = copyManager.deepCopy(currentIntersection); - nativeIntersections[intersectionIndex].location = new Point(currentIntersection.location[0], currentIntersection.location[1], super.projection) - if (!nativeIntersections[intersectionIndex].location.transform(askedProjection)) { + let location = new Point(currentIntersection.location[0], currentIntersection.location[1], super.projection); + if (!location.transform(askedProjection)) { throw errorManager.createError(" Error during reprojection of intersection in OSRM response. "); } + nativeIntersections[intersectionIndex].location = [location.x, location.y]; } nativeSteps[k].intersections = nativeIntersections; } From 63188321dc690257b9f086345dc7a35e01bef83b Mon Sep 17 00:00:00 2001 From: Loic Date: Thu, 15 Feb 2024 18:21:54 +0100 Subject: [PATCH 10/12] fix and tests --- src/js/apis/admin/1.0.0/update.js | 2 +- src/js/apis/osrm/1.0.0/index.js | 31 +- src/js/apis/osrm/1.0.0/init.js | 995 +----------------- src/js/apis/osrm/1.0.0/update.js | 24 +- src/js/apis/simple/1.0.0/update.js | 2 +- src/js/sources/osrmSource.js | 3 +- .../configurations/local-service.json | 5 + .../features/req-osrm-1.0.0-common.feature | 78 ++ .../features/req-osrm-1.0.0-osrm.feature | 21 + 9 files changed, 112 insertions(+), 1049 deletions(-) create mode 100644 test/functional/request/cucumber/features/req-osrm-1.0.0-common.feature create mode 100644 test/functional/request/cucumber/features/req-osrm-1.0.0-osrm.feature diff --git a/src/js/apis/admin/1.0.0/update.js b/src/js/apis/admin/1.0.0/update.js index 78bebfe3..6b6a3397 100644 --- a/src/js/apis/admin/1.0.0/update.js +++ b/src/js/apis/admin/1.0.0/update.js @@ -2,7 +2,7 @@ const log4js = require('log4js'); -var LOGGER = log4js.getLogger("INIT"); +var LOGGER = log4js.getLogger("UPDATE"); module.exports = { diff --git a/src/js/apis/osrm/1.0.0/index.js b/src/js/apis/osrm/1.0.0/index.js index f089c890..1f4af00a 100644 --- a/src/js/apis/osrm/1.0.0/index.js +++ b/src/js/apis/osrm/1.0.0/index.js @@ -25,37 +25,10 @@ let swaggerDocument = require(apiJsonPath); router.use('/openapi', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); // GetCapabilities -router.all("/resources", function(req, res) { +router.all("/resources", function(req, res, next) { LOGGER.debug("request on /osrm/1.0.0/resources?"); - - // get service - let service = req.app.get("service"); - - // get uid - let uid = service.apisManager.getApi("osrm","1.0.0").uid; - LOGGER.debug(uid); - - // get getCapabilities from init.js - let getCapabilities = req.app.get(uid + "-getcap"); - LOGGER.debug(getCapabilities); - - // Change base url in GetCapabilties if "Host" or "X-Forwarded-Host" is specified in request's headers - // Host is stored by expres in req.hostname - if (req.hostname) { - let regexpHost = /^https?:\/\/[\w\d:-_.]*\//; - try { - getCapabilities.info.url = getCapabilities.info.url.replace(regexpHost, req.protocol + "://" + req.hostname + "/"); - } catch(error) { - // apply default service url in GetCapabilities - } - - } else { - // apply default service url in GetCapabilities - } - - res.set('content-type', 'application/json'); - res.status(200).json(getCapabilities); + return next(errorManager.createError("Not Implemented", 501)); }); diff --git a/src/js/apis/osrm/1.0.0/init.js b/src/js/apis/osrm/1.0.0/init.js index 4eb68f2b..7173a1f2 100644 --- a/src/js/apis/osrm/1.0.0/init.js +++ b/src/js/apis/osrm/1.0.0/init.js @@ -6,981 +6,6 @@ let LOGGER = log4js.getLogger("INIT"); module.exports = { - /** - * - * @function - * @name createGetCapabilities - * @description Fonction utilisée pour créer le GetCapabilities - * @param {object} app - App ExpressJS - * @param {string} uid - uid de l'api. Il permet de stocker des objets dans app. - * @return {boolean} True si tout s'est bien passé et False sinon - * - */ - - createGetCapabilities: function(app, uid) { - - // récupération du service - let service = app.get("service"); - - // récupération de la configuration de l'application - let globalConfiguration = service.configuration; - - //création du getCapabilities - let getCapabilities = {}; - - // info - getCapabilities.info = {}; - // info.name - getCapabilities.info.name = globalConfiguration.application.name; - // info.title - getCapabilities.info.title = globalConfiguration.application.title; - // info.description - getCapabilities.info.description = globalConfiguration.application.description; - // info.url - getCapabilities.info.url = globalConfiguration.application.url; - - // provider - getCapabilities.provider = {}; - - if (globalConfiguration.application.provider) { - - // provider.name - getCapabilities.provider.name = globalConfiguration.application.provider.name; - // provider.site - if (globalConfiguration.application.provider.site) { - getCapabilities.provider.site = globalConfiguration.application.provider.site; - } else { - getCapabilities.provider.site = ""; - } - // provider.mail - getCapabilities.provider.mail = globalConfiguration.application.provider.mail; - - } else { - getCapabilities.provider.name = ""; - getCapabilities.provider.site = ""; - getCapabilities.provider.mail = ""; - } - - // api - getCapabilities.api = {}; - // api.name - getCapabilities.api.name = "osrm"; - // api.version - getCapabilities.api.version = "1.0.0"; - - // --- operations - getCapabilities.operations = new Array(); - - // route - - // On vérifie que l'opération route est disponible et on l'intégre seulement si elle est - if (service.verifyAvailabilityOperation("route")) { - - // récupération de l'opération route du service - let serviceOpRoute = service.getOperationById("route"); - - let routeDescription = {}; - // route.id - routeDescription.id = "route"; - // route.description - routeDescription.description = serviceOpRoute.description; - // route.url - routeDescription.url = "/route?"; - // route.methods - routeDescription.methods = new Array(); - routeDescription.methods.push("GET"); - routeDescription.methods.push("POST"); - - // -- route.parameters - routeDescription.parameters = new Array(); - - // TODO: refactorer tout ce code. Une fonction qui prend en argument un paramètre et créer l'objet getCap - // route.parameters.resource - let resourceServiceParameter = serviceOpRoute.getParameterById("resource"); - let resourceParameterDescription = {}; - resourceParameterDescription.name = "resource"; - resourceParameterDescription.in = "query"; - resourceParameterDescription.description = resourceServiceParameter.description; - resourceParameterDescription.required = resourceServiceParameter.required; - resourceParameterDescription.default = resourceServiceParameter.defaultValue; - resourceParameterDescription.schema = {}; - resourceParameterDescription.schema.type = "string"; - resourceParameterDescription.example = "bduni"; - routeDescription.parameters.push(resourceParameterDescription); - - // route.parameters.start - let startServiceParameter = serviceOpRoute.getParameterById("start"); - let startParameterDescription = {}; - startParameterDescription.name = "start"; - startParameterDescription.in = "query"; - startParameterDescription.description = startServiceParameter.description; - startParameterDescription.required = startServiceParameter.required; - startParameterDescription.default = startServiceParameter.defaultValue; - startParameterDescription.schema = {}; - startParameterDescription.schema.type = "string"; - startParameterDescription.example = "2.337306,48.849319"; - routeDescription.parameters.push(startParameterDescription); - - // route.parameters.end - let endServiceParameter = serviceOpRoute.getParameterById("end"); - let endParameterDescription = {}; - endParameterDescription.name = "end"; - endParameterDescription.in = "query"; - endParameterDescription.description = endServiceParameter.description; - endParameterDescription.required = endServiceParameter.required; - endParameterDescription.default = endServiceParameter.defaultValue; - endParameterDescription.schema = {}; - endParameterDescription.schema.type = "string"; - endParameterDescription.example = "2.367776,48.852891"; - routeDescription.parameters.push(endParameterDescription); - - // route.parameters.intermediates - let intermediatesServiceParameter = serviceOpRoute.getParameterById("intermediates"); - let intermediatesParameterDescription = {}; - intermediatesParameterDescription.name = "intermediates"; - intermediatesParameterDescription.in = "query"; - intermediatesParameterDescription.description = intermediatesServiceParameter.description; - intermediatesParameterDescription.required = intermediatesServiceParameter.required; - intermediatesParameterDescription.default = intermediatesServiceParameter.defaultValue; - intermediatesParameterDescription.schema = {}; - intermediatesParameterDescription.schema.type = "array"; - intermediatesParameterDescription.schema.items = {}; - intermediatesParameterDescription.schema.items.type = "string"; - intermediatesParameterDescription.min = intermediatesServiceParameter.min; - intermediatesParameterDescription.max = intermediatesServiceParameter.max; - intermediatesParameterDescription.explode = "false"; - intermediatesParameterDescription.style = "pipeDelimited"; - intermediatesParameterDescription.example = "2.368776,48.852890|2.367976,48.842891"; - routeDescription.parameters.push(intermediatesParameterDescription); - - // route.parameters.profile - let profilesServiceParameter = serviceOpRoute.getParameterById("profile"); - let profileParameterDescription = {}; - profileParameterDescription.name = "profile"; - profileParameterDescription.in = "query"; - profileParameterDescription.description = profilesServiceParameter.description; - profileParameterDescription.required = profilesServiceParameter.required; - profileParameterDescription.default = profilesServiceParameter.defaultValue; - profileParameterDescription.schema = {}; - profileParameterDescription.schema.type = "enumeration"; - profileParameterDescription.example = "car"; - routeDescription.parameters.push(profileParameterDescription); - - // route.parameters.optimization - let optimizationServiceParameter = serviceOpRoute.getParameterById("optimization"); - let optimizationParameterDescription = {}; - optimizationParameterDescription.name = "optimization"; - optimizationParameterDescription.in = "query"; - optimizationParameterDescription.description = optimizationServiceParameter.description; - optimizationParameterDescription.required = optimizationServiceParameter.required; - optimizationParameterDescription.default = optimizationServiceParameter.defaultValue; - optimizationParameterDescription.schema = {}; - optimizationParameterDescription.schema.type = "enumeration"; - optimizationParameterDescription.example = "fastest"; - routeDescription.parameters.push(optimizationParameterDescription); - - // route.parameters.getSteps - let getStepsServiceParameter = serviceOpRoute.getParameterById("getSteps"); - let getStepsParameterDescription = {}; - getStepsParameterDescription.name = "getSteps"; - getStepsParameterDescription.in = "query"; - getStepsParameterDescription.description = getStepsServiceParameter.description; - getStepsParameterDescription.required = getStepsServiceParameter.required; - getStepsParameterDescription.default = getStepsServiceParameter.defaultValue; - getStepsParameterDescription.schema = {}; - getStepsParameterDescription.schema.type = "boolean"; - getStepsParameterDescription.example = "true"; - routeDescription.parameters.push(getStepsParameterDescription); - - // route.parameters.waysAttributes - let waysAttributesServiceParameter = serviceOpRoute.getParameterById("waysAttributes"); - let waysAttributesParameterDescription = {}; - waysAttributesParameterDescription.name = "waysAttributes"; - waysAttributesParameterDescription.in = "query"; - waysAttributesParameterDescription.description = waysAttributesServiceParameter.description; - waysAttributesParameterDescription.required = waysAttributesServiceParameter.required; - waysAttributesParameterDescription.default = waysAttributesServiceParameter.defaultValue; - waysAttributesParameterDescription.schema = {}; - waysAttributesParameterDescription.schema.type = "array"; - waysAttributesParameterDescription.schema.items = {}; - waysAttributesParameterDescription.schema.items.type = "string"; - waysAttributesParameterDescription.min = waysAttributesServiceParameter.min; - waysAttributesParameterDescription.max = waysAttributesServiceParameter.max; - waysAttributesParameterDescription.explode = "false"; - waysAttributesParameterDescription.style = "pipeDelimited"; - waysAttributesParameterDescription.example = "name|type"; - routeDescription.parameters.push(waysAttributesParameterDescription); - - // route.parameters.geometryFormat - let geometryFormatServiceParameter = serviceOpRoute.getParameterById("geometryFormat"); - let geometryFormatParameterDescription = {}; - geometryFormatParameterDescription.name = "geometryFormat"; - geometryFormatParameterDescription.in = "query"; - geometryFormatParameterDescription.description = geometryFormatServiceParameter.description; - geometryFormatParameterDescription.required = geometryFormatServiceParameter.required; - geometryFormatParameterDescription.default = geometryFormatServiceParameter.defaultValue; - geometryFormatParameterDescription.schema = {}; - geometryFormatParameterDescription.schema.type = "enumeration"; - geometryFormatParameterDescription.example = "geojson"; - routeDescription.parameters.push(geometryFormatParameterDescription); - - // route.parameters.getBbox - let getBboxServiceParameter = serviceOpRoute.getParameterById("bbox"); - let getBboxParameterDescription = {}; - getBboxParameterDescription.name = "getBbox"; - getBboxParameterDescription.in = "query"; - getBboxParameterDescription.description = getBboxServiceParameter.description; - getBboxParameterDescription.required = getBboxServiceParameter.required; - getBboxParameterDescription.default = getBboxServiceParameter.defaultValue; - getBboxParameterDescription.schema = {}; - getBboxParameterDescription.schema.type = "boolean"; - getBboxParameterDescription.example = "true"; - routeDescription.parameters.push(getBboxParameterDescription); - - // route.parameters.crs - let projectionServiceParameter = serviceOpRoute.getParameterById("projection"); - let crsParameterDescription = {}; - crsParameterDescription.name = "crs"; - crsParameterDescription.in = "query"; - crsParameterDescription.description = projectionServiceParameter.description; - crsParameterDescription.required = projectionServiceParameter.required; - crsParameterDescription.default = projectionServiceParameter.defaultValue; - crsParameterDescription.schema = {}; - crsParameterDescription.schema.type = "enumeration"; - crsParameterDescription.example = "EPSG:4326"; - routeDescription.parameters.push(crsParameterDescription); - - // route.parameters.timeUnit - let timeUnitServiceParameter = serviceOpRoute.getParameterById("timeUnit"); - let timeUnitParameterDescription = {}; - timeUnitParameterDescription.name = "timeUnit"; - timeUnitParameterDescription.in = "query"; - timeUnitParameterDescription.description = timeUnitServiceParameter.description; - timeUnitParameterDescription.required = timeUnitServiceParameter.required; - timeUnitParameterDescription.default = timeUnitServiceParameter.defaultValue; - timeUnitParameterDescription.schema = {}; - timeUnitParameterDescription.schema.type = "enumeration"; - timeUnitParameterDescription.example = "minute"; - routeDescription.parameters.push(timeUnitParameterDescription); - - // route.parameters.distanceUnit - let distanceUnitServiceParameter = serviceOpRoute.getParameterById("distanceUnit"); - let distanceUnitParameterDescription = {}; - distanceUnitParameterDescription.name = "distanceUnit"; - distanceUnitParameterDescription.in = "query"; - distanceUnitParameterDescription.description = distanceUnitServiceParameter.description; - distanceUnitParameterDescription.required = distanceUnitServiceParameter.required; - distanceUnitParameterDescription.default = distanceUnitServiceParameter.defaultValue; - distanceUnitParameterDescription.schema = {}; - distanceUnitParameterDescription.schema.type = "enumeration"; - distanceUnitParameterDescription.example = "meter"; - routeDescription.parameters.push(distanceUnitParameterDescription); - - // route.parameters.constraints - let constraintsServiceParameter = serviceOpRoute.getParameterById("constraints"); - let constraintsParameterDescription = {}; - constraintsParameterDescription.name = "constraints"; - constraintsParameterDescription.in = "query"; - constraintsParameterDescription.description = constraintsServiceParameter.description; - constraintsParameterDescription.required = constraintsServiceParameter.required; - constraintsParameterDescription.default = constraintsServiceParameter.defaultValue; - constraintsParameterDescription.schema = {}; - constraintsParameterDescription.schema.type = "array"; - constraintsParameterDescription.schema.items = {}; - constraintsParameterDescription.schema.items.type = "object"; - constraintsParameterDescription.schema.items.properties = {}; - constraintsParameterDescription.schema.items.properties.constraintType = {}; - constraintsParameterDescription.schema.items.properties.constraintType.type = "string"; - constraintsParameterDescription.schema.items.properties.key = {}; - constraintsParameterDescription.schema.items.properties.key.type = "string"; - constraintsParameterDescription.schema.items.properties.operator = {}; - constraintsParameterDescription.schema.items.properties.operator.type = "string"; - constraintsParameterDescription.schema.items.properties.value = {}; - constraintsParameterDescription.schema.items.properties.value.type = "string"; - constraintsParameterDescription.min = constraintsServiceParameter.min; - constraintsParameterDescription.max = constraintsServiceParameter.max; - constraintsParameterDescription.explode = "false"; - constraintsParameterDescription.style = "pipeDelimited"; - constraintsParameterDescription.example = "{'constraintType':'banned','key':'ways_type','operator':'=','value':'autoroute'}"; - routeDescription.parameters.push(constraintsParameterDescription); - - // -- end route.parameters - - getCapabilities.operations.push(routeDescription); - - } - // --- end route - - // isochrone - - // On vérifie que l'opération isochrone est disponible et on l'intégre seulement si elle est - if (service.verifyAvailabilityOperation("isochrone")) { - - // récupération de l'opération isochrone du service - let serviceOpIsochrone = service.getOperationById("isochrone"); - - let isochroneDescription = {}; - // isochrone.id - isochroneDescription.id = "isochrone"; - // isochrone.description - isochroneDescription.description = serviceOpIsochrone.description; - // isochrone.url - isochroneDescription.url = "/isochrone?"; - // isochrone.methods - isochroneDescription.methods = new Array(); - isochroneDescription.methods.push("GET"); - isochroneDescription.methods.push("POST"); - - // -- isochrone.parameters - isochroneDescription.parameters = new Array(); - - // isochrone.parameters.resource - let resourceServiceParameter = serviceOpIsochrone.getParameterById("resource"); - let resourceParameterDescription = {}; - resourceParameterDescription.name = "resource"; - resourceParameterDescription.in = "query"; - resourceParameterDescription.description = resourceServiceParameter.description; - resourceParameterDescription.required = resourceServiceParameter.required; - resourceParameterDescription.default = resourceServiceParameter.defaultValue; - resourceParameterDescription.schema = {}; - resourceParameterDescription.schema.type = "string"; - resourceParameterDescription.example = "bduni"; - isochroneDescription.parameters.push(resourceParameterDescription); - - // isochrone.parameters.point - let pointServiceParameter = serviceOpIsochrone.getParameterById("point"); - let pointParameterDescription = {}; - pointParameterDescription.name = "point"; - pointParameterDescription.in = "query"; - pointParameterDescription.description = pointServiceParameter.description; - pointParameterDescription.required = pointServiceParameter.required; - pointParameterDescription.default = pointServiceParameter.defaultValue; - pointParameterDescription.schema = {}; - pointParameterDescription.schema.type = "string"; - pointParameterDescription.example = "2.337306,48.849319"; - isochroneDescription.parameters.push(pointParameterDescription); - - // isochrone.parameters.costType - let costTypeServiceParameter = serviceOpIsochrone.getParameterById("costType"); - let costTypeParameterDescription = {}; - costTypeParameterDescription.name = "costType"; - costTypeParameterDescription.in = "query"; - costTypeParameterDescription.description = costTypeServiceParameter.description; - costTypeParameterDescription.required = costTypeServiceParameter.required; - costTypeParameterDescription.default = costTypeServiceParameter.defaultValue; - costTypeParameterDescription.schema = {}; - costTypeParameterDescription.schema.type = "string"; - costTypeParameterDescription.example = "time"; - isochroneDescription.parameters.push(costTypeParameterDescription); - - // isochrone.parameters.costValue - let costValueServiceParameter = serviceOpIsochrone.getParameterById("costValue"); - let costValueParameterDescription = {}; - costValueParameterDescription.name = "costValue"; - costValueParameterDescription.in = "query"; - costValueParameterDescription.description = costValueServiceParameter.description; - costValueParameterDescription.required = costValueServiceParameter.required; - costValueParameterDescription.default = costValueServiceParameter.defaultValue; - costValueParameterDescription.schema = {}; - costValueParameterDescription.schema.type = "float"; - costValueParameterDescription.example = "100"; - isochroneDescription.parameters.push(costValueParameterDescription); - - // isochrone.parameters.profile - let profileServiceParameter = serviceOpIsochrone.getParameterById("profile"); - let profileParameterDescription = {}; - profileParameterDescription.name = "profile"; - profileParameterDescription.in = "query"; - profileParameterDescription.description = profileServiceParameter.description; - profileParameterDescription.required = profileServiceParameter.required; - profileParameterDescription.default = profileServiceParameter.defaultValue; - profileParameterDescription.schema = {}; - profileParameterDescription.schema.type = "string"; - profileParameterDescription.example = "2.337306,48.849319"; - isochroneDescription.parameters.push(profileParameterDescription); - - // isochrone.parameters.direction - let directionServiceParameter = serviceOpIsochrone.getParameterById("direction"); - let directionParameterDescription = {}; - directionParameterDescription.name = "direction"; - directionParameterDescription.in = "query"; - directionParameterDescription.description = directionServiceParameter.description; - directionParameterDescription.required = directionServiceParameter.required; - directionParameterDescription.default = directionServiceParameter.defaultValue; - directionParameterDescription.schema = {}; - directionParameterDescription.schema.type = "string"; - directionParameterDescription.example = "departure"; - isochroneDescription.parameters.push(directionParameterDescription); - - // isochrone.parameters.crs - let projectionServiceParameter = serviceOpIsochrone.getParameterById("projection"); - let crsParameterDescription = {}; - crsParameterDescription.name = "crs"; - crsParameterDescription.in = "query"; - crsParameterDescription.description = projectionServiceParameter.description; - crsParameterDescription.required = projectionServiceParameter.required; - crsParameterDescription.default = projectionServiceParameter.defaultValue; - crsParameterDescription.schema = {}; - crsParameterDescription.schema.type = "enumeration"; - crsParameterDescription.example = "EPSG:4326"; - isochroneDescription.parameters.push(crsParameterDescription); - - // isochrone.parameters.geometryFormat - let geometryFormatServiceParameter = serviceOpIsochrone.getParameterById("geometryFormat"); - let geometryFormatParameterDescription = {}; - geometryFormatParameterDescription.name = "geometryFormat"; - geometryFormatParameterDescription.in = "query"; - geometryFormatParameterDescription.description = geometryFormatServiceParameter.description; - geometryFormatParameterDescription.required = geometryFormatServiceParameter.required; - geometryFormatParameterDescription.default = geometryFormatServiceParameter.defaultValue; - geometryFormatParameterDescription.schema = {}; - geometryFormatParameterDescription.schema.type = "enumeration"; - geometryFormatParameterDescription.example = "geojson"; - isochroneDescription.parameters.push(geometryFormatParameterDescription); - - // isochrone.parameters.timeUnit - let timeUnitServiceParameter = serviceOpIsochrone.getParameterById("timeUnit"); - let timeUnitParameterDescription = {}; - timeUnitParameterDescription.name = "timeUnit"; - timeUnitParameterDescription.in = "query"; - timeUnitParameterDescription.description = timeUnitServiceParameter.description; - timeUnitParameterDescription.required = timeUnitServiceParameter.required; - timeUnitParameterDescription.default = timeUnitServiceParameter.defaultValue; - timeUnitParameterDescription.schema = {}; - timeUnitParameterDescription.schema.type = "enumeration"; - timeUnitParameterDescription.example = "minute"; - isochroneDescription.parameters.push(timeUnitParameterDescription); - - // isochrone.parameters.distanceUnit - let distanceUnitServiceParameter = serviceOpIsochrone.getParameterById("distanceUnit"); - let distanceUnitParameterDescription = {}; - distanceUnitParameterDescription.name = "distanceUnit"; - distanceUnitParameterDescription.in = "query"; - distanceUnitParameterDescription.description = distanceUnitServiceParameter.description; - distanceUnitParameterDescription.required = distanceUnitServiceParameter.required; - distanceUnitParameterDescription.default = distanceUnitServiceParameter.defaultValue; - distanceUnitParameterDescription.schema = {}; - distanceUnitParameterDescription.schema.type = "enumeration"; - distanceUnitParameterDescription.example = "meter"; - isochroneDescription.parameters.push(distanceUnitParameterDescription); - - // isochrone.parameters.constraints - let constraintsServiceParameter = serviceOpIsochrone.getParameterById("constraints"); - let constraintsParameterDescription = {}; - constraintsParameterDescription.name = "constraints"; - constraintsParameterDescription.in = "query"; - constraintsParameterDescription.description = constraintsServiceParameter.description; - constraintsParameterDescription.required = constraintsServiceParameter.required; - constraintsParameterDescription.default = constraintsServiceParameter.defaultValue; - constraintsParameterDescription.schema = {}; - constraintsParameterDescription.schema.type = "array"; - constraintsParameterDescription.schema.items = {}; - constraintsParameterDescription.schema.items.type = "object"; - constraintsParameterDescription.schema.items.properties = {}; - constraintsParameterDescription.schema.items.properties.constraintType = {}; - constraintsParameterDescription.schema.items.properties.constraintType.type = "string"; - constraintsParameterDescription.schema.items.properties.key = {}; - constraintsParameterDescription.schema.items.properties.key.type = "string"; - constraintsParameterDescription.schema.items.properties.operator = {}; - constraintsParameterDescription.schema.items.properties.operator.type = "string"; - constraintsParameterDescription.schema.items.properties.value = {}; - constraintsParameterDescription.schema.items.properties.value.type = "string"; - constraintsParameterDescription.min = constraintsServiceParameter.min; - constraintsParameterDescription.max = constraintsServiceParameter.max; - constraintsParameterDescription.explode = "false"; - constraintsParameterDescription.style = "pipeDelimited"; - constraintsParameterDescription.example = "{'constraintType':'banned','key':'ways_type','operator':'=','value':'autoroute'}"; - isochroneDescription.parameters.push(constraintsParameterDescription); - - // -- end isochrone.parameters - - getCapabilities.operations.push(isochroneDescription); - - } - // -- end isochrone - - // nearest - - // On vérifie que l'opération nearest est disponible et on l'intégre seulement si elle est - if (service.verifyAvailabilityOperation("nearest")) { - - // récupération de l'opération nearest du service - let serviceOpNearest = service.getOperationById("nearest"); - - let nearestDescription = {}; - // nearest.id - nearestDescription.id = "nearest"; - // nearest.description - nearestDescription.description = serviceOpNearest.description; - // nearest.url - nearestDescription.url = "/nearest?"; - // nearest.methods - nearestDescription.methods = new Array(); - nearestDescription.methods.push("GET"); - nearestDescription.methods.push("POST"); - - // -- nearest.parameters - nearestDescription.parameters = new Array(); - - // TODO: refactorer tout ce code. Une fonction qui prend en argument un paramètre et créer l'objet getCap - // nearest.parameters.resource - let resourceServiceParameter = serviceOpNearest.getParameterById("resource"); - let resourceParameterDescription = {}; - resourceParameterDescription.name = "resource"; - resourceParameterDescription.in = "query"; - resourceParameterDescription.description = resourceServiceParameter.description; - resourceParameterDescription.required = resourceServiceParameter.required; - resourceParameterDescription.default = resourceServiceParameter.defaultValue; - resourceParameterDescription.schema = {}; - resourceParameterDescription.schema.type = "string"; - resourceParameterDescription.example = "bduni"; - nearestDescription.parameters.push(resourceParameterDescription); - - // nearest.parameters.coordinates - let coordinatesServiceParameter = serviceOpNearest.getParameterById("coordinates"); - let coordinatesParameterDescription = {}; - coordinatesParameterDescription.name = "coordinates"; - coordinatesParameterDescription.in = "query"; - coordinatesParameterDescription.description = coordinatesServiceParameter.description; - coordinatesParameterDescription.required = coordinatesServiceParameter.required; - coordinatesParameterDescription.default = coordinatesServiceParameter.defaultValue; - coordinatesParameterDescription.schema = {}; - coordinatesParameterDescription.schema.type = "string"; - coordinatesParameterDescription.example = "2.337306,48.849319"; - nearestDescription.parameters.push(coordinatesParameterDescription); - - // nearest.parameters.number - let numbersServiceParameter = serviceOpNearest.getParameterById("number"); - let numberParameterDescription = {}; - numberParameterDescription.name = "nbPoints"; - numberParameterDescription.in = "query"; - numberParameterDescription.description = numbersServiceParameter.description; - numberParameterDescription.required = numbersServiceParameter.required; - numberParameterDescription.default = numbersServiceParameter.defaultValue; - numberParameterDescription.schema = {}; - numberParameterDescription.schema.type = "integer"; - numberParameterDescription.example = 1; - nearestDescription.parameters.push(numberParameterDescription); - - // nearest.parameters.crs - let projectionServiceParameter = serviceOpNearest.getParameterById("projection"); - let crsParameterDescription = {}; - crsParameterDescription.name = "crs"; - crsParameterDescription.in = "query"; - crsParameterDescription.description = projectionServiceParameter.description; - crsParameterDescription.required = projectionServiceParameter.required; - crsParameterDescription.default = projectionServiceParameter.defaultValue; - crsParameterDescription.schema = {}; - crsParameterDescription.schema.type = "enumeration"; - crsParameterDescription.example = "EPSG:4326"; - nearestDescription.parameters.push(crsParameterDescription); - - - - // -- end nearest.parameters - - getCapabilities.operations.push(nearestDescription); - - } - // --- end nearest - - // --- end operations - - // --- resources - getCapabilities.resources = new Array(); - let resources = service.getResources(); - - for(let resourceId in resources) { - - let resourceDescription = {}; - let localResource = resources[resourceId]; - - // resource.id - resourceDescription.id = localResource.id; - - // resource.description - resourceDescription.description = localResource.configuration.description; - - // -- resource.availableOperations - resourceDescription.availableOperations = new Array(); - - // - route - - // On vérifie que l'opération route est disponible et on l'intégre seulement si elle est - if (service.verifyAvailabilityOperation("route")) { - - // on vérifie qu'elle est disponible sur la ressource - if (localResource.verifyAvailabilityOperation("route")) { - - - // on récupère l'opération de ressource - let resourceOperation = localResource.getOperationById("route"); - - let routeAvailableOperation = {}; - routeAvailableOperation.id = "route"; - routeAvailableOperation.availableParameters = new Array(); - - // route.resource - let resourceParameter = resourceOperation.getParameterById("resource"); - let routeResource = {}; - routeResource.id = "resource"; - routeResource.values = resourceParameter.values; - if (resourceParameter.serviceParameter.defaultValue === "true") { - routeResource.defaultValue = resourceParameter.defaultValueContent; - } - routeAvailableOperation.availableParameters.push(routeResource); - - // route.start - let startParameter = resourceOperation.getParameterById("start"); - let routeStart = {}; - routeStart.id = "start"; - routeStart.values = startParameter.values; - if (startParameter.serviceParameter.defaultValue === "true") { - routeStart.defaultValue = startParameter.defaultValueContent; - } - routeAvailableOperation.availableParameters.push(routeStart); - - // route.end - let endParameter = resourceOperation.getParameterById("end"); - let routeEnd = {}; - routeEnd.id = "end"; - routeEnd.values = endParameter.values; - if (endParameter.serviceParameter.defaultValue === "true") { - routeEnd.defaultValue = endParameter.defaultValueContent; - } - routeAvailableOperation.availableParameters.push(routeEnd); - - // route.intermediates - let intermediatesParameter = resourceOperation.getParameterById("intermediates"); - let routeIntermediates = {}; - routeIntermediates.id = "intermediates"; - routeIntermediates.values = intermediatesParameter.values; - if (intermediatesParameter.serviceParameter.defaultValue === "true") { - routeIntermediates.defaultValue = intermediatesParameter.defaultValueContent; - } - routeAvailableOperation.availableParameters.push(routeIntermediates); - - // route.profile - let profileParameter = resourceOperation.getParameterById("profile"); - let routeProfile = {}; - routeProfile.id = "profile"; - routeProfile.values = profileParameter.values; - if (profileParameter.serviceParameter.defaultValue === "true") { - routeProfile.defaultValue = profileParameter.defaultValueContent; - } - routeAvailableOperation.availableParameters.push(routeProfile); - - // route.optimization - let optimizationParameter = resourceOperation.getParameterById("optimization"); - let routeOptimization = {}; - routeOptimization.id = "optimization"; - routeOptimization.values = optimizationParameter.values; - if (optimizationParameter.serviceParameter.defaultValue === "true") { - routeOptimization.defaultValue = optimizationParameter.defaultValueContent; - } - routeAvailableOperation.availableParameters.push(routeOptimization); - - // route.getSteps - let getStepsParameter = resourceOperation.getParameterById("getSteps"); - let routeGetSteps = {}; - routeGetSteps.id = "getSteps"; - routeGetSteps.values = getStepsParameter.values; - if (getStepsParameter.serviceParameter.defaultValue === "true") { - routeGetSteps.defaultValue = getStepsParameter.defaultValueContent; - } - routeAvailableOperation.availableParameters.push(routeGetSteps); - - // route.waysAttributes - let waysAttributesParameter = resourceOperation.getParameterById("waysAttributes"); - let routeWaysAttributes = {}; - routeWaysAttributes.id = "waysAttributes"; - routeWaysAttributes.values = waysAttributesParameter.values; - if (waysAttributesParameter.serviceParameter.defaultValue === "true") { - routeWaysAttributes.defaultValue = waysAttributesParameter.defaultValueContent; - } - routeAvailableOperation.availableParameters.push(routeWaysAttributes); - - // route.geometryFormat - let geometryFormatParameter = resourceOperation.getParameterById("geometryFormat"); - let routeGeometriesFormat = {}; - routeGeometriesFormat.id = "geometryFormat"; - routeGeometriesFormat.values = geometryFormatParameter.values; - if (geometryFormatParameter.serviceParameter.defaultValue === "true") { - routeGeometriesFormat.defaultValue = geometryFormatParameter.defaultValueContent; - } - routeAvailableOperation.availableParameters.push(routeGeometriesFormat); - - // route.getBbox - let bboxParameter = resourceOperation.getParameterById("bbox"); - let routeGetBbox = {}; - routeGetBbox.id = "getBbox"; - routeGetBbox.values = bboxParameter.values; - if (bboxParameter.serviceParameter.defaultValue === "true") { - routeGetBbox.defaultValue = bboxParameter.defaultValueContent; - } - routeAvailableOperation.availableParameters.push(routeGetBbox); - - // route.crs - let projectionParameter = resourceOperation.getParameterById("projection"); - let routeCrs = {}; - routeCrs.id = "crs"; - routeCrs.values = projectionParameter.values; - if (projectionParameter.serviceParameter.defaultValue === "true") { - routeCrs.defaultValue = projectionParameter.defaultValueContent; - } - routeAvailableOperation.availableParameters.push(routeCrs); - - // route.timeUnit - let timeUnitParameter = resourceOperation.getParameterById("timeUnit"); - let routeTimeUnit = {}; - routeTimeUnit.id = "timeUnit"; - routeTimeUnit.values = timeUnitParameter.values; - if (timeUnitParameter.serviceParameter.defaultValue === "true") { - routeTimeUnit.defaultValue = timeUnitParameter.defaultValueContent; - } - routeAvailableOperation.availableParameters.push(routeTimeUnit); - - // route.distanceUnit - let distanceUnitParameter = resourceOperation.getParameterById("distanceUnit"); - let routeDistanceUnit = {}; - routeDistanceUnit.id = "distanceUnit"; - routeDistanceUnit.values = distanceUnitParameter.values; - if (distanceUnitParameter.serviceParameter.defaultValue === "true") { - routeDistanceUnit.defaultValue = distanceUnitParameter.defaultValueContent; - } - routeAvailableOperation.availableParameters.push(routeDistanceUnit); - - // route.constraints - let constraintsParameter = resourceOperation.getParameterById("constraints"); - let routeConstraints = {}; - routeConstraints.id = "constraints"; - routeConstraints.values = constraintsParameter.getcapabilities; - if (constraintsParameter.serviceParameter.defaultValue === "true") { - routeConstraints.defaultValue = constraintsParameter.defaultValueContent; - } - routeAvailableOperation.availableParameters.push(routeConstraints); - - resourceDescription.availableOperations.push(routeAvailableOperation); - - } - - } - // - end route - - // - isochrone - - // On vérifie que l'opération isochrone est disponible et on l'intégre seulement si elle est - if (service.verifyAvailabilityOperation("isochrone")) { - - // on vérifie qu'elle est disponible sur la ressource - if (localResource.verifyAvailabilityOperation("isochrone")) { - - - // on récupère l'opération de ressource - let resourceOperation = localResource.getOperationById("isochrone"); - - let isochroneAvailableOperation = {}; - isochroneAvailableOperation.id = "isochrone"; - isochroneAvailableOperation.availableParameters = new Array(); - - // isochrone.resource - let resourceParameter = resourceOperation.getParameterById("resource"); - let isochroneResource = {}; - isochroneResource.id = "resource"; - isochroneResource.values = resourceParameter.values; - if (resourceParameter.serviceParameter.defaultValue === "true") { - isochroneResource.defaultValue = resourceParameter.defaultValueContent; - } - isochroneAvailableOperation.availableParameters.push(isochroneResource); - - // isochrone.point - let pointParameter = resourceOperation.getParameterById("point"); - let isochronePoint = {}; - isochronePoint.id = "point"; - isochronePoint.values = pointParameter.values; - if (pointParameter.serviceParameter.defaultValue === "true") { - isochronePoint.defaultValue = pointParameter.defaultValueContent; - } - isochroneAvailableOperation.availableParameters.push(isochronePoint); - - // isochrone.costType - let costTypeParameter = resourceOperation.getParameterById("costType"); - let isochroneCostType = {}; - isochroneCostType.id = "costType"; - isochroneCostType.values = costTypeParameter.values; - if (costTypeParameter.serviceParameter.defaultValue === "true") { - isochroneCostType.defaultValue = costTypeParameter.defaultValueContent; - } - isochroneAvailableOperation.availableParameters.push(isochroneCostType); - - // isochrone.costValue - let costValueParameter = resourceOperation.getParameterById("costValue"); - let isochroneCostValue = {}; - isochroneCostValue.id = "costValue"; - isochroneCostValue.values = costValueParameter.values; - if (costValueParameter.serviceParameter.defaultValue === "true") { - isochroneCostValue.defaultValue = costValueParameter.defaultValueContent; - } - isochroneAvailableOperation.availableParameters.push(isochroneCostValue); - - // isochrone.profile - let profileParameter = resourceOperation.getParameterById("profile"); - let isochroneProfile = {}; - isochroneProfile.id = "profile"; - isochroneProfile.values = profileParameter.values; - if (profileParameter.serviceParameter.defaultValue === "true") { - isochroneProfile.defaultValue = profileParameter.defaultValueContent; - } - isochroneAvailableOperation.availableParameters.push(isochroneProfile); - - // isochrone.direction - let directionParameter = resourceOperation.getParameterById("direction"); - let isochroneDirection = {}; - isochroneDirection.id = "direction"; - isochroneDirection.values = directionParameter.values; - if (directionParameter.serviceParameter.defaultValue === "true") { - isochroneDirection.defaultValue = directionParameter.defaultValueContent; - } - isochroneAvailableOperation.availableParameters.push(isochroneDirection); - - // isochrone.crs - let projectionParameter = resourceOperation.getParameterById("projection"); - let isochroneProjection = {}; - isochroneProjection.id = "projection"; - isochroneProjection.values = projectionParameter.values; - if (projectionParameter.serviceParameter.defaultValue === "true") { - isochroneProjection.defaultValue = projectionParameter.defaultValueContent; - } - isochroneAvailableOperation.availableParameters.push(isochroneProjection); - - - // isochrone.geometryFormat - let geometryFormatParameter = resourceOperation.getParameterById("geometryFormat"); - let isochroneGeometriesFormat = {}; - isochroneGeometriesFormat.id = "geometryFormat"; - isochroneGeometriesFormat.values = geometryFormatParameter.values; - if (geometryFormatParameter.serviceParameter.defaultValue === "true") { - isochroneGeometriesFormat.defaultValue = geometryFormatParameter.defaultValueContent; - } - isochroneAvailableOperation.availableParameters.push(isochroneGeometriesFormat); - - // isochrone.timeUnit - let timeUnitParameter = resourceOperation.getParameterById("timeUnit"); - let isochroneTimeUnit = {}; - isochroneTimeUnit.id = "timeUnit"; - isochroneTimeUnit.values = timeUnitParameter.values; - if (timeUnitParameter.serviceParameter.defaultValue === "true") { - isochroneTimeUnit.defaultValue = timeUnitParameter.defaultValueContent; - } - isochroneAvailableOperation.availableParameters.push(isochroneTimeUnit); - - // isochrone.distanceUnit - let distanceUnitParameter = resourceOperation.getParameterById("distanceUnit"); - let isochroneDistanceUnit = {}; - isochroneDistanceUnit.id = "distanceUnit"; - isochroneDistanceUnit.values = distanceUnitParameter.values; - if (distanceUnitParameter.serviceParameter.defaultValue === "true") { - isochroneDistanceUnit.defaultValue = distanceUnitParameter.defaultValueContent; - } - isochroneAvailableOperation.availableParameters.push(isochroneDistanceUnit); - - // isochrone.constraints - let constraintsParameter = resourceOperation.getParameterById("constraints"); - let isochroneConstraints = {}; - isochroneConstraints.id = "constraints"; - isochroneConstraints.values = constraintsParameter.getcapabilities; - if (constraintsParameter.serviceParameter.defaultValue === "true") { - isochroneConstraints.defaultValue = constraintsParameter.defaultValueContent; - } - isochroneAvailableOperation.availableParameters.push(isochroneConstraints); - - resourceDescription.availableOperations.push(isochroneAvailableOperation); - - } - - } - // - end isochrone - - // - nearest - - // On vérifie que l'opération nearest est disponible et on l'intégre seulement si elle est - if (service.verifyAvailabilityOperation("nearest")) { - - // on vérifie qu'elle est disponible sur la ressource - if (localResource.verifyAvailabilityOperation("nearest")) { - - - // on récupère l'opération de ressource - let resourceOperation = localResource.getOperationById("nearest"); - - let nearestAvailableOperation = {}; - nearestAvailableOperation.id = "nearest"; - nearestAvailableOperation.availableParameters = new Array(); - - // nearest.resource - let resourceParameter = resourceOperation.getParameterById("resource"); - let nearestResource = {}; - nearestResource.id = "resource"; - nearestResource.values = resourceParameter.values; - if (resourceParameter.serviceParameter.defaultValue === "true") { - nearestResource.defaultValue = resourceParameter.defaultValueContent; - } - nearestAvailableOperation.availableParameters.push(nearestResource); - - // nearest.coordinates - let coordinatesParameter = resourceOperation.getParameterById("coordinates"); - let nearestCoordinates = {}; - nearestCoordinates.id = "coordinates"; - nearestCoordinates.values = coordinatesParameter.values; - if (coordinatesParameter.serviceParameter.defaultValue === "true") { - nearestCoordinates.defaultValue = coordinatesParameter.defaultValueContent; - } - nearestAvailableOperation.availableParameters.push(nearestCoordinates); - - // nearest.number - let numberParameter = resourceOperation.getParameterById("number"); - let nearestNumber = {}; - nearestNumber.id = "nbPoints"; - nearestNumber.values = numberParameter.values; - if (numberParameter.serviceParameter.defaultValue === "true") { - nearestNumber.defaultValue = numberParameter.defaultValueContent; - } - nearestAvailableOperation.availableParameters.push(nearestNumber); - - // nearest.crs - let projectionParameter = resourceOperation.getParameterById("projection"); - let nearestCrs = {}; - nearestCrs.id = "crs"; - nearestCrs.values = projectionParameter.values; - if (projectionParameter.serviceParameter.defaultValue === "true") { - nearestCrs.defaultValue = projectionParameter.defaultValueContent; - } - nearestAvailableOperation.availableParameters.push(nearestCrs); - - resourceDescription.availableOperations.push(nearestAvailableOperation); - - } - - } - // - end nearest - - // -- end resource.availableOperations - - - - getCapabilities.resources.push(resourceDescription); - - } // end for(let resourceId in resources) - - // --- end resources - - // sauvegarde du getCapabilities - app.set(uid + "-getcap", getCapabilities); - - return true; - - }, - /** * * @function @@ -993,25 +18,7 @@ module.exports = { */ run: function(app, uid) { - try { - - // TODO: vérification que l'ensemble des opérations et paramètres soient disponibles - // ils sont utilisés dans l'api mais leur existence n'est pas vérifiée - - // Création du GetCapabilities - if (!this.createGetCapabilities(app, uid)) { - LOGGER.error("Erreur lors de la creation du GetCapabilities."); - return false; - } else { - // tout s'est bien passé - } - return true; - - } catch (err) { - LOGGER.error("Erreur lors de la creation du GetCapabilities.", err); - return false; - } - + return true; } } diff --git a/src/js/apis/osrm/1.0.0/update.js b/src/js/apis/osrm/1.0.0/update.js index b367a955..8a977fea 100644 --- a/src/js/apis/osrm/1.0.0/update.js +++ b/src/js/apis/osrm/1.0.0/update.js @@ -2,26 +2,10 @@ const log4js = require('log4js'); -let LOGGER = log4js.getLogger("INIT"); +let LOGGER = log4js.getLogger("UPDATE"); module.exports = { - /** - * - * @function - * @name updateGetCapabilities - * @description Fonction utilisée pour mettre à jour le GetCapabilities - * @param {object} app - App ExpressJS - * @return {boolean} True si tout s'est bien passé et False sinon - * - */ - - updateGetCapabilities: function(app) { - - return true; - - }, - /** * * @function @@ -35,12 +19,6 @@ module.exports = { run: function(app, uid) { - // Création du GetCapabilities - if (!this.updateGetCapabilities(app)) { - LOGGER.error("Erreur lors de la creation du GetCapabilities."); - return false; - } - return true; } diff --git a/src/js/apis/simple/1.0.0/update.js b/src/js/apis/simple/1.0.0/update.js index 32cff8d2..eb9866f3 100644 --- a/src/js/apis/simple/1.0.0/update.js +++ b/src/js/apis/simple/1.0.0/update.js @@ -2,7 +2,7 @@ const log4js = require('log4js'); -var LOGGER = log4js.getLogger("INIT"); +var LOGGER = log4js.getLogger("UPDATE"); module.exports = { diff --git a/src/js/sources/osrmSource.js b/src/js/sources/osrmSource.js index 12174fc4..0e851afa 100644 --- a/src/js/sources/osrmSource.js +++ b/src/js/sources/osrmSource.js @@ -392,7 +392,8 @@ module.exports = class osrmSource extends Source { // --- // Récupération des paramètres de la réponse OSRM pour une éventuelle réponse avec son API native - engineExtras.code = osrmResponse.code; + // Si on est ici, pour le moment, c'est que c'est Ok, sinon une erreur aura déjà été renvoyée + engineExtras.code = "Ok"; // --- diff --git a/test/functional/request/cucumber/configurations/local-service.json b/test/functional/request/cucumber/configurations/local-service.json index f40e7aeb..ccbaa75b 100644 --- a/test/functional/request/cucumber/configurations/local-service.json +++ b/test/functional/request/cucumber/configurations/local-service.json @@ -10,6 +10,11 @@ "nearest": "/simple/1.0.0/nearest", "getcapabilities": "/simple/1.0.0/getcapabilities" } + }, + "osrm": { + "1.0.0": { + "///route/v1/_/": "/osrm/1.0.0////route/v1/_/" + } } }, "defaultParameters": [ diff --git a/test/functional/request/cucumber/features/req-osrm-1.0.0-common.feature b/test/functional/request/cucumber/features/req-osrm-1.0.0-common.feature new file mode 100644 index 00000000..85995dca --- /dev/null +++ b/test/functional/request/cucumber/features/req-osrm-1.0.0-common.feature @@ -0,0 +1,78 @@ +Feature: Road2-osrm-1.0.0-common + Tests fonctionnels de Road2 via l'API osrm/1.0.0 + + Background: + Given I have loaded all my test configuration in "../../configurations/local-service.json" + + Scenario: Route principale de l'API osrm/1.0.0 + Given an "GET" request on "/osrm/1.0.0" + When I send the request + Then the server should send a response with status 200 + And the response should contain "Road2 via OSRM API 1.0.0" + + Scenario: GetCapabilities de l'API osrm/1.0.0 + Given an "GET" request on "/osrm/1.0.0/resources" + When I send the request + Then the server should send a response with status 501 + And the response should contain "Not Implemented" + + Scenario: Requête sur une route inexistante + Given an "GET" request on "/osrm/1.0.0/test" + When I send the request + Then the server should send a response with status 404 + And the response should contain "Not found" + + Scenario: [route] Route sur l'API osrm 1.0.0 avec mauvaise resource + Given an "GET" request on operation "///route/v1/_/" in api "osrm" "1.0.0" + And with path parameters: + | key | value | + | resource | test | + | profile | car | + | optimization | fastest | + | coordinates | 2.333865,48.881989;2.344851,48.872393 | + When I send the request + Then the server should send a response with status 400 + And the response should have an header "content-type" with value "application/json" + And the response should contain "Parameter 'resourceId' is invalid: it does not exist on this service" + + Scenario: [route] Route sur l'API osrm 1.0.0 avec mauvais profile + Given an "GET" request on operation "///route/v1/_/" in api "osrm" "1.0.0" + And with path parameters: + | key | value | + | resource | bduni-idf-osrm | + | profile | test | + | optimization | fastest | + | coordinates | 2.333865,48.881989;2.344851,48.872393 | + When I send the request + Then the server should send a response with status 400 + And the response should have an header "content-type" with value "application/json" + And the response should contain "Parameter 'profileId' is invalid" + + Scenario: [route] Route sur l'API osrm 1.0.0 avec mauvaise optimisation + Given an "GET" request on operation "///route/v1/_/" in api "osrm" "1.0.0" + And with path parameters: + | key | value | + | resource | bduni-idf-osrm | + | profile | car | + | optimization | test | + | coordinates | 2.333865,48.881989;2.344851,48.872393 | + When I send the request + Then the server should send a response with status 400 + And the response should have an header "content-type" with value "application/json" + And the response should contain "Parameter 'optimizationId' is invalid" + + Scenario: [route] Route sur l'API osrm 1.0.0 avec mauvaises coordonnées + Given an "GET" request on operation "///route/v1/_/" in api "osrm" "1.0.0" + And with path parameters: + | key | value | + | resource | bduni-idf-osrm | + | profile | car | + | optimization | fastest | + | coordinates | 2.333865,48.881989;2.344851, | + When I send the request + Then the server should send a response with status 400 + And the response should have an header "content-type" with value "application/json" + And the response should contain "Parameter 'coordinates' is invalid" + + + diff --git a/test/functional/request/cucumber/features/req-osrm-1.0.0-osrm.feature b/test/functional/request/cucumber/features/req-osrm-1.0.0-osrm.feature new file mode 100644 index 00000000..e68248e0 --- /dev/null +++ b/test/functional/request/cucumber/features/req-osrm-1.0.0-osrm.feature @@ -0,0 +1,21 @@ +Feature: Road2-osrm-1.0.0-osrm + Tests fonctionnels de Road2 via l'API osrm/1.0.0 avec le moteur OSRM + + Background: + Given I have loaded all my test configuration in "../../configurations/local-service.json" + + + Scenario: Route sur l'API osrm 1.0.0 + Given an "GET" request on operation "///route/v1/_/" in api "osrm" "1.0.0" + And with path parameters: + | key | value | + | resource | bduni-idf-osrm | + | profile | car | + | optimization | fastest | + | coordinates | 2.333865,48.881989;2.344851,48.872393 | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain an attribute "code" with value "Ok" + And the response should contain an attribute "waypoints" + And the response should contain an attribute "routes" From 663de048e8d43e330fe9d058f8a165bdc016b47e Mon Sep 17 00:00:00 2001 From: Loic Date: Fri, 23 Feb 2024 17:01:05 +0100 Subject: [PATCH 11/12] some littles changes and tests --- .../apis/osrm/1.0.0/controller/controller.js | 49 +++++- src/js/apis/osrm/1.0.0/index.js | 10 +- src/js/sources/osrmSource.js | 24 +++ .../features/req-osrm-1.0.0-common.feature | 8 +- .../features/req-osrm-1.0.0-osrm.feature | 153 +++++++++++++++++- 5 files changed, 227 insertions(+), 17 deletions(-) diff --git a/src/js/apis/osrm/1.0.0/controller/controller.js b/src/js/apis/osrm/1.0.0/controller/controller.js index 56dcfdff..b1dc53c8 100644 --- a/src/js/apis/osrm/1.0.0/controller/controller.js +++ b/src/js/apis/osrm/1.0.0/controller/controller.js @@ -226,13 +226,15 @@ module.exports = { LOGGER.debug("converted getSteps: " + routeRequest.computeSteps); } } - } else if ("defaultValueContent" in routeOperation.getParameterById("getSteps") && typeof(routeOperation.getParameterById("getSteps").defaultValueContent) !== "undefined") { - // Default value from configuration - routeRequest.computeSteps = routeOperation.getParameterById("getSteps").defaultValueContent; - LOGGER.debug("configuration default getSteps: " + routeRequest.computeSteps); } else { + + // Default value could be taken from configuration + // routeRequest.computeSteps = routeOperation.getParameterById("getSteps").defaultValueContent; + // but we take the default of OSRM HTTP API : false + // (see http://project-osrm.org/docs/v5.5.1/api/#route-service) routeRequest.computeSteps = false; LOGGER.debug("general default getSteps: " + routeRequest.computeSteps); + } // geometries (OSRM) / geometryFormat (Road2) @@ -253,8 +255,11 @@ module.exports = { } else { - // Default value from configuration - routeRequest.geometryFormat = routeOperation.getParameterById("geometryFormat").defaultValueContent; + // Default value could be taken from configuration + // routeRequest.geometryFormat = routeOperation.getParameterById("geometryFormat").defaultValueContent; + // but we take the default of OSRM HTTP API : polyline + // (see http://project-osrm.org/docs/v5.5.1/api/#route-service) + routeRequest.geometryFormat = "polyline"; LOGGER.debug("default geometryFormat used: " + routeRequest.geometryFormat); } @@ -367,10 +372,19 @@ module.exports = { // Steps (optional) let stepArray = new Array(); if (routeRequest.computeSteps && portion.steps.length !== 0) { + for (let stepIdx = 0; stepIdx < portion.steps.length; stepIdx++) { + let simpleStep = portion.steps[stepIdx]; // from road2 standard response let extraStep = leg.steps[stepIdx]; // from engine specific extras + let tmpName = ""; + try { + tmpName = JSON.stringify(simpleStep.attributes.name); + } catch(error) { + LOGGER.debug(error); + } + stepArray[stepIdx] = { "geometry": simpleStep.geometry.getGeometryWithFormat(routeRequest.geometryFormat), "intersections": copyManager.deepCopy(extraStep.intersections), @@ -378,7 +392,7 @@ module.exports = { "type": simpleStep.instruction.type }, "mode": extraStep.mode, - "name": simpleStep.name + "name": tmpName }; // Distance @@ -403,10 +417,31 @@ module.exports = { if (simpleStep.instruction.exit) { stepArray[stepIdx].maneuver.exit = simpleStep.instruction.exit; } + if (extraStep.maneuver) { + // Test with hasOwnProperty because it can be 0 inside this property + if (extraStep.maneuver.hasOwnProperty("bearing_before")) { + stepArray[stepIdx].maneuver.bearing_before = extraStep.maneuver.bearing_before; + } + if (extraStep.maneuver.hasOwnProperty("bearing_after")) { + stepArray[stepIdx].maneuver.bearing_after = extraStep.maneuver.bearing_after; + } + if (extraStep.maneuver.hasOwnProperty("location")) { + stepArray[stepIdx].maneuver.location = [extraStep.maneuver.location[0],extraStep.maneuver.location[1]]; + } + } + } + legArray[legIdx].steps = stepArray; + legArray[legIdx].summary = leg.summary; + } else { + + // We add an empty array (see http://project-osrm.org/docs/v5.5.1/api/#route-object) + legArray[legIdx].steps = new Array(); + legArray[legIdx].summary = ""; LOGGER.debug("no steps asked by user"); + } } diff --git a/src/js/apis/osrm/1.0.0/index.js b/src/js/apis/osrm/1.0.0/index.js index 1f4af00a..61470844 100644 --- a/src/js/apis/osrm/1.0.0/index.js +++ b/src/js/apis/osrm/1.0.0/index.js @@ -134,24 +134,24 @@ function sendError(err, req, res, next) { if (err.status) { // if error has a status, this error should be sent to the client res.status(err.status); - res.json({ error: {errorType: err.code, message: err.message}}); + res.json({code: err.code, message: err.message}); } else { // if error has no status, this error's details should not be sent to the client res.status(500); - res.json({ error: {errorType: "internal", message: "Internal Server Error"}}); + res.json({code: "internal", message: "Internal Server Error"}); } } else if ((process.env.NODE_ENV === "debug")) { res.status(err.status || 500); - res.json({ error: {errorType: err.code, + res.json({code: err.code, message: err.message, stack: err.stack, // useful for SQL errors more: err - }}); + }); } else { // in the development environment, every error should be sent to clients res.status(err.status || 500); - res.json({ error: {errorType: err.code, message: err.message}}); + res.json({code: err.code, message: err.message}); } } diff --git a/src/js/sources/osrmSource.js b/src/js/sources/osrmSource.js index 0e851afa..b5a507c1 100644 --- a/src/js/sources/osrmSource.js +++ b/src/js/sources/osrmSource.js @@ -552,8 +552,11 @@ module.exports = class osrmSource extends Source { steps[k].instruction.exit = currentOsrmRouteStep.maneuver.exit; } + // Add OSRM extra in the routeResponse nativeSteps[k] = {}; nativeSteps[k].mode = currentOsrmRouteStep.mode; + + // Add intersections extra let nativeIntersections = new Array(); for (let intersectionIndex = 0; intersectionIndex < currentOsrmRouteStep.intersections.length; intersectionIndex++) { let currentIntersection = currentOsrmRouteStep.intersections[intersectionIndex]; @@ -565,10 +568,31 @@ module.exports = class osrmSource extends Source { nativeIntersections[intersectionIndex].location = [location.x, location.y]; } nativeSteps[k].intersections = nativeIntersections; + + // Add maneuver extra + let nativeManeuver = {}; + // Test with hasOwnProperty because it can be 0 inside this property + if (currentOsrmRouteStep.maneuver.hasOwnProperty("bearing_before")) { + nativeManeuver.bearing_before = currentOsrmRouteStep.maneuver.bearing_before; + } + if (currentOsrmRouteStep.maneuver.hasOwnProperty("bearing_after")) { + nativeManeuver.bearing_after = currentOsrmRouteStep.maneuver.bearing_after; + } + if (currentOsrmRouteStep.maneuver.hasOwnProperty("location")) { + let location = new Point(currentOsrmRouteStep.maneuver.location[0], currentOsrmRouteStep.maneuver.location[1], super.projection); + if (!location.transform(askedProjection)) { + throw errorManager.createError(" Error during reprojection of step location in OSRM response. "); + } + nativeManeuver.location = [location.x, location.y]; + } + + nativeSteps[k].maneuver = nativeManeuver; + } portions[j].steps = steps; nativeLegs[j].steps = nativeSteps; + nativeLegs[j].summary = currentOsrmRouteLeg.summary; } diff --git a/test/functional/request/cucumber/features/req-osrm-1.0.0-common.feature b/test/functional/request/cucumber/features/req-osrm-1.0.0-common.feature index 85995dca..5a6e9da4 100644 --- a/test/functional/request/cucumber/features/req-osrm-1.0.0-common.feature +++ b/test/functional/request/cucumber/features/req-osrm-1.0.0-common.feature @@ -33,7 +33,7 @@ Feature: Road2-osrm-1.0.0-common When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" - And the response should contain "Parameter 'resourceId' is invalid: it does not exist on this service" + And the response should contain an attribute "message" with value "Parameter 'resourceId' is invalid: it does not exist on this service" Scenario: [route] Route sur l'API osrm 1.0.0 avec mauvais profile Given an "GET" request on operation "///route/v1/_/" in api "osrm" "1.0.0" @@ -46,7 +46,7 @@ Feature: Road2-osrm-1.0.0-common When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" - And the response should contain "Parameter 'profileId' is invalid" + And the response should contain an attribute "message" with value "Parameter 'profileId' is invalid" Scenario: [route] Route sur l'API osrm 1.0.0 avec mauvaise optimisation Given an "GET" request on operation "///route/v1/_/" in api "osrm" "1.0.0" @@ -59,7 +59,7 @@ Feature: Road2-osrm-1.0.0-common When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" - And the response should contain "Parameter 'optimizationId' is invalid" + And the response should contain an attribute "message" with value "Parameter 'optimizationId' is invalid" Scenario: [route] Route sur l'API osrm 1.0.0 avec mauvaises coordonnées Given an "GET" request on operation "///route/v1/_/" in api "osrm" "1.0.0" @@ -72,7 +72,7 @@ Feature: Road2-osrm-1.0.0-common When I send the request Then the server should send a response with status 400 And the response should have an header "content-type" with value "application/json" - And the response should contain "Parameter 'coordinates' is invalid" + And the response should contain an attribute "message" with value "Parameter 'coordinates' is invalid" diff --git a/test/functional/request/cucumber/features/req-osrm-1.0.0-osrm.feature b/test/functional/request/cucumber/features/req-osrm-1.0.0-osrm.feature index e68248e0..f853ab29 100644 --- a/test/functional/request/cucumber/features/req-osrm-1.0.0-osrm.feature +++ b/test/functional/request/cucumber/features/req-osrm-1.0.0-osrm.feature @@ -4,7 +4,6 @@ Feature: Road2-osrm-1.0.0-osrm Background: Given I have loaded all my test configuration in "../../configurations/local-service.json" - Scenario: Route sur l'API osrm 1.0.0 Given an "GET" request on operation "///route/v1/_/" in api "osrm" "1.0.0" And with path parameters: @@ -19,3 +18,155 @@ Feature: Road2-osrm-1.0.0-osrm And the response should contain an attribute "code" with value "Ok" And the response should contain an attribute "waypoints" And the response should contain an attribute "routes" + And the response should contain an attribute "routes.[0].geometry" + And the response should contain an attribute "routes.[0].distance" + And the response should contain an attribute "routes.[0].duration" + And the response should contain an attribute "routes.[0].legs" + And the response should contain an attribute "routes.[0].legs.[0].distance" + And the response should contain an attribute "routes.[0].legs.[0].duration" + And the response should contain an attribute "routes.[0].legs.[0].steps" + And the response should contain an attribute "routes.[0].legs.[0].summary" + And the response should contain an attribute "waypoints.[0].name" + And the response should contain an attribute "waypoints.[0].distance" + And the response should contain an attribute "waypoints.[0].location" + And the response should contain an attribute "waypoints.[0].hint" + + Scenario: [osrm/1.0.0] Route avec point intermédiaire + Given an "GET" request on operation "///route/v1/_/" in api "osrm" "1.0.0" + And with path parameters: + | key | value | + | resource | bduni-idf-osrm | + | profile | car | + | optimization | fastest | + | coordinates | 2.431111978054117,48.843103213614626;2.332134095987717,48.871144172016784;2.382134095987717,48.851144172016784 | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain an attribute "code" with value "Ok" + And the response should contain an attribute "waypoints.[2].name" + And the response should contain an attribute "routes.[0].legs.[1].summary" + + Scenario: [osrm/1.0.0] Route avec steps=false (default) + Given an "GET" request on operation "///route/v1/_/" in api "osrm" "1.0.0" + And with path parameters: + | key | value | + | resource | bduni-idf-osrm | + | profile | car | + | optimization | fastest | + | coordinates | 2.431111978054117,48.843103213614626;2.332134095987717,48.871144172016784;2.382134095987717,48.851144172016784 | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain an attribute "code" with value "Ok" + And the response should not contain an attribute "routes.[0].legs.[0].steps.[0]" + + Scenario: [osrm/1.0.0] Route avec steps=false explicitement + Given an "GET" request on operation "///route/v1/_/" in api "osrm" "1.0.0" + And with path parameters: + | key | value | + | resource | bduni-idf-osrm | + | profile | car | + | optimization | fastest | + | coordinates | 2.431111978054117,48.843103213614626;2.332134095987717,48.871144172016784;2.382134095987717,48.851144172016784 | + And with query parameters: + | key | value | + | steps | false | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain an attribute "code" with value "Ok" + And the response should not contain an attribute "routes.[0].legs.[0].steps.[0]" + + Scenario: [osrm/1.0.0] Route avec steps=true + Given an "GET" request on operation "///route/v1/_/" in api "osrm" "1.0.0" + And with path parameters: + | key | value | + | resource | bduni-idf-osrm | + | profile | car | + | optimization | fastest | + | coordinates | 2.431111978054117,48.843103213614626;2.332134095987717,48.871144172016784;2.382134095987717,48.851144172016784 | + And with query parameters: + | key | value | + | steps | true | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain an attribute "code" with value "Ok" + And the response should contain an attribute "routes.[0].legs.[0].steps.[0].name" + And the response should contain an attribute "routes.[0].legs.[0].steps.[0].distance" + And the response should contain an attribute "routes.[0].legs.[0].steps.[0].duration" + And the response should contain an attribute "routes.[0].legs.[0].steps.[0].mode" + And the response should contain an attribute "routes.[0].legs.[0].steps.[0].geometry" + And the response should contain an attribute "routes.[0].legs.[0].steps.[0].maneuver" + And the response should contain an attribute "routes.[0].legs.[0].steps.[0].intersections" + + Scenario: [osrm/1.0.0] Route avec geometries=polyline (default) + Given an "GET" request on operation "///route/v1/_/" in api "osrm" "1.0.0" + And with path parameters: + | key | value | + | resource | bduni-idf-osrm | + | profile | car | + | optimization | fastest | + | coordinates | 2.431111978054117,48.843103213614626;2.332134095987717,48.871144172016784;2.382134095987717,48.851144172016784 | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain an attribute "code" with value "Ok" + And the response should contain a string attribute "routes.[0].geometry" + And the response should contain a string attribute "waypoints.[0].hint" + + Scenario: [osrm/1.0.0] Route avec geometries=polyline explicitement + Given an "GET" request on operation "///route/v1/_/" in api "osrm" "1.0.0" + And with path parameters: + | key | value | + | resource | bduni-idf-osrm | + | profile | car | + | optimization | fastest | + | coordinates | 2.431111978054117,48.843103213614626;2.332134095987717,48.871144172016784;2.382134095987717,48.851144172016784 | + And with query parameters: + | key | value | + | geometries | polyline | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain an attribute "code" with value "Ok" + And the response should contain a string attribute "routes.[0].geometry" + And the response should contain a string attribute "waypoints.[0].hint" + + Scenario: [osrm/1.0.0] Route avec geometries=geojson + Given an "GET" request on operation "///route/v1/_/" in api "osrm" "1.0.0" + And with path parameters: + | key | value | + | resource | bduni-idf-osrm | + | profile | car | + | optimization | fastest | + | coordinates | 2.431111978054117,48.843103213614626;2.332134095987717,48.871144172016784 | + And with query parameters: + | key | value | + | geometries | geojson | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain an attribute "code" with value "Ok" + And the response should contain a string attribute "routes.[0].geometry.type" + And the response should contain a string attribute "waypoints.[0].hint" + + Scenario: [osrm/1.0.0] Route avec geometries=geojson et steps=true + Given an "GET" request on operation "///route/v1/_/" in api "osrm" "1.0.0" + And with path parameters: + | key | value | + | resource | bduni-idf-osrm | + | profile | car | + | optimization | fastest | + | coordinates | 2.431111978054117,48.843103213614626;2.332134095987717,48.871144172016784 | + And with query parameters: + | key | value | + | geometries | geojson | + | steps | true | + When I send the request + Then the server should send a response with status 200 + And the response should have an header "content-type" with value "application/json" + And the response should contain an attribute "code" with value "Ok" + And the response should contain a string attribute "routes.[0].geometry.type" + And the response should contain a string attribute "routes.[0].legs.[0].steps.[0].geometry.type" + And the response should contain a string attribute "waypoints.[0].hint" \ No newline at end of file From fbf8f80fdb0876b4280f83ae95fef901508cbb3e Mon Sep 17 00:00:00 2001 From: Loic Date: Thu, 4 Apr 2024 14:24:47 +0200 Subject: [PATCH 12/12] [doc] update version to 2.2.5 --- changelog.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index f8a08974..6d890e15 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,10 @@ # CHANGELOG +## 2.2.5 + +FEAT: +- The native OSRM API v1 was added but some parameters and error responses are still missing + ## 2.2.4 FIXED: - The pg module can emit error event and they were not catched and so it caused some crashs of Road2 diff --git a/package.json b/package.json index a6fb3853..a2dcb62b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "road2", - "version": "2.2.4", + "version": "2.2.5", "description": "Calcul d'itinéraire", "author": "RDEV - IGN", "main": "src/js/road2.js",