diff --git a/changelog.md b/changelog.md index e067086f..fcfcd151 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,14 @@ # CHANGELOG +## 3.0.2 + +ADDED: +- Valhalla route: optional `date_time` parameter, mapped to Valhalla `date_time` (`type = 1`, `value = `) +- Valhalla isochrone: optional `date_time` parameter, mapped to Valhalla `date_time` (`type = 1`, `value = `) + +UPDATED: +- Valhalla multimodal profile is now exposed in sample capabilities/configuration + ## 3.0.1 FIXED: diff --git a/documentation/apis/simple/1.0.0/api.json b/documentation/apis/simple/1.0.0/api.json index 348c722a..4c0189db 100644 --- a/documentation/apis/simple/1.0.0/api.json +++ b/documentation/apis/simple/1.0.0/api.json @@ -121,6 +121,17 @@ }, "example": "fastest" }, + { + "name": "date_time", + "in": "query", + "description": "Date et heure de départ pour le calcul multimodal Valhalla. Ce paramètre est optionnel et n'est pris en compte que pour les ressources Valhalla configurées en multimodal.", + "required": false, + "schema": { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$" + }, + "example": "2026-07-03T08:30" + }, { "name": "geometryFormat", "in": "query", @@ -550,6 +561,17 @@ }, "example": "départ" }, + { + "name": "date_time", + "in": "query", + "description": "Date et heure de départ pour le calcul multimodal Valhalla. Ce paramètre est optionnel et n'est pris en compte que pour les ressources Valhalla configurées en multimodal.", + "required": false, + "schema": { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$" + }, + "example": "2026-07-03T08:30" + }, { "name": "constraints", "in": "query", @@ -804,6 +826,10 @@ "optimization": { "type": "string" }, + "date_time": { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$" + }, "constraints": { "type": "array", "items": { @@ -888,6 +914,10 @@ "arrivé" ] }, + "date_time": { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$" + }, "constraints": { "type": "array", "items": { diff --git a/documentation/configuration/resources/valhalla.resource b/documentation/configuration/resources/valhalla.resource index 2f8d8ee0..06fc5142 100644 --- a/documentation/configuration/resources/valhalla.resource +++ b/documentation/configuration/resources/valhalla.resource @@ -34,7 +34,8 @@ "defaultValueContent": "car", "values": [ "car", - "pedestrian" + "pedestrian", + "multimodal" ] }, { @@ -83,6 +84,9 @@ "EPSG:2154" ] }, + { + "id": "date_time" + }, { "id": "timeUnit", "defaultValueContent": "minute", @@ -125,7 +129,7 @@ { "id": "resource", "values": [ - "bdtopo-pgr" + "bdtopo-valhalla" ] }, { @@ -155,7 +159,8 @@ "defaultValueContent": "car", "values": [ "car", - "pedestrian" + "pedestrian", + "multimodal" ] }, { @@ -166,21 +171,20 @@ "arrival" ] }, + { + "id": "date_time" + }, { "id": "constraints", "values": [ { - "keyType": "name-pgr", + "keyType": "name-valhalla", "key": "wayType", "availableConstraintType": ["banned"], "availableValues": [ { "value": "autoroute", - "field": "acces_vehicule_leger", - "condition": { - "type": "equal", - "value": "$niv3$A péage$niv3$" - } + "field": "exclude_tolls" } ] } diff --git a/documentation/index.md b/documentation/index.md index 3d2e3a4d..0e2a7498 100644 --- a/documentation/index.md +++ b/documentation/index.md @@ -1,18 +1,20 @@ # {{ title }} - Documentation -> **Description:** {{ description }} -> **Auteur et contributeurs:** {{ author }} -> **Version:** {{ version }} -> **Code source:** {{ repo_url }} +> **Description:** {{ description }} +> **Auteur et contributeurs:** {{ author }} +> **Version:** {{ version }} +> **Code source:** {{ repo_url }} > **Dernière mise à jour de la documentation:** {{ date_update }} -## Présentation générale +## Présentation générale -Road2 est un serveur de calcul d'itinéraires et d'isochrones écrit en Javascript et conçu pour fonctionner avec NodeJS. Ce serveur propose le calcul d'itinéraires et d'isochrones via des moteurs existants comme [OSRM](https://github.com/Project-OSRM/osrm-backend) ou [PGRouting](https://pgrouting.org/). Road2 est donc une interface pour moteurs de calculs. Ces derniers ne sont pas fait dans le code de Road2 mais via des appels à ses moteurs. Cela peut se traduire par l'appel à une librairie, ou à une base de données, ou encore à un autre service web. +Road2 est un serveur de calcul d'itinéraires et d'isochrones écrit en Javascript et conçu pour fonctionner avec NodeJS. Ce serveur propose le calcul d'itinéraires et d'isochrones via des moteurs existants comme [OSRM](https://github.com/Project-OSRM/osrm-backend) ou [PGRouting](https://pgrouting.org/). Road2 est donc une interface pour moteurs de calculs. Ces derniers ne sont pas fait dans le code de Road2 mais via des appels à ses moteurs. Cela peut se traduire par l'appel à une librairie, ou à une base de données, ou encore à un autre service web. Road2 a été conçu dans l'idée de pouvoir facilement ajouter des nouveaux moteurs et de nouvelles APIs, et cela, de manière totalement transparente les uns pour autres. Autrement dit, ajouter un moteur n'a pas d'impact sur les APIs déjà existantes. L'objectif est de faciliter l'ajout de nouvelles fonctionnalités tout en pérennisant l'accès au service. Pour une plus longue discussion sur les concepts logiciels introduits dans Road2, on pourra se référer à la documentation [suivante](./developers/concepts.md). -Actuellement, Road2 propose trois moteurs, OSRM, PGRouting et Valhalla, via une unique API REST. +Actuellement, Road2 propose trois moteurs, OSRM, PGRouting et Valhalla, via une unique API REST. + +Pour les ressources Valhalla configurees en multimodal, un parametre optionnel `date_time` est disponible sur les operations `route` et `isochrone` (format attendu: `YYYY-MM-DDTHH:mm`). ```{toctree} diff --git a/documentation_en/apis/simple/1.0.0/api.json b/documentation_en/apis/simple/1.0.0/api.json index 1783072a..4887da62 100644 --- a/documentation_en/apis/simple/1.0.0/api.json +++ b/documentation_en/apis/simple/1.0.0/api.json @@ -129,6 +129,17 @@ }, "example": "fastest" }, + { + "name": "date_time", + "in": "query", + "description": "Departure date and time for Valhalla multimodal computation. This parameter is optional and only used on Valhalla resources configured for multimodal routing.", + "required": false, + "schema": { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$" + }, + "example": "2026-07-03T08:30" + }, { "name": "geometryFormat", "in": "query", @@ -558,6 +569,17 @@ }, "example": "départ" }, + { + "name": "date_time", + "in": "query", + "description": "Departure date and time for Valhalla multimodal computation. This parameter is optional and only used on Valhalla resources configured for multimodal routing.", + "required": false, + "schema": { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$" + }, + "example": "2026-07-03T08:30" + }, { "name": "constraints", "in": "query", @@ -812,6 +834,10 @@ "optimization": { "type": "string" }, + "date_time": { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$" + }, "constraints": { "type": "array", "items": { @@ -896,6 +922,10 @@ "arrivé" ] }, + "date_time": { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$" + }, "constraints": { "type": "array", "items": { diff --git a/documentation_en/index.md b/documentation_en/index.md index f9e182f1..86c8381d 100644 --- a/documentation_en/index.md +++ b/documentation_en/index.md @@ -1,9 +1,9 @@ # {{ title }} - Documentation -> **Description:** {{ description }} -> **Authors and contributors:** {{ author }} -> **Version:** {{ version }} -> **Source code:** {{ repo_url }} +> **Description:** {{ description }} +> **Authors and contributors:** {{ author }} +> **Version:** {{ version }} +> **Source code:** {{ repo_url }} > **last update of the documentation:** {{ date_update }} ## General presentation @@ -14,6 +14,8 @@ Road2 was designed with the idea of being able to easily add new engines and new Currently, Road2 offers three engines, OSRM, PGRouting and Valhalla, through a single REST API. +For Valhalla resources configured for multimodal routing, an optional `date_time` parameter is available on `route` and `isochrone` operations (expected format: `YYYY-MM-DDTHH:mm`). + ```{toctree} --- caption: Road2 diff --git a/package.json b/package.json index 83fbf763..180401e8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "road2", - "version": "3.0.1", + "version": "3.0.2", "description": "Calcul d'itinéraire", "author": "RDEV - IGN", "main": "src/js/road2.js", diff --git a/readme.md b/readme.md index 1ff3829a..b3b9249b 100644 --- a/readme.md +++ b/readme.md @@ -1,52 +1,52 @@ # Road2 -[English](./readme_en.md) +[English](./readme_en.md) -## Présentation générale +## Présentation générale -Road2 est un serveur de calcul d'itinéraires et d'isochrones écrit en Javascript et conçu pour fonctionner avec NodeJS. Ce serveur propose le calcul d'itinéraires et d'isochrones via des moteurs existants comme [OSRM](https://github.com/Project-OSRM/osrm-backend) ou [PGRouting](https://pgrouting.org/). Road2 est donc une interface pour moteurs de calculs. Ces derniers ne sont pas fait dans le code de Road2 mais via des appels à ses moteurs. Cela peut se traduire par l'appel à une librairie, ou à une base de données, ou encore à un autre service web. +Road2 est un serveur de calcul d'itinéraires et d'isochrones écrit en Javascript et conçu pour fonctionner avec NodeJS. Ce serveur propose le calcul d'itinéraires et d'isochrones via des moteurs existants comme [OSRM](https://github.com/Project-OSRM/osrm-backend) ou [PGRouting](https://pgrouting.org/). Road2 est donc une interface pour moteurs de calculs. Ces derniers ne sont pas fait dans le code de Road2 mais via des appels à ses moteurs. Cela peut se traduire par l'appel à une librairie, ou à une base de données, ou encore à un autre service web. Road2 a été conçu dans l'idée de pouvoir facilement ajouter des nouveaux moteurs et de nouvelles APIs, et cela, de manière totalement transparente les uns pour autres. Autrement dit, ajouter un moteur n'a pas d'impact sur les APIs déjà existantes. L'objectif est de faciliter l'ajout de nouvelles fonctionnalités tout en pérennisant l'accès au service. Pour une plus longue discussion sur les concepts logiciels introduits dans Road2, on pourra se référer à la documentation [suivante](./documentation/developers/concepts.md). -Actuellement, Road2 propose trois moteurs, OSRM, PGRouting et Valhalla, via une unique API REST. D'autres APIs sont en cours de développement. +Actuellement, Road2 propose trois moteurs, OSRM, PGRouting et Valhalla, via une unique API REST. D'autres APIs sont en cours de développement. -## Fonctionnalités disponibles +## Fonctionnalités disponibles -Road2 propose plusieurs grandes familles de fonctionnalités : +Road2 propose plusieurs grandes familles de fonctionnalités : - Calculer des itinéraires -- Calculer des isochrones et des isodistances -- Administrer le service -- etc... +- Calculer des isochrones et des isodistances +- Administrer le service +- etc... Ces familles regroupent l'ensemble des fonctionnalités et sont détaillées [ici](./documentation/developers/functionnalities.md). -## Licence +## Licence -Road2 est diffusé sous la licence GPL v3. +Road2 est diffusé sous la licence GPL v3. ## Découverte du service -### Démonstrateur +### Démonstrateur -L'IGN propose un démonstrateur pour [l'itinéraire](https://geoservices.ign.fr/documentation/services_betas/itineraires.html) et [l'isochrone](https://geoservices.ign.fr/documentation/services_betas/isochrones.html). Ces démonstrateurs permettent de construire des requêtes via une carte et de visualiser les résultats. +L'IGN propose un démonstrateur pour [l'itinéraire](https://geoservices.ign.fr/documentation/services_betas/itineraires.html) et [l'isochrone](https://geoservices.ign.fr/documentation/services_betas/isochrones.html). Ces démonstrateurs permettent de construire des requêtes via une carte et de visualiser les résultats. -Autrement, pour une première prise en main du service en local, il est possible d'utiliser l'image [alpine](./docker/demonstration/Dockerfile) de Road2. Cela permettra d'avoir localement une instance du service et une page web permettant de le tester. Les instructions de mise en place sont données [ici](./documentation/docker/demonstration/readme.md). +Autrement, pour une première prise en main du service en local, il est possible d'utiliser l'image [alpine](./docker/demonstration/Dockerfile) de Road2. Cela permettra d'avoir localement une instance du service et une page web permettant de le tester. Les instructions de mise en place sont données [ici](./documentation/docker/demonstration/readme.md). -### Découvrir et tester les APIs du service +### Découvrir et tester les APIs du service -L'IGN propose une visualisation de l'API utilisateur pour [l'itinéraire](https://geoservices.ign.fr/documentation/services/api-et-services-ogc/itineraires/api) et [l'isochrone](https://geoservices.ign.fr/documentation/services/api-et-services-ogc/isochrone/api). +L'IGN propose une visualisation de l'API utilisateur pour [l'itinéraire](https://geoservices.ign.fr/documentation/services/api-et-services-ogc/itineraires/api) et [l'isochrone](https://geoservices.ign.fr/documentation/services/api-et-services-ogc/isochrone/api). L'IGN propose également des pages pour tester une instance du service sur l'ensemble de la France, avec une visualisation cartographique. Il y a une page pour l'[itinéraire](https://geoservices.ign.fr/documentation/services/api-et-services-ogc/itineraires) et une page pour l'[isochrone](https://geoservices.ign.fr/documentation/services/api-et-services-ogc/isochrones). -Autrement, l'ensemble des APIs disponibles sont documentées dans ce [dossier](./documentation/apis/). Pour le moment, il y a une seule API utilisateur qui est documentée via un [fichier](./documentation/apis/simple/1.0.0/api.json) JSON utilisant openapi 3.0.0, et une API d'administration documentée via un autre [fichier](./documentation/apis/administration/1.0.0/api.json) JSON suivant le même formalisme. +Autrement, l'ensemble des APIs disponibles sont documentées dans ce [dossier](./documentation/apis/). Pour le moment, il y a une seule API utilisateur qui est documentée via un [fichier](./documentation/apis/simple/1.0.0/api.json) JSON utilisant openapi 3.0.0, et une API d'administration documentée via un autre [fichier](./documentation/apis/administration/1.0.0/api.json) JSON suivant le même formalisme. -Il est possible de visualiser ces documentations d'API localement en suivant les instructions qui sont [ici](./documentation/docker/demonstration/readme.md). +Il est possible de visualiser ces documentations d'API localement en suivant les instructions qui sont [ici](./documentation/docker/demonstration/readme.md). -## Installation et utilisation de Road2 +## Installation et utilisation de Road2 ### Pré-requis -Pour utiliser ce projet, il est nécessaire d'avoir installé NodeJS sur la machine utilisée. La version de NodeJS utilisée pendant les développements est *12.14.0*. +Pour utiliser ce projet, il est nécessaire d'avoir installé NodeJS sur la machine utilisée. La version de NodeJS utilisée pendant les développements est *12.14.0*. ### Installation des modules @@ -57,41 +57,41 @@ npm install NB : Il y a des dépendances optionnelles pour gérer celles de chaque moteur. Pour plus d'informations, voir ce [document](./documentation/production/readme.md). -### Génération de données +### Génération de données Qu'importe la source des données, il est nécessaire de les fournir dans l'un des formats utilisables par Road2. Étant donné que ce dernier peut utiliser plusieurs moteurs les calculs, il accepte plusieurs formats de données: -- OSRM 5.26.0 rend possible l'utilisation de données OSRM générées avec cette version. -- PGRouting 3.1.3 rend possible l'utilisation d'une base de données utilisant cette version. Il sera nécessaire d'y ajouter les procédures du projet [pgrouting-procedures](https://github.com/IGNF/pgrouting-procedures) afin que Road2 puisse communiquer avec la base. +- OSRM 5.26.0 rend possible l'utilisation de données OSRM générées avec cette version. +- PGRouting 3.1.3 rend possible l'utilisation d'une base de données utilisant cette version. Il sera nécessaire d'y ajouter les procédures du projet [pgrouting-procedures](https://github.com/IGNF/pgrouting-procedures) afin que Road2 puisse communiquer avec la base. -Ces données peuvent donc être générées à partir d'une base de données quelconque, ou de fichiers OSM. Le projet [route-graph-generator](https://github.com/IGNF/route-graph-generator) propose des outils pour générer les graphes à partir de n'importe quelle base de données ou fichier osm. Si la base de données ne correspondant pas au format de la base attendue par route-graph-generator, il suffira de la dériver. +Ces données peuvent donc être générées à partir d'une base de données quelconque, ou de fichiers OSM. Le projet [route-graph-generator](https://github.com/IGNF/route-graph-generator) propose des outils pour générer les graphes à partir de n'importe quelle base de données ou fichier osm. Si la base de données ne correspondant pas au format de la base attendue par route-graph-generator, il suffira de la dériver. -Pour une discussion détaillée sur les données attendues, on pourra se référer à cette [documentation](./documentation/data/readme.md). +Pour une discussion détaillée sur les données attendues, on pourra se référer à cette [documentation](./documentation/data/readme.md). ### Configuration -Afin que le serveur fonctionne, il est nécessaire de le [configurer](./documentation/configuration/readme.md). Il s'agit de créer une arborescence de quelques fichiers JSON permettant l'instanciation du serveur avec des ressources. +Afin que le serveur fonctionne, il est nécessaire de le [configurer](./documentation/configuration/readme.md). Il s'agit de créer une arborescence de quelques fichiers JSON permettant l'instanciation du serveur avec des ressources. -### Lancement +### Lancement -Une fois configuré, il est possible de lancer une instance de Road2 avec la commande: +Une fois configuré, il est possible de lancer une instance de Road2 avec la commande: ``` node ${road2}/src/js/road2.js --ROAD2_CONF_FILE=${configuration}/administration.json ``` ### Pour plus de détails -On trouvera dans le dossier [docker/distrubutions](./docker/distributions) différents Dockerfiles qui permettent de voir l'installation et de tester le service sur différentes plateformes. Pour le moment, Debian 10 est disponible. +On trouvera dans le dossier [docker/distrubutions](./docker/distributions) différents Dockerfiles qui permettent de voir l'installation et de tester le service sur différentes plateformes. Pour le moment, Debian 10 est disponible. -## Participer aux développements +## Participer aux développements -Les participations à ce projet sont encouragées (votre notre [charte](./CODE_OF_CONDUCT.md) à ce sujet). Nous avons mis en place un [guide](./CONTRIBUTING.md) des contributions pour vous accompagner dans cette démarche. +Les participations à ce projet sont encouragées (votre notre [charte](./CODE_OF_CONDUCT.md) à ce sujet). Nous avons mis en place un [guide](./CONTRIBUTING.md) des contributions pour vous accompagner dans cette démarche. -On trouvera une documentation dédiée aux développeurs [ici](./documentation/developers/readme.md). Elle indique les concepts utiles pour effectuer des développements sur Road2. +On trouvera une documentation dédiée aux développeurs [ici](./documentation/developers/readme.md). Elle indique les concepts utiles pour effectuer des développements sur Road2. -Pour en savoir plus sur notre roadmap, vous pouvez regarder le projet [IGNF/Road2 Roadmap](https://github.com/orgs/IGNF/projects/3). +Pour en savoir plus sur notre roadmap, vous pouvez regarder le projet [IGNF/Road2 Roadmap](https://github.com/orgs/IGNF/projects/3). -Enfin, il est possible d'utiliser ce [docker-compose](./documentation/docker/dev/readme.md) pour avoir un environnement de développement incluant la construction des binaires, des modules et la génération des données. +Enfin, il est possible d'utiliser ce [docker-compose](./documentation/docker/dev/readme.md) pour avoir un environnement de développement incluant la construction des binaires, des modules et la génération des données. ## Utilisation en production -Afin d'utiliser Road2 en production, plusieurs informations sont données dans ce [document](./documentation/production/readme.md). Il s'agit principalement des besoins déjà observés pour une mise en production du service couvrant l'ensemble du territoire français. +Afin d'utiliser Road2 en production, plusieurs informations sont données dans ce [document](./documentation/production/readme.md). Il s'agit principalement des besoins déjà observés pour une mise en production du service couvrant l'ensemble du territoire français. diff --git a/readme_en.md b/readme_en.md index f2b0a15c..26d8380b 100644 --- a/readme_en.md +++ b/readme_en.md @@ -8,7 +8,7 @@ Road2 is a route and isochrone calculation server written in Javascript and desi Road2 was designed with the idea of being able to easily add new engines and new APIs, and this, in a completely transparent way for each other. In other words, adding an engine has no impact on existing APIs. The goal is to facilitate the addition of new features while maintaining access to the service. For a longer discussion on the software concepts introduced in Road2, please refer to the [following documentation](./documentation_en/developers/concepts.md). -Currently, Road2 offers three engines, OSRM, PGRouting and Valhalla, through one REST API. Other API are coming. +Currently, Road2 offers three engines, OSRM, PGRouting and Valhalla, through one REST API. Other API are coming. ## Features available diff --git a/src/js/apis/simple/1.0.0/controller/controller.js b/src/js/apis/simple/1.0.0/controller/controller.js index a53d42f4..b330e0eb 100644 --- a/src/js/apis/simple/1.0.0/controller/controller.js +++ b/src/js/apis/simple/1.0.0/controller/controller.js @@ -426,6 +426,31 @@ module.exports = { } // --- + // date_time + if (parameters.date_time) { + + LOGGER.debug("user date_time:"); + LOGGER.debug(parameters.date_time); + + const routeDateTimeParameter = routeOperation.getParameterById("date_time"); + if (routeDateTimeParameter.id) { + let validity = routeDateTimeParameter.check(parameters.date_time); + if (validity.code !== "ok") { + throw errorManager.createError(" Parameter 'date_time' is invalid: " + validity.message, 400); + } + } + + // Valhalla multimodal expects local datetime without timezone seconds. + const valhallaDateTimeRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/; + if (!valhallaDateTimeRegex.test(parameters.date_time)) { + throw errorManager.createError(" Parameter 'date_time' is invalid: expected format YYYY-MM-DDTHH:mm ", 400); + } + + routeRequest.date_time = parameters.date_time; + LOGGER.debug("date_time valide"); + } + // --- + // Contraintes // --- if (parameters.constraints) { @@ -905,6 +930,30 @@ module.exports = { distanceUnit ); + // date_time + if (parameters.date_time) { + + LOGGER.debug("user date_time:"); + LOGGER.debug(parameters.date_time); + + const isochroneDateTimeParameter = isochroneOperation.getParameterById("date_time"); + if (isochroneDateTimeParameter.id) { + let validity = isochroneDateTimeParameter.check(parameters.date_time); + if (validity.code !== "ok") { + throw errorManager.createError(" Parameter 'date_time' is invalid: " + validity.message, 400); + } + } + + const valhallaDateTimeRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/; + if (!valhallaDateTimeRegex.test(parameters.date_time)) { + throw errorManager.createError(" Parameter 'date_time' is invalid: expected format YYYY-MM-DDTHH:mm ", 400); + } + + isochroneRequest.date_time = parameters.date_time; + LOGGER.debug("date_time valide"); + } + // --- + /* Vérification de la validité du profile et de sa compatibilité avec le costType. */ if (!resource.checkSourceAvailibilityFromRequest(isochroneRequest)) { throw errorManager.createError("Parameters 'profile' and 'costType' are not compatible.", 400); @@ -1291,7 +1340,7 @@ module.exports = { // crs userResponse.crs = nearestResponse.coordinates.projection; - // Liste des points les plus proches + // Liste des points les plus proches userResponse.points = new Array(); for (let p=0; p < nearestResponse.points.length; p++) { diff --git a/src/js/apis/simple/1.0.0/init.js b/src/js/apis/simple/1.0.0/init.js index 988cd8e9..edb9d38b 100644 --- a/src/js/apis/simple/1.0.0/init.js +++ b/src/js/apis/simple/1.0.0/init.js @@ -193,6 +193,19 @@ module.exports = { optimizationParameterDescription.example = "fastest"; routeDescription.parameters.push(optimizationParameterDescription); + // route.parameters.date_time + let dateTimeServiceParameter = serviceOpRoute.getParameterById("date_time"); + let dateTimeParameterDescription = {}; + dateTimeParameterDescription.name = "date_time"; + dateTimeParameterDescription.in = "query"; + dateTimeParameterDescription.description = dateTimeServiceParameter.description; + dateTimeParameterDescription.required = dateTimeServiceParameter.required; + dateTimeParameterDescription.default = dateTimeServiceParameter.defaultValue; + dateTimeParameterDescription.schema = {}; + dateTimeParameterDescription.schema.type = "string"; + dateTimeParameterDescription.example = "2026-07-03T08:30"; + routeDescription.parameters.push(dateTimeParameterDescription); + // route.parameters.getSteps let getStepsServiceParameter = serviceOpRoute.getParameterById("getSteps"); let getStepsParameterDescription = {}; @@ -426,6 +439,19 @@ module.exports = { directionParameterDescription.example = "departure"; isochroneDescription.parameters.push(directionParameterDescription); + // isochrone.parameters.date_time + let dateTimeServiceParameter = serviceOpIsochrone.getParameterById("date_time"); + let dateTimeParameterDescription = {}; + dateTimeParameterDescription.name = "date_time"; + dateTimeParameterDescription.in = "query"; + dateTimeParameterDescription.description = dateTimeServiceParameter.description; + dateTimeParameterDescription.required = dateTimeServiceParameter.required; + dateTimeParameterDescription.default = dateTimeServiceParameter.defaultValue; + dateTimeParameterDescription.schema = {}; + dateTimeParameterDescription.schema.type = "string"; + dateTimeParameterDescription.example = "2026-07-03T08:30"; + isochroneDescription.parameters.push(dateTimeParameterDescription); + // isochrone.parameters.crs let projectionServiceParameter = serviceOpIsochrone.getParameterById("projection"); let crsParameterDescription = {}; @@ -694,6 +720,18 @@ module.exports = { } routeAvailableOperation.availableParameters.push(routeOptimization); + // route.date_time + let dateTimeParameter = resourceOperation.getParameterById("date_time"); + if (dateTimeParameter.id) { + let routeDateTime = {}; + routeDateTime.id = "date_time"; + routeDateTime.values = dateTimeParameter.values; + if (dateTimeParameter.serviceParameter.defaultValue === "true") { + routeDateTime.defaultValue = dateTimeParameter.defaultValueContent; + } + routeAvailableOperation.availableParameters.push(routeDateTime); + } + // route.getSteps let getStepsParameter = resourceOperation.getParameterById("getSteps"); let routeGetSteps = {}; @@ -857,6 +895,18 @@ module.exports = { } isochroneAvailableOperation.availableParameters.push(isochroneDirection); + // isochrone.date_time + let dateTimeParameter = resourceOperation.getParameterById("date_time"); + if (dateTimeParameter.id) { + let isochroneDateTime = {}; + isochroneDateTime.id = "date_time"; + isochroneDateTime.values = dateTimeParameter.values; + if (dateTimeParameter.serviceParameter.defaultValue === "true") { + isochroneDateTime.defaultValue = dateTimeParameter.defaultValueContent; + } + isochroneAvailableOperation.availableParameters.push(isochroneDateTime); + } + // isochrone.crs let projectionParameter = resourceOperation.getParameterById("projection"); let isochroneProjection = {}; diff --git a/src/js/parameters/parameterManager.js b/src/js/parameters/parameterManager.js index c90626eb..174840ec 100644 --- a/src/js/parameters/parameterManager.js +++ b/src/js/parameters/parameterManager.js @@ -6,6 +6,7 @@ const Parameter = require('../parameters/parameter'); const BoolParameter = require('../parameters/boolParameter'); const EnumParameter = require('../parameters/enumParameter'); const PointParameter = require('../parameters/pointParameter'); +const StringParameter = require('../parameters/stringParameter'); const FloatParameter = require('../parameters/floatParameter'); const IntParameter = require('../parameters/intParameter'); const ConstraintParameter = require('../parameters/constraintParameter'); @@ -113,7 +114,7 @@ module.exports = class parameterManager { * * @function * @name get checkParameterDirectory - * @description Vérifier les configurations de paramètre d'un dossier + * @description Vérifier les configurations de paramètre d'un dossier * @param {string} directory - Dossier qui contient les configurations de paramètre à vérifier * */ @@ -122,7 +123,7 @@ module.exports = class parameterManager { if (!fs.existsSync(directory)) { LOGGER.fatal("Mauvaise configuration: Le dossier des parametres n'existe pas : " + directory); return false; - } + } // On vérifie que l'application peut lire les fichiers du dossier if (!fs.readdirSync(directory).every(parameter => { @@ -138,7 +139,7 @@ module.exports = class parameterManager { let contentFile = {}; try { - // Il s'agit juste de savoir si le fichier est lisible par Road2, il sera exploité plus tard + // Il s'agit juste de savoir si le fichier est lisible par Road2, il sera exploité plus tard contentFile = JSON.parse(fs.readFileSync(parameterFile)); } catch (error) { LOGGER.error("Mauvaise configuration: impossible de lire ou de parser le fichier de parametres: " + parameterFile); @@ -263,6 +264,7 @@ module.exports = class parameterManager { } else { if (parameterConf.type !== "boolean" && parameterConf.type !== "enumeration" + && parameterConf.type !== "string" && parameterConf.type !== "point" && parameterConf.type !== "float" && parameterConf.type !== "integer" @@ -361,14 +363,14 @@ module.exports = class parameterManager { * * @function * @name flushCheckedParameter - * @description Vider la liste des paramètres déjà vérifiés + * @description Vider la liste des paramètres déjà vérifiés * */ flushCheckedParameter() { this._checkedParameterId = new Array(); this._checkedParametersConfiguration = {}; - + } /** @@ -409,7 +411,7 @@ module.exports = class parameterManager { LOGGER.error(error); return false; } - + if (!this.loadParameterConfiguration(parameterConf)) { LOGGER.error("Impossible de charger le parametre"); return false; @@ -437,7 +439,7 @@ module.exports = class parameterManager { * * @function * @name loadParameterConfiguration - * @description Créer un paramètre à partir de sa configuration + * @description Créer un paramètre à partir de sa configuration * @param {json} configuration - Configuration d'un parametre * @return {boolean} * @@ -672,6 +674,25 @@ module.exports = class parameterManager { // il n'y a rien à faire } + } else if (serviceParameterConf.type === "string") { + + // Gestion des valeurs par defaut + if (serviceParameterConf.defaultValue === "true") { + if (!resourceParameterJsonObject.defaultValueContent) { + LOGGER.error("Le parametre ne contient pas de valeur par defaut alors qu'il doit en avoir un"); + return false; + } else if (typeof resourceParameterJsonObject.defaultValueContent !== "string") { + LOGGER.error("La valeur par defaut n'est pas une chaine de caracteres"); + return false; + } + } + + if (resourceParameterJsonObject.values && resourceParameterJsonObject.values.pattern + && typeof resourceParameterJsonObject.values.pattern !== "string") { + LOGGER.error("Le pattern du parametre string n'est pas une chaine de caracteres"); + return false; + } + } else if (serviceParameterConf.type === "float" || serviceParameterConf.type === "integer") { // Gestion des valeurs par défaut @@ -681,7 +702,7 @@ module.exports = class parameterManager { LOGGER.error("Le parametre ne contient pas de valeur par defaut alors qu'il doit en avoir un"); return false; } else { - // on vérifie qu'on a bien un float + // on vérifie qu'on a bien un float if (typeof resourceParameterJsonObject.defaultValueContent !== "number") { LOGGER.error("La valeur par defaut n'est pas un nombre"); return false; @@ -693,7 +714,7 @@ module.exports = class parameterManager { // il n'y a rien à faire } - //on regarde si les min et max précisés sont bien des float + //on regarde si les min et max précisés sont bien des float // min et max ne sont pas obligatoires if (resourceParameterJsonObject.values) { @@ -716,7 +737,7 @@ module.exports = class parameterManager { } } else { - // il n'y a rien à vérifier + // il n'y a rien à vérifier } } else if (serviceParameterConf.type === "constraint") { @@ -939,6 +960,8 @@ module.exports = class parameterManager { curResParam = new BoolParameter(curSerParam); } else if (curSerParamConf.type === "enumeration") { curResParam = new EnumParameter(curSerParam); + } else if (curSerParamConf.type === "string") { + curResParam = new StringParameter(curSerParam); } else if (curSerParamConf.type === "point") { curResParam = new PointParameter(curSerParam); } else if (curSerParamConf.type === "float") { diff --git a/src/js/parameters/stringParameter.js b/src/js/parameters/stringParameter.js new file mode 100644 index 00000000..7d30774d --- /dev/null +++ b/src/js/parameters/stringParameter.js @@ -0,0 +1,130 @@ +'use strict'; + +const ResourceParameter = require('./resourceParameter'); +const log4js = require('log4js'); +const errorManager = require('../utils/errorManager'); +const validationManager = require('../utils/validationManager'); + +const LOGGER = log4js.getLogger("STRINGPARAM"); + +/** +* +* @class +* @name StringParameter +* @description Classe modelisant un parametre de type string dans une operation. +* +*/ +module.exports = class StringParameter extends ResourceParameter { + + /** + * + * @function + * @name constructor + * @description Constructeur de la classe StringParameter + * @param {object} parameter - Reference au parametre de service + * + */ + constructor(parameter) { + + // Parametre de service + super(parameter); + + // defaultValueContent + this._defaultValueContent = ""; + + // values + this._values = {}; + this._values.pattern = null; + + } + + /** + * + * @function + * @name get defaultValueContent + * @description Recuperer la valeur par defaut + * + */ + get defaultValueContent() { + return this._defaultValueContent; + } + + /** + * + * @function + * @name get values + * @description Recuperer les valeurs possibles (quand elles sont precisees) + * + */ + get values() { + return this._values; + } + + /** + * + * @function + * @name load + * @description Charger la configuration + * @param {string} parameterConf - Configuration d'un parametre + * @return {boolean} + * + */ + load(parameterConf) { + + if (super.serviceParameter.defaultValue === "true") { + this._defaultValueContent = parameterConf.defaultValueContent; + } + + if (parameterConf.values && parameterConf.values.pattern) { + this._values.pattern = parameterConf.values.pattern; + } + + return true; + + } + + /** + * + * @function + * @name specificCheck + * @description Verifier la validite d'une valeur par rapport au parametre + * @param {string} userValue - Valeur a verifier + * @return {object} result.code - "ok" si tout s'est bien passe et "error" sinon + * result.message - "" si tout s'est bien passe et la raison de l'erreur sinon + * + */ + specificCheck(userValue) { + + LOGGER.debug("specificCheck()"); + + if (typeof userValue !== "string") { + return errorManager.createErrorMessage("value is not a string but it should be"); + } + + if (this._values.pattern) { + const regex = new RegExp(this._values.pattern); + if (!regex.test(userValue)) { + return errorManager.createErrorMessage("value does not match required pattern"); + } + } + + return validationManager.createValidationMessage(""); + + } + + /** + * + * @function + * @name specificConvertion + * @description Convertir une valeur dans un format adapte aux requetes + * @param {string} userValue - Valeur a verifier + * @return {object} + * + */ + specificConvertion(userValue) { + + return userValue; + + } + +} \ No newline at end of file diff --git a/src/js/requests/isochroneRequest.js b/src/js/requests/isochroneRequest.js index e4ed838b..2c266bec 100644 --- a/src/js/requests/isochroneRequest.js +++ b/src/js/requests/isochroneRequest.js @@ -59,7 +59,10 @@ module.exports = class isochroneRequest extends Request { this._distanceUnit = distanceUnit; - // Gestion des contraintes + // date_time + this._date_time = null; + + // Gestion des contraintes this._constraints = new Array(); } @@ -294,6 +297,29 @@ module.exports = class isochroneRequest extends Request { this._distanceUnit = du; } + /** + * + * @function + * @name get date_time + * @description Recuperer la date et heure de depart. + * + */ + get date_time() { + return this._date_time; + } + + /** + * + * @function + * @name set date_time + * @description Attribuer la date et heure de depart. + * @param {object} dt - Date et heure de depart. + * + */ + set date_time(dt) { + this._date_time = dt; + } + /** * * @function diff --git a/src/js/requests/routeRequest.js b/src/js/requests/routeRequest.js index 3410036c..8a2680de 100644 --- a/src/js/requests/routeRequest.js +++ b/src/js/requests/routeRequest.js @@ -70,6 +70,9 @@ module.exports = class routeRequest extends Request { // distanceUnit this._distanceUnit = "meter"; + // date_time + this._date_time = null; + // Constraints // Tableau contenant des instances de Constraint this._constraints = new Array(); @@ -309,7 +312,7 @@ module.exports = class routeRequest extends Request { * * @function * @name get timeUnit - * @description Récupérer l'unité des durées + * @description Récupérer l'unité des durées * */ get timeUnit () { @@ -320,7 +323,7 @@ module.exports = class routeRequest extends Request { * * @function * @name set timeUnit - * @description Attribuer l'unité des durées + * @description Attribuer l'unité des durées * */ set timeUnit (i) { @@ -331,7 +334,7 @@ module.exports = class routeRequest extends Request { * * @function * @name get distanceUnit - * @description Récupérer l'unité des distances + * @description Récupérer l'unité des distances * */ get distanceUnit () { @@ -342,13 +345,35 @@ module.exports = class routeRequest extends Request { * * @function * @name set distanceUnit - * @description Attribuer l'unité des distances + * @description Attribuer l'unité des distances * */ set distanceUnit (i) { this._distanceUnit = i; } + /** + * + * @function + * @name get date_time + * @description Recuperer la date et heure de depart + * + */ + get date_time () { + return this._date_time; + } + + /** + * + * @function + * @name set date_time + * @description Attribuer la date et heure de depart + * + */ + set date_time (i) { + this._date_time = i; + } + /** * * @function diff --git a/src/js/sources/valhallaSource.js b/src/js/sources/valhallaSource.js index 10804402..3d40d437 100644 --- a/src/js/sources/valhallaSource.js +++ b/src/js/sources/valhallaSource.js @@ -191,7 +191,11 @@ module.exports = class valhallaSource extends Source { costingOptionsString += "}}"; // Permet de grandement se simplifier le parsing !! const optionsString = `"directions_options":{"format":"osrm"}`; - const commandString = `{${locationsString},${costingString},${costingOptionsString},${optionsString}}`; + let dateTimeString = ""; + if (request.date_time) { + dateTimeString = `,"date_time":{"type":1,"value":"${request.date_time}"}`; + } + const commandString = `{${locationsString},${costingString},${costingOptionsString},${optionsString}${dateTimeString}}`; LOGGER.info(commandString); return new Promise( (resolve, reject) => { @@ -292,7 +296,11 @@ module.exports = class valhallaSource extends Source { const contoursString = `"contours":[{"${request.costType}":${costValue}}]`; const reverseString = `"reverse":${reverse}`; const polygonsString = `"polygons":true`; - const commandString = `{${locationsString},${costingString},${costingOptionsString},${contoursString},${reverseString},${polygonsString}}`; + let dateTimeString = ""; + if (request.date_time) { + dateTimeString = `,"date_time":{"type":1,"value":"${request.date_time}"}`; + } + const commandString = `{${locationsString},${costingString},${costingOptionsString},${contoursString},${reverseString},${polygonsString}${dateTimeString}}`; LOGGER.info(commandString); return new Promise( (resolve, reject) => { diff --git a/src/resources/operations/isochrone.json b/src/resources/operations/isochrone.json index 77a80f4e..2c71a4d9 100644 --- a/src/resources/operations/isochrone.json +++ b/src/resources/operations/isochrone.json @@ -9,6 +9,7 @@ "costValue", "profile", "direction", + "date_time", "constraints", "projection", "geometryFormat", diff --git a/src/resources/operations/route.json b/src/resources/operations/route.json index fb3a1a1b..58300454 100644 --- a/src/resources/operations/route.json +++ b/src/resources/operations/route.json @@ -14,6 +14,7 @@ "geometryFormat", "bbox", "projection", + "date_time", "timeUnit", "distanceUnit", "constraints" diff --git a/src/resources/parameters/date_time.json b/src/resources/parameters/date_time.json new file mode 100644 index 00000000..972c6bb3 --- /dev/null +++ b/src/resources/parameters/date_time.json @@ -0,0 +1,11 @@ +{ + "id": "date_time", + "name": "Date et heure de depart", + "description": "Date et heure de depart pour le calcul multimodal Valhalla (format ISO local: YYYY-MM-DDTHH:mm)", + "required": "false", + "defaultValue": "false", + "example": "2026-07-03T08:30", + "type": "string", + "min": 0, + "max": 1 +} \ No newline at end of file