diff --git a/changelog.md b/changelog.md index fda6a48a..1c4fac7e 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,10 @@ # CHANGELOG +## 2.2.7 +ADDED: +- Valhalla: Provide the ability to set maxBuffer in exec command options to avoid maxBuffer lenght exceeded error (#109) +- Docs: add english documentation (#107) + ## 2.2.6 FIXED: - Ignore turf errors for steps simplification in pgrSource, and add a warning to analyse which steps trigger the error diff --git a/documentation/developers/functionnalities.md b/documentation/developers/functionnalities.md index b731dc80..6ae839e1 100644 --- a/documentation/developers/functionnalities.md +++ b/documentation/developers/functionnalities.md @@ -160,7 +160,7 @@ Les exclusions sont les contraintes classiques comme l'interdiction d'emprunter ### Déterminer le point du graphe le plus proche -Pour un point donnée, OSRM peut renovyer les points les plus proches du graphe. +Pour un point donnée, OSRM peut renvoyer les points les plus proches du graphe. diff --git a/documentation/developers/modification.md b/documentation/developers/modification.md index 7308ab98..2a41e5e4 100644 --- a/documentation/developers/modification.md +++ b/documentation/developers/modification.md @@ -96,7 +96,7 @@ Une opération est définie par un id et des paramètres. Un paramètre est quan Les dossiers `src/js/operations` et `src/js/parameters` contiennent le code nécessaire à la gestion des opérations et des paramètres. Il y a une distinction à faire entre les opérations de service et les opérations de ressource. Les opérations de services sont les opérations permises sur le service. Elles sont décrites par les JSON de `src/resources/`. Les opérations de ressource sont la déclinaison de ces opérations avec des paramètres spécifiques à chaque ressource. Ils sont décrits dans le fichier ressource. -Par exemple, on peut déclarer une opération de service que l'on nommera `route`. Pour le service, cette opération existe, est disponible et est décrite via des fichiers JSON. Cette opération peut nécessiter un paramètre `start`. À ce niveau, on sait que l'opération, que le paramètre existe et est obligatoire. Mais on ne sait pas quelles valeurs il peut prendre. Cela dépend de la ressource. Chaque ressource peut avoir une emprise différente. +Par exemple, on peut déclarer une opération de service que l'on nommera `route`. Pour le service, cette opération existe, est disponible et est décrite via des fichiers JSON. Cette opération peut nécessiter un paramètre `start`. À ce niveau, on sait que l'opération est disponible, que le paramètre existe et est obligatoire. Mais on ne sait pas quelles valeurs il peut prendre. Cela dépend de la ressource. Chaque ressource peut avoir une emprise différente. #### Ajouter/modifier/supprimer une opération diff --git a/documentation/production/readme.md b/documentation/production/readme.md index 39c85266..413add40 100644 --- a/documentation/production/readme.md +++ b/documentation/production/readme.md @@ -67,3 +67,8 @@ Par défaut, il y a des options qui sont utilisées mais elles peuvent être rem ### Gestion du HTTPS Road2 peut être directement interrogé en HTTPS. Pour cela, il utilise le module `https` de NodeJS. Il est donc possible de lui fournir les [options](https://nodejs.org/docs/latest-v12.x/api/tls.html#tls_tls_createserver_options_secureconnectionlistener) disponibles dans ce module. + +### Gestion du buffer + +Il est possible de changer la taille du buffer lors d'une source Valhalla en valorisant la variable d'environnement `EXEC_MAX_BUFFER_SIZE`. +La valeur par défaut est de 1MB. diff --git a/documentation_en/.gitignore b/documentation_en/.gitignore new file mode 100644 index 00000000..e6b95935 --- /dev/null +++ b/documentation_en/.gitignore @@ -0,0 +1,2 @@ +code/ +_build diff --git a/documentation_en/apis/administration/1.0.0/api.json b/documentation_en/apis/administration/1.0.0/api.json new file mode 100644 index 00000000..e6ee622f --- /dev/null +++ b/documentation_en/apis/administration/1.0.0/api.json @@ -0,0 +1,1862 @@ +{ + "openapi": "3.0.0", + "info": { + "description": "Description de l'API d'administration de Road2.", + "version": "1.0.0", + "title": "Administration de Road2", + "contact": { + "email": "contact.geoservices@ign.fr" + } + }, + "servers": [ + { + "url": "https://localhost:8079/admin/1.0.0/", + "description": "Serveur de test local" + } + ], + "tags": [ + { + "name": "État du serveur", + "description": "Connaître l'état de l'instance globale (administrateur et services)" + }, + { + "name": "Gestion de l'administrateur", + "description": "Administrer l'administrateur" + }, + { + "name": "Gestion des services", + "description": "Administrer des services" + }, + { + "name": "Gestion des ressources", + "description": "Administrer les ressources sur un service spécifique." + }, + { + "name": "Gestion des sources", + "description": "Administrer les sources sur un service spécifique." + } + ], + "paths": { + "/version": { + "get": { + "tags": [ + "État du serveur" + ], + "summary": "Obtenir la version du serveur.", + "description": "Cette requête retourne la version de Road2 utilisée par cette instance \nde l'administrateur.\n", + "operationId": "version", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "version": { + "type": "string", + "description": "Version de Road2 utilisée" + } + } + } + } + } + } + } + } + }, + "/health": { + "get": { + "tags": [ + "État du serveur" + ], + "summary": "Obtenir l'état du serveur.", + "description": "Cette requête retourne l'état du serveur d'administration et \nde l'ensemble des services qu'il administre. Cet état fait principalement \nréférence à la disponibilité des données. Chaque service a un état lié à la \ndisponibilité de ses sources. Chaque source a un état lié à la disponibilité de ses données. \n", + "operationId": "health", + "parameters": [ + { + "name": "verbose", + "description": "\"Par défaut, puisque verbose=false, la requête retourne seulement l'état global \nde l'administrateur et de ses services. Si on souhaite récupérer plus d'information \nsur l'état de chaque service, on met verbose à true.\"\n", + "in": "query", + "required": false, + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "state": { + "type": "string", + "enum": [ + "green", + "yellow", + "red" + ], + "description": "\"Lorsque tout va bien: 'green'. Si un des services a \nun problème:yellow. Si aucun service n'est disponible:red. Seule information retournée\nsi verbose=false. \"\n", + "example": "green" + }, + "administrator": { + "type": "object", + "properties": { + "state": { + "type": "string", + "example": "green", + "enum": [ + "green", + "yellow", + "red" + ], + "description": "\"Pour le moment, seul green est disponible car soit le serveur est disponible pour\nrépondre aux requêtes d'administration, soit il ne l'est pas du tout.\"\n" + } + } + }, + "services": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "main", + "description": "\"Id du service pour l'administrateur\"\n" + }, + "state": { + "type": "string", + "example": "green", + "enum": [ + "green", + "yellow", + "red" + ], + "description": "\"Lorsque toutes les sources sont disponibles : 'green'. Si une des sources a \nun problème:yellow. Si aucune source n'est disponible:red.\"\n" + }, + "sources": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "bduni-idf-car-fastest", + "description": "\"Id de la source pour le service\"\n" + }, + "state": { + "type": "string", + "example": "green", + "enum": [ + "green", + "red" + ], + "description": "\"Lorsque les données sont disponibles : 'green'. Si les données ne le sont plus: 'red'.\"\n" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "/configuration": { + "get": { + "tags": [ + "Gestion de l'administrateur" + ], + "summary": "Obtenir la configuration de l'administrateur.", + "description": "Cette requête retourne la configuration de l'administrateur.\n", + "operationId": "get-admin-configuration", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/adminConfiguration" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Gestion de l'administrateur" + ], + "summary": "Modifier la configuration de l'administrateur.", + "description": "Dans la configuration de l'administrateur, tout est modifiable. \nCela permet notamment de créer, modifier et supprimer les services \ngérés par cet administrateur. \n", + "operationId": "patch-admin-configuration", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/adminConfiguration" + } + } + } + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/adminConfiguration" + } + } + } + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + } + }, + "/services": { + "get": { + "tags": [ + "Gestion des services" + ], + "summary": "Obtenir la liste des services proposés par l'administrateur", + "description": "Cette requête retourne l'ensemble des services connus par l'administrateur. \nCette liste peut être vide si aucun service n'a été configuré.\n", + "operationId": "services", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "allOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + { + "$ref": "#/components/schemas/serviceConfiguration" + } + ] + } + } + } + } + }, + "500": { + "description": "Internal error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Gestion des services" + ], + "summary": "Créer un service.", + "description": "Si un service n'existe pas, il est possible de le créer \nen fournissant sa configuration. \n", + "operationId": "post-service", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/serviceConfiguration" + } + } + } + }, + "responses": { + "201": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "type": "object", + "allOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + { + "$ref": "#/components/schemas/serviceConfiguration" + } + ] + } + } + } + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + } + }, + "/services/{service}": { + "get": { + "tags": [ + "Gestion des services" + ], + "summary": "Obtenir la configuration d'un service.", + "description": "Cette requête retourne la configuration du service demandé.\n", + "operationId": "get-service", + "parameters": [ + { + "name": "service", + "description": "Id du service concerné", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/serviceConfiguration" + } + } + } + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Gestion des services" + ], + "summary": "Modifier la configuration d'un service existant.", + "description": "Il s'agit de modifier des paramètres de configuration d'un service \ndéjà existant. Il faut fournir la configuration du service à modifier.\nSi un redémarrage du service est nécessaire, il sera de fait \ntemporairement indisponible.\n", + "operationId": "patch-service", + "parameters": [ + { + "name": "service", + "description": "Id du service concerné", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/serviceConfiguration" + } + } + } + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/serviceConfiguration" + } + } + } + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Gestion des services" + ], + "summary": "Supprimer un service.", + "description": "Pour supprimer un service, il sera nécessaire d'avoir d'abord \nsupprimé les sources et ressources associées. Une fois supprimés, \nun service et sa configuration ne peuvent être récupérés. \n", + "operationId": "delete-service", + "parameters": [ + { + "name": "service", + "description": "Id du service concerné", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "successful operation" + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + } + }, + "/services/{service}/restart": { + "get": { + "tags": [ + "Gestion des services" + ], + "summary": "Demander le redémarage d'un service.", + "description": "Cette requête demande le redémarage du service demandé. Cette demande peut \néchouer et l'utilisateur en sera informé. \n", + "operationId": "get-service-restart", + "parameters": [ + { + "name": "service", + "description": "Id du service concerné", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/serviceConfiguration" + } + } + } + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + } + }, + "/services/{service}/projections/{projection}": { + "get": { + "tags": [ + "Gestion des services", + "Projection" + ], + "summary": "Récupération d'une projection supportée par un service.", + "description": "Cette requête retourne une projection supportée par un service.\n", + "operationId": "get-service-projection", + "parameters": [ + { + "name": "service", + "description": "Id du service concerné", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "projection", + "description": "Id de la projection concernée", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/projectionConfiguration" + } + } + } + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + } + }, + "/services/{service}/resources": { + "get": { + "tags": [ + "Gestion des ressources" + ], + "summary": "Obtenir la liste des ressources proposées par un service.", + "description": "Cette requête retourne la liste des ressources sur le service indiqué.\n", + "operationId": "get-resources", + "parameters": [ + { + "name": "service", + "in": "path", + "description": "Id du service concerné", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "source", + "description": "Filtre pour avoir les ressources qui utilisent cette source\n", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "resources": { + "type": "array", + "items": { + "$ref": "#/components/schemas/resourceConfiguration" + } + } + } + } + } + } + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Gestion des ressources" + ], + "summary": "Intégrer une nouvelle ressource. Il faut fournir la configuration de la ressource à intégrer.", + "description": "Les sources indiquées doivent déjà exister.", + "operationId": "post-resource", + "parameters": [ + { + "name": "service", + "description": "Id du service concerné", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "responses": { + "201": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + } + }, + "/services/{service}/resources/{resource}": { + "get": { + "tags": [ + "Gestion des ressources" + ], + "summary": "Obtenir la configuration d'une ressource.", + "description": "Cette requête retourne la configuration d'une ressource.", + "operationId": "get-resource", + "parameters": [ + { + "name": "service", + "description": "Id du service concerné", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "resource", + "in": "path", + "description": "Id de la ressource concernée", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/resourceConfiguration" + } + } + } + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Gestion des ressources" + ], + "summary": "Modifier une ressource existante. Il faut fournir la configuration de la ressource à modifier.", + "description": "Les sources indiquées doivent déjà exister.", + "operationId": "patch-resource", + "parameters": [ + { + "name": "service", + "description": "Id du service concerné", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "resource", + "in": "path", + "description": "Id de la ressource concernée", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/resourceConfiguration" + } + } + } + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/resourceConfiguration" + } + } + } + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Gestion des ressources" + ], + "summary": "Supprimer une ressource. Il faut fournir l'id de la ressource à supprimer.", + "description": "", + "operationId": "delete-resource", + "parameters": [ + { + "name": "service", + "description": "Id du service concerné", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "resource", + "in": "path", + "description": "Id de la ressource concernée", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "successful operation" + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + } + }, + "/services/{service}/sources": { + "get": { + "tags": [ + "Gestion des sources" + ], + "summary": "Obtenir la liste des sources proposées par un service.", + "description": "Cette requête retourne la liste des sources sur le service indiqué.", + "operationId": "get-sources", + "parameters": [ + { + "name": "service", + "in": "path", + "description": "Id du service concerné", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "sources": { + "type": "array", + "items": { + "$ref": "#/components/schemas/sourceConfiguration" + } + } + } + } + } + } + } + } + }, + "post": { + "tags": [ + "Gestion des sources" + ], + "summary": "Intégrer une nouvelle source. Il faut fournir la configuration de la source à intégrer.", + "description": "La donnée doit déjà exister.", + "operationId": "post-source", + "parameters": [ + { + "name": "service", + "description": "Id du service concerné", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/sourceConfiguration" + } + } + } + }, + "responses": { + "201": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/sourceConfiguration" + } + } + } + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + } + }, + "/services/{service}/sources/{source}": { + "get": { + "tags": [ + "Gestion des sources" + ], + "summary": "Obtenir la configuration d'une source.", + "description": "Cette requête retourne la configuration d'une source déjà existante.", + "operationId": "get-source", + "parameters": [ + { + "name": "service", + "description": "Id du service concerné", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "source", + "in": "path", + "description": "Id de la source concernée", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/sourceConfiguration" + } + } + } + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Gestion des sources" + ], + "summary": "Modifier une source existante. Il faut fournir la configuration de la source à modifier.", + "description": "La donnée doit déjà exister.", + "operationId": "patch-source", + "parameters": [ + { + "name": "service", + "description": "Id du service concerné", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "source", + "in": "path", + "description": "Id de la source concernée", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/sourceConfiguration" + } + } + } + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/sourceConfiguration" + } + } + } + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Gestion des sources" + ], + "summary": "Surpprimer une source. Il faut fournir l'id de la source à supprimer.", + "description": "La suppression de la donnée doit être faite autrement. \nOn doit avoir supprimé son usage dans les ressources avant.\n", + "operationId": "delete-source", + "parameters": [ + { + "name": "service", + "description": "Id du service concerné", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "source", + "in": "path", + "description": "Id de la source concernée", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "successful operation" + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "errorResponse": { + "type": "object", + "properties": { + "error": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + }, + "adminConfiguration": { + "type": "object", + "properties": { + "administration": { + "type": "object", + "properties": { + "api": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "admin" + }, + "version": { + "type": "string", + "example": "1.0.0" + } + } + }, + "services": { + "type": "array", + "minItems": 0, + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "main" + }, + "configuration": { + "type": "string", + "example": "/home/docker/config/service.json" + }, + "creationType": { + "type": "string", + "example": "newProcess" + } + } + } + }, + "network": { + "type": "object", + "properties": { + "server": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "administrator" + }, + "https": { + "type": "string", + "example": "false" + }, + "host": { + "type": "string", + "example": "0.0.0.0" + }, + "port": { + "type": "string", + "example": "8079" + } + } + } + } + }, + "logs": { + "type": "object", + "properties": { + "configuration": { + "type": "string", + "example": "/home/docker/config/log4js-administration.json" + } + } + } + } + } + } + }, + "serviceConfiguration": { + "type": "object", + "properties": { + "application": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "Road2" + }, + "title": { + "type": "string", + "example": "Service de calcul d'itinéraire" + }, + "description": { + "type": "string", + "example": "Ce service permet de calculer des itinéraires sur les données du Géoportail." + }, + "url": { + "type": "string", + "example": "https://localhost/" + }, + "provider": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "IGN" + }, + "site": { + "type": "string", + "example": "www.ign.fr" + }, + "mail": { + "type": "string", + "example": "contact.geoservices@ign.fr" + } + } + }, + "logs": { + "type": "object", + "properties": { + "configuration": { + "type": "string", + "example": "/home/docker/config/log4js-service.json" + } + } + }, + "operations": { + "type": "object", + "properties": { + "directory": { + "type": "string", + "example": "/home/docker/app/src/resources/operations" + }, + "parameters": { + "type": "object", + "properties": { + "directory": { + "type": "string", + "example": "/home/docker/app/src/resources/parameters" + } + } + } + } + }, + "resources": { + "type": "object", + "properties": { + "directories": { + "type": "array", + "items": { + "type": "string", + "example": "/home/docker/data/resources/" + } + } + } + }, + "sources": { + "type": "object", + "properties": { + "directories": { + "type": "array", + "items": { + "type": "string", + "example": "/home/docker/data/sources/" + } + } + } + }, + "network": { + "type": "object", + "properties": { + "servers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "internalServer" + }, + "https": { + "type": "string", + "example": "false" + }, + "host": { + "type": "string", + "example": "0.0.0.0" + }, + "port": { + "type": "string", + "example": "8080" + } + } + } + }, + "cors": { + "type": "object", + "properties": { + "configuration": { + "type": "string", + "example": "/home/docker/config/cors.json" + } + } + } + } + }, + "projections": { + "type": "object", + "properties": { + "directory": { + "type": "string", + "example": "/home/docker/config/projections/" + } + } + }, + "apis": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "simple" + }, + "version": { + "type": "string", + "example": "1.0.0" + } + } + } + } + } + } + } + }, + "projectionConfiguration": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "EPSG:4326" + }, + "parameters": { + "type": "string", + "example": "+proj=longlat +datum=WGS84 +no_defs" + } + } + }, + "resourceConfiguration": { + "type": "object", + "properties": { + "resource": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "my-resource" + }, + "type": { + "type": "string", + "example": "osrm" + }, + "description": { + "type": "string", + "example": "Exemple d'une ressource." + }, + "resourceVersion": { + "type": "string", + "example": "yyyy-mm-dd" + }, + "sources": { + "type": "array", + "items": { + "type": "string", + "example": "data-car-fastest" + } + }, + "availableOperations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "route" + }, + "parameters": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "resource" + }, + "defaultValueContent": { + "type": "string" + }, + "values": { + "type": "object" + } + } + } + } + } + } + } + } + } + } + }, + "sourceConfiguration": { + "oneOf": [ + { + "$ref": "#/components/schemas/osrmSourceConfiguration" + }, + { + "$ref": "#/components/schemas/pgrSourceConfiguration" + }, + { + "$ref": "#/components/schemas/valhallaSourceConfiguration" + } + ] + }, + "osrmSourceConfiguration": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "bduni-idf-car-fastest" + }, + "description": { + "type": "string", + "example": "test osrm" + }, + "type": { + "type": "string", + "enum": [ + "osrm" + ] + }, + "projection": { + "type": "string", + "example": "EPSG:4326" + }, + "bbox": { + "type": "string", + "example": "1.748.43.349.1" + }, + "storage": { + "type": "object", + "properties": { + "file": { + "type": "string", + "example": "/home/docker/data/bduni-idf-car-fastest/bduni-idf-car-fastest.osrm" + } + } + }, + "cost": { + "type": "object", + "properties": { + "cost": { + "type": "object", + "properties": { + "profile": { + "type": "string", + "example": "car" + }, + "optimization": { + "type": "string", + "example": "fastest" + } + } + } + } + } + } + }, + "pgrSourceConfiguration": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "bduni-idf-car-fastest" + }, + "description": { + "type": "string", + "example": "test osrm" + }, + "type": { + "type": "string", + "enum": [ + "pgrouting" + ] + }, + "projection": { + "type": "string", + "example": "EPSG:4326" + }, + "bbox": { + "type": "string", + "example": "1.748.43.349.1" + }, + "storage": { + "type": "object", + "properties": { + "base": { + "type": "object", + "properties": { + "dbConfig": { + "type": "string", + "example": "/home/docker/data/output_base.json" + }, + "schema": { + "type": "string", + "example": "public" + }, + "attributes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string", + "example": "name" + }, + "column": { + "type": "string", + "example": "concat_names" + }, + "default": { + "type": "string", + "example": "false" + } + } + } + } + } + } + } + }, + "costs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "cost": { + "type": "object", + "properties": { + "profile": { + "type": "string", + "example": "car" + }, + "optimization": { + "type": "string", + "example": "fastest" + }, + "costType": { + "type": "string", + "example": "time" + }, + "costColumn": { + "type": "string", + "example": "cost_s_car" + }, + "rcostColumn": { + "type": "string", + "example": "reverse_cost_s_car" + } + } + } + } + } + } + } + }, + "valhallaSourceConfiguration": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "bduni-idf-car-fastest" + }, + "description": { + "type": "string", + "example": "test osrm" + }, + "type": { + "type": "string", + "enum": [ + "valhalla" + ] + }, + "projection": { + "type": "string", + "example": "EPSG:4326" + }, + "bbox": { + "type": "string", + "example": "1.748.43.349.1" + }, + "storage": { + "oneOf": [ + { + "type": "object", + "properties": { + "file": { + "type": "string", + "example": "/home/docker/data/bduni-idf-car-fastest/bduni-idf-car-fastest.osrm" + } + } + }, + { + "type": "object", + "properties": { + "base": { + "type": "object", + "properties": { + "dbConfig": { + "type": "string", + "example": "/home/docker/data/output_base.json" + }, + "schema": { + "type": "string", + "example": "public" + }, + "attributes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string", + "example": "name" + }, + "column": { + "type": "string", + "example": "concat_names" + }, + "default": { + "type": "string", + "example": "false" + } + } + } + } + } + } + } + }, + { + "type": "object", + "properties": { + "tar": { + "type": "string", + "example": "/home/docker/data/bdtopo-valhalla-tiles.tar" + }, + "dir": { + "type": "string", + "example": "/home/docker/data/bdtopo-valhalla-tiles/" + }, + "config": { + "type": "string", + "example": "/home/docker/data/valhalla.json" + } + } + } + ] + }, + "costs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "cost": { + "type": "object", + "properties": { + "profile": { + "type": "string", + "example": "car" + }, + "optimization": { + "type": "string", + "example": "fastest" + }, + "costType": { + "type": "string", + "example": "time" + }, + "costing": { + "type": "string", + "example": "auto" + } + } + } + } + } + } + } + } + } + } + } \ No newline at end of file diff --git a/documentation_en/apis/administration/1.0.0/utilisation.md b/documentation_en/apis/administration/1.0.0/utilisation.md new file mode 100644 index 00000000..b96b336c --- /dev/null +++ b/documentation_en/apis/administration/1.0.0/utilisation.md @@ -0,0 +1,183 @@ +# Using the admin API + +## Concepts + +This administration API was coded with the idea of allowing as many actions as possible on the configuration of Road2. The second goal was to provide a few shortcuts and additional features that were deemed useful and didn't add much complexity to the code. + +Useful concepts (administrator, services, resources and sources) are defined on the [concepts] page (../../../developers/concepts.md). + + + + +## Prerequisites + +In order to use this API, it is necessary that the administrator is launched with a valid administrator configuration (ex. [road2.json](../../../../docker/config/road2. json)). On the other hand, it is not necessary to have configured services, sources and resources as they can be configured later. + +The other prerequisite, notably in order to be able to create a service, will be to have data accessible by Road2. Indeed, the administrator is not intended to manage the data itself (eg creation/deletion). + + + + + +## Features + +### Retrieve configuration from administrator + +The configuration must exist. It is then returned. + +### Modify admin configuration + +In the administrator configuration, everything is editable. This allows in particular to create, modify and delete the services managed by this administrator. + +When a change is sent, the resulting new configuration is first parsed. If it is valid, then the change is taken into account. Otherwise, an error is returned. + +When the configuration is modified, the administrator immediately takes these modifications into account. If a restart is required, then the administrator will be temporarily unavailable. Depending on the mode of creation of the services that depend on it, there will or will not be interruption of these services. + + + + +### Managing a service + +If a service exists, it is possible to modify and delete it. If it does not exist, it can be created. + +#### Retrieve the configuration of a service + +If the configuration exists, then it is returned. Otherwise, an error is returned. + +#### Modify the configuration of a service + +To modify the configuration of a service, it must exist. Everything can then be changed. + +When a change is sent, the resulting new configuration is first parsed. +If it is valid, then the change can be taken into account. Otherwise, an error code is returned. +If a restart of the service is necessary, then it will be temporarily unavailable. + +#### Create a service + +If a service does not exist, it is possible to create it by providing its configuration. + +The configuration is first checked. If it is valid, then the service is created. Otherwise, an error is returned. + +Once created, the service is available. + +#### Delete a service + +To delete a service, it will be necessary to have first deleted the associated sources and resources. + +Once deleted, a service and its configuration cannot be recovered. + + + +### Manage a source for a specific service + +The management of sources is always done within a specific service. + +The accessibility of a source is done via the notion of resource. If no resource uses a source, then the source is not queryable. However, to create a resource, it is necessary to have first created at least one source that it will use. + +#### Get source configuration + +If a source exists, it is possible to retrieve its configuration. If it does not exist, an error is returned. + +#### Modify the configuration of a source + +To modify the configuration of a source, it must exist. Everything can then be changed. + +When a change is sent, the resulting new configuration is first parsed. +If it is valid, then the change can be taken into account. Otherwise, an error code is returned. + +#### Create source configuration + +Creating a source configuration is equivalent to creating a source. + +To create a source, only the presence of the data is also required. + + +#### Delete source configuration + +Deleting a source configuration is equivalent to deleting a source. + +To delete a source, its use within the resources must already be deleted. + + + + +### Manage a resource for a specific service + +Resource management always takes place within a specific service. + +#### Get resource configuration + +If a resource exists, it is possible to retrieve its configuration. If it does not exist, an error is returned. + +#### Modify the configuration of a resource + +To modify the configuration of a resource, the resource must exist. Everything can then be changed. + +When a change is sent, the resulting new configuration is first parsed. +If valid, then the change can be taken into account. Otherwise, an error code is returned. + + +#### Create a resource configuration + +Creating a resource configuration is equivalent to creating a resource. When the configuration is created, the resource is made available. + +To create a resource, the sources used must exist. + +#### Delete resource configuration + +Deleting a resource configuration is equivalent to deleting a resource. The resource is therefore no longer available. + + + + + +### Know the status of the administrator and associated services + +This status refers to data availability. Each service has a status linked to the availability of its resources. Each resource has a status related to the availability of its sources. And each source, a status of the availability of its data. + + + + + +### Find out the deployed server version + +Find out the version of Road2 deployed on the current instance. + + + + + +## Shortcuts + +### List the services managed by an administrator + +For some uses, we would like to know the list of available services. It would be possible to have this information by reading the configuration of the administrator. + +### List the resources offered by a service + +For some uses, we would like to know the list of resources available on a specific service. + +### List the sources offered by a service + +For some uses, we would like to know the list of sources available on a specific service. + + + + +## Usage examples + +The most frequent cases seem to be the cases where we have an administrator and services which are already well configured. We then simply want to administer this existing configuration. To do this, simply refer to the paragraph describing the functionalities. + +### Configure a service only through the admin API + +In some cases, we want to create a new service next to the existing ones or simply create the very first one. Here's how: + +Assuming the admin is properly configured and started, and data is available, one can fully configure a new service through the admin API. + +We will start by creating a service with a good configuration. + +Now, we will be able to create sources using the data already available. + +From these sources, we will be able to create resources. + +Everything is in place, all that remains is to make the service available. \ No newline at end of file diff --git a/documentation_en/apis/simple/1.0.0/api.json b/documentation_en/apis/simple/1.0.0/api.json new file mode 100644 index 00000000..5dd07f64 --- /dev/null +++ b/documentation_en/apis/simple/1.0.0/api.json @@ -0,0 +1,1300 @@ +{ + "openapi": "3.0.0", + "info": { + "description": "Description de l'API du service d'itinéraire.", + "version": "1.0.0", + "title": "Service d'itinéraire", + "contact": { + "email": "contact.geoservices@ign.fr" + } + }, + "servers": [ + { + "url": "https://wxs.ign.fr/calcul/geoportail/itineraire/rest/1.0.0/", + "description": "Serveur de test IGN pour l'itinéraire" + }, + { + "url": "https://wxs.ign.fr/calcul/geoportail/isochrone/rest/1.0.0/", + "description": "Serveur de test IGN pour l'isochrone" + }, + { + "url": "http://localhost:8080/simple/1.0.0/", + "description": "Serveur de test local" + } + ], + "tags": [ + { + "name": "Découverte", + "description": "Découvrir le service et les ressources disponibles" + }, + { + "name": "Utilisation", + "description": "Utiliser le service d'itinéraire" + } + ], + "paths": { + "/getcapabilities": { + "get": { + "tags": [ + "Découverte" + ], + "summary": "Découvrir le service", + "description": "Découvrir le service: les opérations possibles, les ressources disponibles et les options proposées.", + "operationId": "getcapabilities", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/getcapabilities" + } + } + } + }, + "404": { + "description": "Not found" + } + } + } + }, + "/route": { + "get": { + "tags": [ + "Utilisation" + ], + "summary": "Calculer un itinéraire", + "description": "Calculer un itinéraire en fournissant un point de départ et un point d'arrivé. Plusieurs options peuvent être fournies.", + "operationId": "routeItineraire-get", + "parameters": [ + { + "name": "resource", + "in": "query", + "description": "Ressource utilisée pour le calcul. Les valeurs disponibles sont présentes dans le GetCapabilities. Une ressource est un graphe de navigation qui peut potentiellement avoir plusieurs modes de transports et de calculs. La ressource va plutôt parler du graphe, de la topologie de la donnée. Par exemple, il pourra s'agir d'un graphe de navigation issu de la BDTOPO v3 permettant de trouver l'itinéraire le plus court pour une voiture. ", + "required": true, + "schema": { + "type": "string" + }, + "example": "bdtopo-osrm" + }, + { + "name": "start", + "in": "query", + "description": "Point de départ. Il devra être exprimé dans le CRS, par défaut, de la ressource (voir le paramètre 'crs' dans le GetCapabilities).", + "required": true, + "schema": { + "$ref": "#/components/schemas/point" + }, + "example": "2.337306,48.849319" + }, + { + "name": "end", + "in": "query", + "description": "Point d'arrivée. Il devra être exprimé dans le CRS, par défaut, de la ressource (voir le paramètre 'crs' dans le GetCapabilities).", + "required": true, + "schema": { + "$ref": "#/components/schemas/point" + }, + "example": "2.367776,48.852891" + }, + { + "name": "intermediates", + "in": "query", + "description": "Points intermédiaires. Ils devront être exprimés dans le CRS, par défaut, de la ressource (voir le paramètre 'crs' dans le GetCapabilities).", + "required": false, + "schema": { + "$ref": "#/components/schemas/coordinates" + }, + "explode": false, + "style": "pipeDelimited", + "example": "2.368776,48.852890" + }, + { + "name": "profile", + "in": "query", + "description": "Mode de déplacement utilisé pour le calcul. Les valeurs disponibles et la valeur par défaut utilisées sont présentes dans le GetCapabilities.", + "required": false, + "schema": { + "type": "string" + }, + "example": "car" + }, + { + "name": "optimization", + "in": "query", + "description": "Mode de calcul utilisé pour déterminer l'itinéraire. Les valeurs disponibles et la valeur par défaut utilisées sont présentes dans le GetCapabilities.", + "required": false, + "schema": { + "type": "string" + }, + "example": "fastest" + }, + { + "name": "geometryFormat", + "in": "query", + "description": "Format des géométries dans la réponse. Peuvent être au format GeoJSON ou Encoded Polyline.", + "required": false, + "schema": { + "type": "string", + "enum": [ + "geojson", + "polyline", + "wkt" + ] + } + }, + { + "name": "constraints", + "in": "query", + "description": "Contraintes utilisées pour le calcul. Il s'agit d'un objet JSON (voir la version POST de cette opération). Les paramètres disponibles sont présents dans le GetCapabilities.", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "example": [ + "{'constraintType':'banned','key':'ways_type','operator':'=','value':'autoroute','threshold':{'key':'delta_time','operator':'<','value':'10'}}" + ], + "explode": false, + "style": "pipeDelimited" + }, + { + "name": "getSteps", + "in": "query", + "description": "Présence des étapes dans la réponse. Les valeurs disponibles et la valeur par défaut utilisées sont présentes dans le GetCapabilities.", + "required": false, + "schema": { + "type": "boolean" + }, + "example": true + }, + { + "name": "getBbox", + "in": "query", + "description": "Présence de l'emprise de l'itinéraire dans la réponse. Les valeurs disponibles et la valeur par défaut utilisées sont présentes dans le GetCapabilities.", + "required": false, + "schema": { + "type": "boolean" + }, + "example": true + }, + { + "name": "distanceUnit", + "in": "query", + "description": "Unité des distances renvoyées. Les valeurs disponibles et la valeur par défaut utilisées sont présentes dans le GetCapabilities.", + "required": false, + "schema": { + "type": "string" + }, + "example": "kilometer" + }, + { + "name": "timeUnit", + "in": "query", + "description": "Unité du temps renvoyé. Les valeurs disponibles et la valeur par défaut utilisées sont présentes dans le GetCapabilities.", + "required": false, + "schema": { + "type": "string" + }, + "example": "hour" + }, + { + "name": "crs", + "in": "query", + "description": "Projection des géometries. Les valeurs disponibles et la valeur par défaut utilisées sont présentes dans le GetCapabilities.", + "required": false, + "schema": { + "type": "string" + }, + "example": "EPSG:4326" + }, + { + "name": "waysAttributes", + "in": "query", + "description": "Attributs des tronçons à afficher dans la réponse. Les valeurs disponibles et les valeurs par défaut utilisées sont présentes dans le GetCapabilities.", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "example": "name", + "explode": false, + "style": "pipeDelimited" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/itineraire" + } + } + } + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "403": { + "description": "Not allowed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Utilisation" + ], + "summary": "Calculer un itinéraire", + "description": "Calculer un itinéraire en fournissant un point de départ et un point d'arrivé. Plusieurs options peuvent être fournies. Il est nécessaire de mettre 'Content-Type: application/json' dans le header de la requête.", + "operationId": "routeItineraire-post", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/routeBody" + } + } + } + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/itineraire" + } + } + } + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "403": { + "description": "Not allowed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + } + }, + "/nearest": { + "get": { + "tags": [ + "Utilisation" + ], + "summary": "Récupérer les points les plus proche d'une paire de coordonnées.", + "description": "Trouver les points les plus proche d'une paire de coordonnées fournie. Plusieurs options peuvent être fournies.", + "operationId": "nearestItineraire-get", + "parameters": [ + { + "name": "resource", + "in": "query", + "description": "Ressource utilisée pour le calcul. Les valeurs disponibles sont présentes dans le GetCapabilities. Une ressource est un graphe de navigation qui peut potentiellement avoir plusieurs modes de transports et de calculs. La ressource va plutôt parler du graphe, de la topologie de la donnée. Par exemple, il pourra s'agir d'un graphe de navigation issu de la BDTOPO v3 permettant de trouver l'itinéraire le plus court pour une voiture. ", + "required": true, + "schema": { + "type": "string" + }, + "example": "bdtopo-osrm" + }, + { + "name": "coordinates", + "in": "query", + "description": "Point de référence dont on cherche les points du graphe les plus proche. Il devra être exprimé dans le CRS, par défaut, de la ressource (voir le paramètre 'crs' dans le GetCapabilities).", + "required": true, + "schema": { + "$ref": "#/components/schemas/point" + }, + "example": "2.337306,48.849319" + }, + { + "name": "crs", + "in": "query", + "description": "Projection des géometries. Les valeurs disponibles et la valeur par défaut utilisées sont présentes dans le GetCapabilities.", + "required": false, + "schema": { + "type": "string" + }, + "example": "EPSG:4326" + }, + { + "name": "nbPoints", + "in": "query", + "description": "Nombre de points que l'on souhaite récupérer. Les valeurs disponibles et la valeur par défaut utilisées sont présentes dans le GetCapabilities.", + "required": false, + "schema": { + "type": "string" + }, + "example": "EPSG:4326" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/nearest" + } + } + } + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "403": { + "description": "Not allowed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Utilisation" + ], + "summary": "Récupérer les points les plus proche d'une paire de coordonnées.", + "description": "Trouver les points les plus proche d'une paire de coordonnées fournie. Plusieurs options peuvent être fournies. Il est nécessaire de mettre 'Content-Type: application/json' dans le header de la requête.", + "operationId": "nearestItineraire-post", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/nearestBody" + } + } + } + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/nearest" + } + } + } + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "403": { + "description": "Not allowed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + } + }, + "/isochrone": { + "get": { + "tags": [ + "Utilisation" + ], + "summary": "Calculer une isochrone ou une isodistance", + "description": "Obtenir une surface géo-localisée représentant l’ensemble des points atteignables à partir d’un point de départ. Les points de départ et d’arrivée peuvent être inversés: on obtient alors la liste des points de départs possibles permettant d’atteindre un point d’arrivée donné. On peut aussi fournir un critère de distance plutôt que de temps: on parle alors de calcul d’iso-distances.", + "operationId": "isochrone", + "parameters": [ + { + "name": "point", + "in": "query", + "description": "Coordonnées d'une position ponctuelle. C'est le point à partir duquel seront fait les calculs. Il devra être exprimé dans le CRS, par défaut, de la ressource (voir le paramètre 'crs' dans le GetCapabilities).", + "required": true, + "schema": { + "$ref": "#/components/schemas/point" + }, + "example": "2.337306,48.849319" + }, + { + "name": "resource", + "in": "query", + "description": "Ressource utilisée pour le calcul. Les valeurs disponibles et la valeur par défaut utilisées sont présentes dans le GetCapabilities. Une ressource est un graphe de navigation qui peut potentiellement avoir plusieurs modes de transports et de calculs. La ressource va plutôt parler du graphe, de la topologie de la donnée. Par exemple, il pourra s'agir d'un graphe de navigation issu de la BDTOPO v3 permettant de trouver des isochrones ou des isodistances.", + "required": true, + "schema": { + "type": "string" + }, + "example": "bdtopo-pgr" + }, + { + "name": "costValue", + "in": "query", + "description": "Valeur du coût utilisé pour le calcul. Les valeurs disponibles et la valeur par défaut utilisées sont présentes dans le GetCapabilities. On pourra, par exemple, préciser une distance ou un temps, selon l'optimisation choisie. L'unité dépendra aussi des paramètres distanceUnit et timeUnit.", + "required": true, + "schema": { + "type": "number", + "format": "float" + }, + "example": "1.0" + }, + { + "name": "costType", + "in": "query", + "description": "Type du coût utilisé pour le calcul. Les valeurs disponibles et la valeur par défaut utilisées sont présentes dans le GetCapabilities. On pourra, par exemple, préciser une distance ou un temps, selon l'optimisation choisie. L'unité dépendra aussi des paramètres distanceUnit et timeUnit.", + "required": true, + "schema": { + "type": "string" + }, + "example": "time" + }, + { + "name": "profile", + "in": "query", + "description": "Mode de déplacement utilisé pour le calcul. Les valeurs disponibles et la valeur par défaut utilisées sont présentes dans le GetCapabilities.", + "required": false, + "schema": { + "type": "string" + }, + "example": "car" + }, + { + "name": "direction", + "in": "query", + "description": "Sens du parcours. Cela permet de définir le sens du parcours. Soit on définit un point de départ et on obtient les points d'arrivé potentiels. Soit on définit un point d'arrivé et on otient les points de départ potentiels. Les valeurs disponibles et la valeur par défaut utilisées sont présentes dans le GetCapabilities.", + "required": false, + "schema": { + "type": "string", + "enum": [ + "departure", + "arrival" + ] + }, + "example": "départ" + }, + { + "name": "constraints", + "in": "query", + "description": "Contraintes utilisées pour le calcul. Il s'agit d'un objet JSON (voir la version POST de cette opération). Les paramètres disponibles sont présents dans le GetCapabilities.", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "example": [ + "{'constraintType':'banned','key':'ways_type','operator':'=','value':'autoroute'}" + ] + }, + { + "name": "geometryFormat", + "in": "query", + "description": "Format des géométries dans la réponse. Peuvent être au format GeoJSON ou Encoded Polyline.", + "required": false, + "schema": { + "type": "string", + "enum": [ + "geojson", + "polyline", + "wkt" + ] + } + }, + { + "name": "distanceUnit", + "in": "query", + "description": "Unité des distances renvoyées. Les valeurs disponibles et la valeur par défaut utilisées sont présentes dans le GetCapabilities.", + "required": false, + "schema": { + "type": "string" + }, + "example": "kilometer" + }, + { + "name": "timeUnit", + "in": "query", + "description": "Unité du temps renvoyé. Les valeurs disponibles et la valeur par défaut utilisées sont présentes dans le GetCapabilities.", + "required": false, + "schema": { + "type": "string" + }, + "example": "hour" + }, + { + "name": "crs", + "in": "query", + "description": "Projection des géometries. Les valeurs disponibles et la valeur par défaut utilisées sont présentes dans le GetCapabilities.", + "required": false, + "schema": { + "type": "string" + }, + "example": "EPSG:4326" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/isochrone" + } + } + } + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "403": { + "description": "Not allowed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Utilisation" + ], + "summary": "Calculer une isochrone ou une isodistance", + "description": "Obtenir une surface géo-localisée représentant l’ensemble des points atteignables à partir d’un point de départ. Les points de départ et d’arrivée peuvent être inversés: on obtient alors la liste des points de départs possibles permettant d’atteindre un point d’arrivée donné. On peut aussi fournir un critère de distance plutôt que de temps: on parle alors de calcul d’iso-distances. Il est nécessaire de mettre 'Content-Type: application/json' dans le header de la requête.", + "operationId": "isochrone-post", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/isochroneBody" + } + } + } + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/isochrone" + } + } + } + }, + "400": { + "description": "Invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "403": { + "description": "Not allowed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "point": { + "type": "string", + "pattern": "^(\\-?\\d+(\\.\\d+)?),\\s*(\\-?\\d+(\\.\\d+)?)$" + }, + "coordinates": { + "type": "array", + "items": { + "$ref": "#/components/schemas/point" + } + }, + "constraint": { + "type": "object", + "properties": { + "constraintType": { + "type": "string", + "enum": [ + "banned", + "preferred", + "unpreferred" + ] + }, + "key": { + "type": "string" + }, + "operator": { + "type": "string" + }, + "value": { + "type": "string" + }, + "threshold": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "operator": { + "type": "string" + }, + "value": { + "type": "string" + } + } + } + } + }, + "constraintIso": { + "type": "object", + "properties": { + "constraintType": { + "type": "string", + "enum": [ + "banned" + ] + }, + "key": { + "type": "string" + }, + "operator": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "routeBody": { + "type": "object", + "properties": { + "resource": { + "type": "string" + }, + "start": { + "$ref": "#/components/schemas/point" + }, + "end": { + "$ref": "#/components/schemas/point" + }, + "intermediates": { + "$ref": "#/components/schemas/coordinates" + }, + "profile": { + "type": "string" + }, + "optimization": { + "type": "string" + }, + "constraints": { + "type": "array", + "items": { + "$ref": "#/components/schemas/constraint" + } + }, + "getSteps": { + "type": "boolean" + }, + "geometryFormat": { + "type": "string", + "enum": [ + "geojson", + "polyline", + "wkt" + ] + }, + "getBbox": { + "type": "boolean" + }, + "distanceUnit": { + "type": "string" + }, + "timeUnit": { + "type": "string" + }, + "crs": { + "type": "string" + }, + "waysAttributes": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nearestBody": { + "type": "object", + "properties": { + "resource": { + "type": "string" + }, + "coordinates": { + "$ref": "#/components/schemas/point" + }, + "nbPoints": { + "type": "integer" + }, + "crs": { + "type": "string" + } + } + }, + "isochroneBody": { + "type": "object", + "properties": { + "point": { + "$ref": "#/components/schemas/point" + }, + "resource": { + "type": "string" + }, + "costType": { + "type": "string", + "enum": [ + "temps", + "distance" + ] + }, + "costValue": { + "type": "number", + "format": "float" + }, + "profile": { + "type": "string" + }, + "direction": { + "type": "string", + "enum": [ + "départ", + "arrivé" + ] + }, + "constraints": { + "type": "array", + "items": { + "$ref": "#/components/schemas/constraintIso" + } + }, + "geometryFormat": { + "type": "string", + "enum": [ + "geojson", + "polyline", + "wkt" + ] + }, + "distanceUnit": { + "type": "string" + }, + "timeUnit": { + "type": "string" + }, + "crs": { + "type": "string" + } + } + }, + "errorResponse": { + "type": "object", + "properties": { + "error": { + "type": "object", + "properties": { + "errorType": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + } + }, + "itineraire": { + "type": "object", + "properties": { + "start": { + "$ref": "#/components/schemas/point" + }, + "end": { + "$ref": "#/components/schemas/point" + }, + "geometry": { + "type": "string" + }, + "duration": { + "type": "number", + "format": "float" + }, + "distance": { + "type": "number", + "format": "float" + }, + "bbox": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "profile": { + "type": "string" + }, + "optimization": { + "type": "string" + }, + "crs": { + "type": "string" + }, + "constraints": { + "type": "array", + "items": { + "$ref": "#/components/schemas/constraint" + } + }, + "alerts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + }, + "portions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "start": { + "$ref": "#/components/schemas/point" + }, + "end": { + "$ref": "#/components/schemas/point" + }, + "duration": { + "type": "number", + "format": "float" + }, + "distance": { + "type": "number", + "format": "float" + }, + "bbox": { + "type": "string" + }, + "steps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "attributs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + } + } + }, + "duration": { + "type": "number", + "format": "float" + }, + "distance": { + "type": "number", + "format": "float" + }, + "geometry": { + "type": "string" + }, + "instruction": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "modifyer": { + "type": "string" + }, + "exit": { + "type": "string" + } + } + }, + "alerts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + } + }, + "nearest": { + "type": "object", + "properties": { + "resource": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "coordinates": { + "$ref": "#/components/schemas/point" + }, + "crs": { + "type": "string" + }, + "points": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "geometry": { + "type": "array", + "items": { + "type": "number" + } + }, + "distance": { + "type": "number" + } + } + } + } + } + }, + "isochrone": { + "type": "object", + "properties": { + "point": { + "$ref": "#/components/schemas/point" + }, + "resource": { + "type": "string" + }, + "resourceVersion": { + "type": "string" + }, + "costType": { + "type": "string" + }, + "costValue": { + "type": "number" + }, + "profile": { + "type": "string" + }, + "direction": { + "type": "string" + }, + "constraints": { + "$ref": "#/components/schemas/constraintIso" + }, + "crs": { + "type": "string" + }, + "geometry": { + "type": "string" + }, + "alerts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + } + }, + "getcapabilities": { + "type": "object", + "properties": { + "info": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + } + } + }, + "api": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "simple" + }, + "version": { + "type": "string", + "example": "1.0.0" + } + } + }, + "operations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "methods": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "GET", + "POST", + "PUT", + "DELETE" + ] + } + }, + "parameters": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "in": { + "type": "string" + }, + "description": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "default": { + "type": "boolean" + }, + "schema": { + "type": "string" + }, + "example": { + "type": "string" + } + } + } + } + } + } + }, + "resources": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "availableOperations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "availableParameters": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "values": { + "type": "string" + }, + "default": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/documentation_en/apis/simple/1.0.0/getCapabilities_example.yaml b/documentation_en/apis/simple/1.0.0/getCapabilities_example.yaml new file mode 100644 index 00000000..053392be --- /dev/null +++ b/documentation_en/apis/simple/1.0.0/getCapabilities_example.yaml @@ -0,0 +1,476 @@ +getCapabilities: + info: + name: "Calcul d'itinéraire" + title: "Service d'itinéraire" + url: "https://wxs.ign.fr/#KEY/geoportail/itineraire" + description: "Service permettant plusieurs opérations liées au calcul d'itinéraire." + api: + name: "rest" + version: "1.0.0" + operations: + - id: "route" + description: "Calculer un itinéraire." + url: "/route?" + methods: + - "GET" + - "POST" + parameters: + - name: "resource" + in: "query" + description: "Ressource utilisée pour le calcul. Les valeurs disponibles sont présentes dans la partie ressources du GetCapabilities." + required: true + default: false + schema: + type: "string" + example: "bduni" + - name: "start" + in: "query" + description: "Point de départ." + required: true + default: false + schema: + type: "string" + example: "48.849319,2.337306" + - name: "end" + in: "query" + description: "Point d'arrivée." + required: true + default: false + schema: + type: "string" + example: "48.852891,2.367776" + - name: "intermediates" + in: "query" + description: "Points intermédiaires." + required: false + default: false + schema: + type: "array" + items: + type: "string" + explode: false + style: "pipeDelimited" + example: "48.852890,2.368776|48.842891,2.367976" + - name: "profile" + in: "query" + description: "Mode de déplacement utilisé pour le calcul." + required: false + default: true + schema: + type: "enumeration" + example: "car" + - name: "optimization" + in: "query" + description: "Optimisation utilisée pour le calcul." + required: false + default: true + schema: + type: "enumeration" + example: "fastest" + - name: "constraints" + in: "query" + description: "Contraintes pour le calcul." + required: false + default: false + schema: + type: "array" + items: + type: "string" + example: "{'constraintType':'banned','key':'ways_type','operator':'=','value':'autoroute','thresholds':{'key':'delta_time','operator':'<','value':'10'}}" + - name: "getSteps" + in: "query" + description: "Présence des étapes dans la réponse." + required: false + default: true + schema: + type: "boolean" + example: true + - name: "getBbox" + in: "query" + description: "Présence de l'emprise de l'itinéraire dans la réponse." + required: false + default: true + schema: + type: "boolean" + example: true + - name: "distanceUnit" + in: "query" + description: "Unité des distances renvoyées." + required: false + default: true + schema: + type: "enumeration" + example: "kilometer" + - name: "timeUnit" + in: "query" + description: "Unité du temps renvoyé." + required: false + default: true + schema: + type: "enumeration" + example: "h-m-s" + - name: "crs" + in: "query" + description: "Projection des géometries." + required: false + default: true + schema: + type: "enumeration" + example: "EPSG:4326" + - name: "waysAttributes" + in: "query" + description: "Attributs des tronçons à afficher dans la réponse." + required: false + default: false + schema: + type: "array" + items: + type: "enumeration" + - id: "nearest" + description: "Trouver le noeud du graphe le plus proche d'un point donné." + url: "/nearest?" + methods: + - "GET" + - "POST" + parameters: + - name: "resource" + in: "query" + description: "Ressource utilisée pour le calcul. Les valeurs disponibles sont présentes dans la partie ressources du GetCapabilities." + required: true + default: false + schema: + type: "string" + example: "bduni" + - name: "coordinates" + in: "query" + description: "Point utilisé pour trouver un ou des noeuds." + required: true + default: false + schema: + type: "string" + example: "48.849319,2.337306" + - name: "nbPoints" + in: "query" + description: "Nombre de points retournés." + required: false + default: true + schema: + type: "number" + format: "integer" + example: "5" + - name: "crs" + in: "query" + description: "Projection des géometries." + required: false + default: true + schema: + type: "enumeration" + example: "EPSG:4326" + - id: "isochrone" + description: "Calculer une isochrone ou une isodistance." + url: "/isochrone?" + methods: + - "GET" + - "POST" + parameters: + - name: "resource" + in: "query" + description: "Ressource utilisée pour le calcul. Les valeurs disponibles sont présentes dans la partie ressources du GetCapabilities." + required: true + default: false + schema: + type: "string" + example: "bduni" + - name: "point" + in: "query" + description: "Point de départ." + required: true + default: false + schema: + type: "string" + example: "48.849319,2.337306" + - name: "costType" + in: "query" + description: "Coût utilisé pour le calcul. Les valeurs disponibles sont présentes dans la partie ressources du GetCapabilities." + required: true + default: false + schema: + type: "enumeration" + example: "temps" + - name: "costValue" + in: "query" + description: "Valeur du coût utilisé pour le calcul. Les valeurs disponibles sont présentes dans la partie ressources du GetCapabilities." + required: true + default: false + schema: + type: "number" + format: "float" + example: "1.0" + - name: "profile" + in: "query" + description: "Mode de déplacement utilisé pour le calcul." + required: false + default: true + schema: + type: "enumeration" + example: "car" + - name: "direction" + in: "query" + description: "Sens du parcours. Cela permet de définir le sens du parcours. Soit on définit un point de départ et on obtient les points d'arrivé potentiels. Soit on définit un point d'arrivé et on otient les points de départ potentiels." + required: false + default: true + schema: + type: "enumeration" + example: "départ" + - name: "constraints" + in: "query" + description: "Contraintes pour le calcul." + required: false + default: false + schema: + type: "array" + items: + type: "string" + example: "{'constraintType':'banned','key':'ways_type','operator':'=','value':'autoroute'}" + - name: "distanceUnit" + in: "query" + description: "Unité des distances renvoyées." + required: false + default: true + schema: + type: "enumeration" + example: "kilometer" + - name: "timeUnit" + in: "query" + description: "Unité du temps renvoyé." + required: false + default: true + schema: + type: "enumeration" + example: "h-m-s" + - name: "crs" + in: "query" + description: "Projection des géometries." + required: false + default: true + schema: + type: "enumeration" + example: "EPSG:4326" + resources: + - id: "bduni" + description: "Données BDUNI v2." + availableOperations: + - id: "route" + availableParameters: + - id: "resource" + values: {"bduni"} + - id: "start" + values: + bbox: "-180,-90,180,90" + projection: "EPSG:4326" + - id: "end" + values: + bbox: "-180,-90,180,90" + projection: "EPSG:4326" + - id: "intermediates" + values: + bbox: "-180,-90,180,90" + projection: "EPSG:4326" + - id: "profile" + default: "car" + values: {"car"} + - id: "optimization" + default: "fastest" + values: {"fastest","shortest"} + - id: "constraints" + values: + keys: + - key: "road_type" #à cause du type osrm + description: "Type d'arc." + availableConstraintType: {"banned"} #à cause du type osrm + availableOperators: {"="} + values: {"autoroute","tunnel"} #dans la configuration de la ressource + thresholds: #automatique + - key: "delta_time" + description: "Delta entre le temps (en min) avec les contraintes et sans ces dernières." + availableOperators: {"<",">","<=",">="} + values: + min: 1 + max: 600 + - key: "delta_distance" + description: "Delta entre la distance (en km) avec les contraintes et sans ces dernières." + availableOperators: {"<",">","<=",">="} + values: + min: 1 + max: 1000 + - id: "format" + default: "application/json" + values: {"application/json","application/xml"} + - id: "getSteps" + default: true + values: {"true","false"} + - id: "getBbox" + default: true + values: {"true","false"} + - id: "getInstructions" + default: false + values: {"true","false"} + - id: "distanceUnit" + default: "kilometer" + values: {"meter","kilometer"} + - id: "timeUnit" + default: "h-m-s" + values: {"heure","minute","seconde","h-m-s"} + - id: "crs" + default: "EPSG:4326" + values: {"EPSG:4326","EPSG:2154"} + - id: "waysAttributes" + # cost, duration et distance sont toujours calculés par défaut + default: {"name","cost","duration","distance"} + #éléments présents par défaut dans osrm + values: {"name","cost","duration","distance"} + - id: "bduni_j+1" + description: "Données BDUNI v2 mise à jour quotidiennement." + availableOperations: + - id: "route" + availableParameters: + - id: "resource" + values: {"bduni_j+1"} + - id: "start" + values: + bbox: "0,6000000,1300000,7200000" + projection: "EPSG:2154" + - id: "end" + values: + bbox: "0,6000000,1300000,7200000" + projection: "EPSG:2154" + - id: "intermediates" + values: + bbox: "0,6000000,1300000,7200000" + projection: "EPSG:2154" + - id: "profile" + values: {"car","bike"} + - id: "optimization" + default: "fastest" + values: {"fastest","shortest"} + - id: "format" + default: "application/json" + values: {"application/json","application/xml"} + - id: "constraints" + values: + keys: + - key: "id" #automatique + description: "Id d'un noeud ou d'un arc. Récupérable via /nearest?getId=true." + availableConstraintType: {"banned","preferred","unpreferred"} + availableOperators: {"="} + values: #calculer sur la table au démarage + min: 1 + max: 10000000 + - key: "polygone" #automatique + description: "Polygone délimitant une zone géographique." + availableConstraintType: {"banned","preferred","unpreferred"} + availableOperators: {"="} + values: #bbox des données + bbox: "0,6000000,1300000,7200000" + projection: "EPSG:2154" + - key: "road_type" #dans la configuration de la ressource + description: "Type d'arc emprunté." + availableConstraintType: {"banned","preferred","unpreferred"} + availableOperators: {"=","!=","like"} + values: {"autoroute","tunnel"} + - key: "road_slope" #dans la configuration de la ressource + description: "Pente des arcs empruntés." + availableConstraintType: {"banned","preferred","unpreferred"} + availableOperators: {"=","!=","<",">","<=",">="} + values: + min: 0 + max: 45 + - key: "road_name" #dans la configuration de la ressource + description: "Nom des arcs empruntés." + availableConstraintType: {"banned","preferred","unpreferred"} + availableOperators: {"=","!=","like"} + values: "string" + thresholds: #automatique + - key: "delta_time" + description: "Delta entre le temps (en min) avec les contraintes et sans ces dernières." + availableOperators: {"<",">","<=",">="} + values: + min: 1 + max: 600 + - key: "delta_distance" + description: "Delta entre la distance (en km) avec les contraintes et sans ces dernières." + availableOperators: {"<",">","<=",">="} + values: + min: 1 + max: 1000 + - id: "getSteps" + default: true + values: {"true","false"} + - id: "getBbox" + default: true + values: {"true","false"} + - id: "getInstructions" + default: false + values: {"true","false"} + - id: "distanceUnit" + default: "kilometer" + values: {"meter","kilometer"} + - id: "timeUnit" + default: "h-m-s" + values: {"heure","minute","seconde","h-m-s"} + - id: "crs" + default: "EPSG:2154" + values: {"EPSG:2154"} + - id: "waysAttributes" + # cost, duration et distance sont toujours calculés par défaut + default: {"name","cost","duration","distance"} + values: {"name","type","cost","duration","distance","slope"} + - id: "isochrone" #seules les ressources pgr proposent de l'isochrone + availableParameters: + - id: "resource" + values: {"bduni_j+1"} + - id: "point" + values: + bbox: "0,6000000,1300000,7200000" + projection: "EPSG:2154" + - id: "costType" + default: "temps" + values: {"temps","distance"} + - id: "costValue" + values: + - id: "temps" + unit: "minute" + description: "Calculer des isochrones." + min: 1.0 + max: 600.0 + - id: "distance" + unit: "kilometer" + description: "Calculer des iso-distances." + min: 1.0 + max: 1000.0 + - id: "profile" + default: "car" + values: {"car","bike"} + - id: "direction" + default: "départ" + values: {"départ","arrivée"} + - id: "constraints" + values: + availableConstraintType: {"banned"} #car isochrone + keys: + - key: "road_type" #dans la configuration de la ressource + description: "Type d'arc." + availableOperators: {"=","!=","like"} + values: {"autoroute","tunnel"} #dans la configuration de la ressource + - id: "format" + default: "application/json" + values: {"application/json","application/xml"} + - id: "distanceUnit" + default: "kilometer" + values: {"meter","kilometer"} + - id: "timeUnit" + default: "h-m-s" + values: {"heure","minute","seconde","h-m-s"} + - id: "crs" + default: "EPSG:2154" + values: {"EPSG:2154"} diff --git a/documentation_en/apis/simple/1.0.0/getCapabilities_model.yaml b/documentation_en/apis/simple/1.0.0/getCapabilities_model.yaml new file mode 100644 index 00000000..5b931ff9 --- /dev/null +++ b/documentation_en/apis/simple/1.0.0/getCapabilities_model.yaml @@ -0,0 +1,161 @@ +# Modèle de données d'un GetCapabilities + +# un getCapabilities est un objet qui contient toutes les informations utile pour utiliser le service avec les ressources disponibles sur l'instance. +"getCapabilities": + type: object + required: true + properties: + # Informations sur le service + "info": + type: object + required: true + properties: + # Nom du service + "name": + type: string + required: true + # Titre du service + "title": + type: string + required: true + # Url d'accès au service + "url": + type: string + required: false + # Description du service + "description": + type: string + required: true + # un getCapabilities correspond à une API. Elle est ainsi rappelée. + "api": + type: object + required: true + properties: + # Nom de l'API + "name": + type: string + required: true + # Version de l'API + "version": + type: string + required: true + # Liste des opérations disponibles pour l'utilisation. Il n'y a pas les opérations d'administrations car ce document aide l'utilisation du service et non son administration. + "operations": + type: array + required: true + minItems: 1 + items: + type: object + properties: + # Id de l'opération décrite. Cet id sera repris plus tard pour décrire les valeurs disponibles de chaque paramètre. + "id": + type: string + required: true + # Description de l'opération. + "description": + type: string + required: true + # Partie de la route qui suit l'url du service. + "url": + type: string + required: true + # Méthodes d'accès à l'opération. + "methods": + type: array + required: true + uniqueItems: true + items: + type: string + enum: ["GET","POST","PUT","DELETE"] + # Paramètres de l'opération. + "parameters": + type: array + required: true + items: + type: object + properties: + # Nom du paramètre. Ce nom sera repris plus tard pour décrire les valeurs disponibles de chaque paramètre. + "name": + type: string + required: true + # Emplacement du paramètre. + "in": + type: string + required: true + enum: ["query","url","body"] + # Description du paramètre + "description": + type: string + required: true + # Précise si le paramètre est obligatoire ou pas + "required": + type: "boolean" + required: true + # Précise s'il existe une valeur par défaut quand le paramètre n'est pas précisé. Cette valeur par défaut est indiquée dans la suite du GetCapabilities. + "default": + type: "boolean" + required: true + # format de la donnée + "schema": + type: "schema" + required: true + # Exemple pour aider à comprendre l'utilisation du paramètre. + "example": + type: string + required: true + # Pour les paramètres qui peuvent être répétés, il indique s'il faut réécrire le nom du paramètre ou pas. + "explode": + type: "boolean" + required: false + # Valable uniquement pour explode=false, il indique comment séparer les différentes valeurs du paramètre. + "style": + type: string + required: false + enum: ["pipeDelimited","spaceDelimited","commaDelimited"] + # Description des ressources disponibles + "resources": + type: array + required: true + minItems: 1 + items: + type: object + properties: + # Id de la ressource. Utilisé dans les requêtes. + "id": + type: string + required: true + # Description de la ressource. + "description": + type: string + required: true + # Opérations disponibles sur cette ressource. + "availableOperations": + type: array + required: true + items: + type: object + properties: + # Id de l'opération. C'est l'id que l'on trouve dans la description des opérations. + "id": + type: string + required: true + # Valeurs des paramètres de l'opération + "availableParameters": + type: array + required: true + items: + type: object + properties: + # Nom du paramétre concerné + "id": + type: string + required: true + # Valeurs disponibles pour ce paramètre + "values": + type: "schema" + required: true + # Valeur par défaut s'il y en a une. + "default": + type: "schema" + required: false + diff --git a/documentation_en/conf.py b/documentation_en/conf.py new file mode 100644 index 00000000..96991791 --- /dev/null +++ b/documentation_en/conf.py @@ -0,0 +1,111 @@ +#!python3 + +""" + Configuration for project documentation using Sphinx. +""" + +# standard +import sys +import json +from datetime import datetime +from os import environ, path + +sys.path.insert(0, path.abspath("..")) # move into project package + +# -- Build environment ----------------------------------------------------- +on_rtd = environ.get("READTHEDOCS", None) == "True" + +# -- Project information ----------------------------------------------------- +f = open('../package.json') + +package_node = json.load(f) + +author = package_node["author"] +description = package_node["description"] +project = package_node["name"] +version = package_node["version"] +uri_repository = "https://github.com/IGNF/road2/" + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + # Sphinx included + "sphinx.ext.autosectionlabel", + "sphinx.ext.extlinks", + "sphinx.ext.githubpages", + "sphinx.ext.imgmath", + "sphinx.ext.intersphinx", + "sphinx.ext.viewcode", + # 3rd party + "myst_parser", + "sphinx_copybutton", + "sphinx.ext.napoleon", +] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +source_suffix = {".md": "markdown", ".rst": "restructuredtext"} +autosectionlabel_prefix_document = True +# The master toctree document. +master_doc = "index" + + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path . +exclude_patterns = [ + "_build", + ".venv", + "Thumbs.db", + ".DS_Store", + "_output", + "demo", +] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = "sphinx" + + +# -- Options for HTML output ------------------------------------------------- + +# -- Theme + +#html_favicon = str(__about__.__icon_path__) +#html_logo = str(__about__.__icon_path__) +html_theme = "furo" +html_title = "Road2 documentation" + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' +html_search_language = "en" + +# -- EXTENSIONS -------------------------------------------------------- +# MyST Parser +myst_enable_extensions = [ + "amsmath", + "colon_fence", + "deflist", + "dollarmath", + "html_image", + "linkify", + "replacements", + "smartquotes", + "substitution", +] + +myst_substitutions = { + "author": author, + "date_update": datetime.now().strftime("%d %B %Y"), + "description": description, + "repo_branch": "master", + "repo_url": uri_repository, + "title": project, + "version": version, +} + +myst_url_schemes = ["http", "https", "mailto"] \ No newline at end of file diff --git a/documentation_en/configuration/readme.md b/documentation_en/configuration/readme.md new file mode 100644 index 00000000..05192336 --- /dev/null +++ b/documentation_en/configuration/readme.md @@ -0,0 +1,70 @@ +# Road2 Setup + +## Overview + +To instantiate Road2, it is necessary to provide it with some information. For that, there are different JSON. The files that we are going to fill will depend on the use we want to make of them. We will present a complete use. This will help to understand the other uses. + +A complete use of the possibilities offered by the Road2 project is as follows: we want to instantiate, from the start, an administrator and its service. + +In this case, our entry point will be the *administration.json* file. + +This file will indicate several pieces of information and two elements: +- the location of the service configuration: one *service.json* per associated service. +- a *log4js.json* for the administrator logs + +Each *service.json* will indicate the following: +- a *log4js.json* per service for the logs, +- a *cors.json* per service if you want to specify a CORS policy, +- the *projections* folder, +- the *ressources* files, #TODO +- the *sources* folders. + +## administration.json + +This file indicates some general information related to the administration instance. Its main purpose is the indication of logs and managed services. + +You can find an [example](../../docker/config/road2.json) of this file and the [model](./administration/admin_model.yaml) in YAML format. + +## service.json + +This file indicates some general information related to the instance of a service. Its main purpose is the indication of logs, sources and server resources. However, it allows you to specify much more information, such as the operations or projections available on the instance. + +You can find an [example](../../docker/config/service.json) of this file and the [model](./services/service_model.yaml) in YAML format. + +## log4js.json + +This file is used to specify the level of the logs, the location of the files and the format of their content. It does not strictly follow the JSON syntax used to configure [log4js](https://log4js-node.github.io/log4js-node/). +The format is that of a JSON which contains two objects `mainConf` and `httpConf`. Both of these must be present. + +The content of `mainConf` is a log4js configuration object. The content of `httpConf` is a `level` attribute containing the levels offered by log4js and a `format` attribute containing the syntax available for log4js. Both of these attributes must be present. + +You can find an [example](../../docker/config/log4js-service.json) of this file in JSON format. This is the one used in docker images. + +## cors.json + +This file allows you to specify the configuration that you want to apply to the application in terms of CORS. Its content is related to the [CORS](https://www.npmjs.com/package/cors#configuration-options) module configuration of NodeJS. +You can find an [example](../../docker/config/cors.json) of this file in JSON format. This is the one used in docker images. + +## The projections + +The *service.json* file indicates a projections folder. This folder can contain multiple JSON files. These files will be read, regardless of their extension, to obtain the necessary information allowing [PROJ4](http://proj4js.org/) to perform reprojections. + +You can find an [example](../../docker/config/projections/world.json) of this file and the [model](./projections/projection_model.yaml) in YAML format. + +## The sources + +In *service.json* type files, it is possible to specify several *sources* folders. Each folder will be read and the `*.source` files will be analyzed by Road2. Each of these files represents a source for Road2. + +We can find in {{ '[documentation/configuration/sources]({}/tree/{}/documentation/configuration/sources)'.format(repo_url, repo_branch) }} an example of this kind of file for each type of source available in the Road2 code. + +## The resources + +In the *service.json* files, it can also point to several *resources* folders. Each folder will be read and the `*.resource` files will be analyzed by Road2. Each of these files represents this time a resource for Road2. + +You can find, in this {{ '[documentation/configuration/resources]({}/tree/{}/documentation/configuration/resources)'.format(repo_url, repo_branch) }}, an example of this kind of file for each type of resource available in the Road2 code. Each type follows the same YAML model. + +## Files related to some Road2 engines + +### PGRouting: Configuring a database + +In order to read the data in a database, it is necessary to provide Road2 with a file that gives it the login credentials to the database. This is possible through a json file. An example of this file is provided [here](./pgrouting/configuration_bdd.json). The content of this file corresponds to the options of the NodeJS module `pg`. \ No newline at end of file diff --git a/documentation_en/data/readme.md b/documentation_en/data/readme.md new file mode 100644 index 00000000..dcebb3e2 --- /dev/null +++ b/documentation_en/data/readme.md @@ -0,0 +1,56 @@ +# Data for Road2 + +## Navigable data + +### Topology + +In order for geographical data to be usable by a route calculation engine, it is necessary for it to be navigable. This means that these data represent an obligatorily connected, and potentially multiple, directed graph with loops. In other words, the data represents a set of nodes connected to each other by arcs, with the following properties: +- The graph is necessarily connected. The nodes are connected to each other by arcs so that each node is accessible from any other node in the graph. THIS graph can of course be divided into connected sub-graphs. But some routes will not exist. +- Generally, the graph is oriented. Arcs make sense. If this is not the case, each arc will be doubled to have two-way traffic. +- Potentially, the graph is multiple and may contain loops. Several arcs can connect the same two points. An arc can connect a single point and therefore make a loop. + +In what has just been presented, the arcs represent traffic lanes and the nodes are generally only intersections between several lanes. + +### The reality of navigation + +The previous definition allows engines to calculate routes. However, if we are satisfied with this minimum, the result obtained is very likely not to comply with Traffic Laws. To get closer to reality, it is necessary to complete this topology with information present on the road and that the engines can take into account. The quality and completeness of the data thus added will lead to realistic routes. + +#### Traffic directions + +This information is essential to arrive at realistic routes. Without it, roundabouts will definitely be taken the wrong way. + +#### Non-communications + +Non-communications are information that specifies the prohibition to take an arc from another according to the direction of navigation. In concrete terms, these are the computer representation of certain signs (eg no left turn). + +This information is also very important to have realistic routes. They are handled well by the engines. + +### Metadata + +You can also add information related to nodes or arcs. For example, a character string can be added to each arc, which indicates the name of the road it represents. These metadata are not necessary for the calculations, but they are useful for a service user. + +## Data transformation + +In this part, navigable data is considered acquired. In order to use them with Road2, it will be necessary to convert them into one of the formats readable by the server. + +### OSRM Data + +It is possible to use data in OSRM format. The only constraint is to have data generated with the same version as that of Road2. This version is indicated in the `package.json` at the root of the GIT project. + +### PGRouting Data + +If data in PGRouting format is available, it is possible to use it with Road2. A priori, there are no strong constraints on the version of PGRouting used. The only constraint is at the software level. It is necessary to install the procedures from the [GIT](https://github.com/IGNF/pgrouting-procedures) `pgrouting-procedures` project so that Road2 can query the database that hosts the PGRouting data and functions. + +### Valhalla Data + +Similarly, Road2 can be used with Valhalla data. + +### Other data + +When the data is not in one of the formats managed by Road2, it is still possible to convert them. Everyone can do it as they wish to end up with one of the formats mentioned above. However, if some want it, a [GIT](https://github.com/IGNF/route-graph-generator) project has been set up, `route-graph-generator`, in order to convert the data. + +`route-graph-generator` is a set of python scripts that read data in different formats and convert it to a pivot format within a database. From the pivot database, it is possible to convert the data into any format supported by Road2. Thus, the same input data can allow calculations by all the engines. + +Possible input formats for scripts are database or OSM format. + +In the case of a database, the conversion of data into the pivot format is carried out by SQL scripts. At the moment, there is only one that can convert IGN data. However, it is easy to add new ones to start from a different format and end up in the pivot format. From this pivot format, all the tools already exist to achieve the formats managed by Road2. \ No newline at end of file diff --git a/documentation_en/developers/DCO.md b/documentation_en/developers/DCO.md new file mode 100644 index 00000000..d950df0c --- /dev/null +++ b/documentation_en/developers/DCO.md @@ -0,0 +1,34 @@ +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. \ No newline at end of file diff --git a/documentation_en/developers/concepts.md b/documentation_en/developers/concepts.md new file mode 100644 index 00000000..784abb47 --- /dev/null +++ b/documentation_en/developers/concepts.md @@ -0,0 +1,185 @@ +# Concepts used for Road2 + +This chapter describes several software concepts used in Road2. Most, if not all, are united by their objective: the modularity of the application. + +## Part 1: Modularity of the application + +### Independence between APIs and engines + +This is the basic concept for understanding Road2's code. + +#### API concepts + +**An API, for Road2, is a set of routes that the server recognizes and groups within the same name**. For each name, there will potentially be several versions. And within each version there will potentially be multiple routes. + +For example, if we consider an API called `rest` which only has one version `1.0.0`. In this API, one could define a single `compute` route which allows to request a route with minimal `start` and `end` parameters. We will then talk about the `rest/1.0.0` API which allows a user to obtain a route by making the request `/rest/1.0.0/compute?start=2,48&end=2,48.1`. + +Each API is defined in a separate folder from the others. This makes them independent of each other. And for the same name, there is independence between two different versions. Implementation examples can be found in the {{ '[code apis]({}/tree/{}/src/js/apis)'.format(repo_url, repo_branch) }} folder. + +#### Engine concept + +**An engine, for Road2, is a software component that can perform various calculations**. This component can be a library, another web service, a database, etc... + +For example, OSRM is an engine which is written in C++ and which offers a wrapper for use with NodeJS, and this via a NodeJS module. So it's a simple dependency in the `package.json`. + +Incidentally, it seems useful to specify here that each engine is independent of the others through its implementation in the project code (see the notion of source below). + +#### Concept of service + +Road2 was coded to facilitate the management of APIs and engines. To achieve this goal, the API part and the engine part are separated and neither sees what the other is doing. + +An API will therefore have to create a generic request object which will be sent to a *service*. This *service* will send the request to the engine concerned. The engine will therefore receive this object, perform a calculation, and create a generic response object which will then be returned to the API. The API can then format it if necessary for the user. **The *service* can be considered as a proxy between APIs and engines**. + +This makes it possible to add or remove an API without such a modification impacting the engines. And vice versa. + +### Link between resources and sources + +It is the second most important concept after the independence of APIs and engines. It is necessary to understand it to develop on the project. + +#### Notion of graph + +It seems useful to discuss the notion of *graph*, according to Road2, to explain the following. When we do route calculation, we use an engine that reads a *graph* to generate the route. However, **a *graph* is a topology, ie a set of nodes and arcs that form a navigable whole, on which there is at least one cost**. + +Indeed, each arc is associated with at least one cost. This cost can be the distance to the arc or the time it takes to drive across it. Thus, each cost can be seen as the couple *profile/optimization*, where *profile* is the means of transport (ex. car) and *optimization* is the type of travel that one wishes (ex. "faster "). + +Some graphs can have multiple costs per topology (eg PGRouting, Valhalla) and others not (eg OSRM). But when calculating a route, only one cost is used. + +#### Notion of source + +As specified just above, to have a route, it is necessary to use an engine that uses a graph. The *source*, in the conceptual language of Road2, is the origin of the calculation. **The source contains the call to an engine on a specific graph to obtain the result of a calculation**. It is the link between the application and the real calculation, such as that of a route for example. + +Concretely, a *source* gathers two entities: +- a Javascript class which makes the link between the rest of the code and the engine. Each engine will therefore be linked to Road2 by a child class of the `Source` class. This child class must contain the code that allows you to ask the engine for a route or something else (eg isochrone, etc...). This is what concerns the developer. +- each instance of the class, by a configuration which indicates where is the graph that the engine can read, therefore represents an engine for a real graph. We then have the possibility of concretely calculating a route. (C'est ce qui concerne l'administrateur du service d'itinéraire par l'intermédiaire de la configuration)#TODO. For example, an instantiated source will be the call to the OSRM engine on a graph, (ctd a file)#TODO, in osrm format. + + +From all that has just been said, we notice that adding an engine amounts to adding a child class of `Source`. This generates independence between each engine. + +Moreover, in theory, a single source can call upon several engines to return a result. The bottom line is that a source only returns one result for a single query. + +In the end, a source will take into account an instance of `Request`, do the calculation and return an instance of `Response`. This allows the source to remain independent from the rest of the code. + +When calculating a route, you need at least a topology and the costs associated with this topology. A cost corresponds to a single travel mode and a single optimization (eg the Car/shorter couple). + +It turns out that an OSRM graph contains only one cost per folder. It therefore makes it possible to calculate routes only on a single mode of travel and a single optimization. On the other hand, PgRouting offers as many cost columns as you want on the same topology. We find the same grouping of couples on a topology in Valhalla. + +#### Notion of resource + +However, for the user and for the service administrator, we have created the notion of *resource*. **A resource will be defined as a set of sources**. It is the resource that makes the link between a request and the right source to answer it. + +Originally, the objective was to be able to combine several sources from the same data but with a different cost calculation, and therefore to give the *user* a simplified view of the technical constraints. The resource therefore being the *link* between the technical view and the user view. **Another way to view the resource is to view it as a graph that has multiple costs on each arc**. + +This is useful for OSRM for example. In this case, care must therefore be taken when generating the data. When a resource is made, it is then imperative to use the same topology for several different cost calculations. + +But since the beginning of the project, we have extended the possibilities by allowing to associate sources which do not have the same topology. Thus, today, a resource is only a collection of sources. There may only be one. This is often the case for PGRouting. + +For the *administrator*, an instance of Road2 must be able to manage several resources. A resource will notably be configurable by a file. The server will read all the files contained in a folder indicated by the general configuration. + +It should be noted that all this can also have an impact on constraints, such as filters. This is because the constraints are applied at the resource level and not at a source. This is a choice that simplifies the configuration. + +Finally, note that Road2 is coded to make it easy to add new types of resources and sources independently. It is therefore possible to create different types of source and associate them within various types of resources. + +### The operations + +An operation is a calculation that we want to perform. A route calculation, an isochrone calculation, a distance calculation are examples of expected operations. However, a given engine cannot necessarily perform all these operations. One may be able to do routes and distance but not isochrones. It is therefore necessary to know what an engine can do. + +Moreover, a given operation can be more or less resource-intensive. We will therefore potentially want to finely manage the authorizations for operations on the service or a resource. + +Road2 therefore integrates the notion of operation to manage these different issues. + +#### The settings + +Each operation has parameters to perform a calculation. Most parameters can be grouped into categories. For example, a parameter could be a keyword from a list or a point representing coordinates. + +Within these categories, checking the validity of a parameter will follow the same principle. For example, for a point, we will always check if it is included in a bounding box. For a keyword, we will check that it is indeed part of a predefined list. + +In order to (pool)#TODO the code, classes of parameters have been created. And they can be used anywhere in the code. An example of using these classes can be found in the `simple/1.0.0` API. + +### Interface: requests and responses + +Now it is possible to talk in more detail about the interface between a given API and an engine. As specified above, the engine does not know the APIs and the APIs do not know the engines. Thus, to communicate, there is an interface which boils down to two classes of Javascript objects: `Request` and `Response`. + +#### The Request Object + +The `Request` class is considered a parent class. From it, you can create as many child classes as you want. Each instance of a `request` child class is a generic request that will be passed to an engine. The engine will therefore not know which API queried it, but it will have all the useful information to perform the requested calculation. + +#### The Response object + +When an engine has finished its calculation, it creates an object of its own. But to be understood by an API, it must create a `response` object, child class of `Response`, which represents a generic response that each API can understand. The API therefore does not know which engine made the calculation but it has all the useful information to respond to the user according to the expected format. + +### Constraints + +Road2 has developed the concept of constraint to allow more complex route calculations. A constraint is a condition that we give to Road2 and that it translates to the different engines that support these conditions. + +For example, a classic condition found in all engines may be the prohibition to use highways. + +These conditions have been generalized. In addition to being able to prohibit, one can prefer or avoid certain types of roads. And it is not limited to road types, we can define the roads concerned the condition in several ways. This can be related to its width, or to any information present in the database. + +## Part 2: General operation of the Road2 app + +This part describes the application of these concepts in the code during a classic execution. + +### When launching the application + +The Road2 project offers two web servers, a service and an administrator. It therefore has two entry points depending on the use you want to make of it. You can run just the service and it will work just fine. And we can also launch an administrator only. This will launch a service when asked to do so. Finally, you can launch both at once. + +#### Admin launch + +The first possible entry point is the `src/js/road2.js` file. This file will generate an instance of the `Administrator` class. + +This administrator allows several things: +- It can be launched only to check the correct configuration of the administrator and the associated services. In this case, the process stops after the check and returns an error code to determine if there was a problem and its type. +- It can be launched in server mode to administer one or more services via an HTTP(S) API. In this case, the administrator will launch all the services already configured. It will also be possible to create others later. +- You can create an administrator without configuring a service. It will be possible to configure them later. + +An administrator was created to perform tasks that would have interfered with the proper execution of the service. + +The administrator was therefore created to be independent of the service. In their writing, they were thought to be launched into different processes. Thus, if the administrator has tedious tasks, it does not impact the service. If one falls, the other does not. + +However, it is possible to start a service in the same process as its administrator. + +#### Launching a service + +The historical entry point is the `src/js/service/main.js` file. This file will generate an instance of the `Service` class. + +This service is the object used to manage the resources offered by the current instance. It therefore contains a resource catalog and a resource manager. + +Each resource contains multiple sources. Since several resources can point to common sources, the service contains a catalog of unique sources and a manager of these sources. + +When the application is launched, we start by reading the configuration of the application to be able to instantiate the logger. + +Once the logger is loaded, the configuration is completely checked. It is possible to configure a service with empty sources and resources folders. They can be filled in later. However, these folders must be specified when configuring the service. + +After that, we load the resources and sources of the service indicated in the configuration if there are any. This is when files are read, stored in RAM if necessary, and database connections are made. + +Finally, we end up loading the APIs exposed by the service. This is where ExpressJS creates the Node server(s) and loads the available routes. + +#### Focus on configuration verification + +Whether it is an administrator or a service, the configuration will be checked. + +This usually goes through managers. + +##### Managers + +Most classes have a manager. This manager allows as specified just before to check the configurations. But it also allows you to create instances of the classes concerned. Finally, it also keeps track of the different instances and therefore allows them to be managed. + +Managers are designed to be used in the following way: you create it without configuration. On the other hand, it can have other managers in parameter. +Once created, this manager can be used to verify a configuration. We can give it the configuration of an object or we can sometimes give it a set of configuration. In this second case, there will generally be consistency to be checked between each configuration. +Then, we can load objects from their configuration. It is important to check before loading. Because the loading assumes the validity of the configuration. Which is known by a check. In the same way, it is sometimes possible to load several objects with a single call to the manager. The fact of loading only once a configuration present in various places will be managed in the manager. + +To work well, the manager will therefore have two lists. A rather short-lived list that will keep track of already verified configurations. It will be used to check the consistency of all the configurations. This list will have to be emptied when the verifications are finished. +The second list will be a list of already loaded configurations. This list is persistent and indicates the state of the manager. It is used to ensure that each configuration is loaded only once even if it is requested several times. +Also, when you want to modify the configuration during the life of the application, it is this list that will be considered first to check consistency. The first list will only be reused if it is a supposedly consistent set that is checked. + +### When receiving a request + +When a request arrives, it is processed by the ExpressJS router of the called API. It is possible to do the processing you want within this router. These treatments may have no relation to the rest of the application. It is an express router in the basic framework sense. + +We can assume that the objective will be to make a route calculation. Road2 thus integrates several classes and several functions which make it possible to achieve this objective without touching the engines. + +If there are pre-processings to be performed before launching a calculation, it will be preferable to define them in the `index.js` file which contains the definition of the router or in other files but which will be in the the API folder: `${apiName}/${apiVersion}`. The same operation will be preferred for the post-processings. This will keep the code modular. + +Once the potential pre-processing has been done, it is necessary to create a `request` object to send it to the application service via the `service.computeRequest()` function. This function will launch the calculation and create a `response` object that the API can then rewrite to respond to the client. + +NB: When processing a `req` request from ExpressJS, it will be possible to access the instance of the `Service` class which contains a lot of useful information. This will be possible by the `req.app.get("service")` method which returns the instance of the service. \ No newline at end of file diff --git a/documentation_en/developers/conduct.md b/documentation_en/developers/conduct.md new file mode 100644 index 00000000..8bd2c1c7 --- /dev/null +++ b/documentation_en/developers/conduct.md @@ -0,0 +1,2 @@ +```{include} ../../CODE_OF_CONDUCT.md +``` \ No newline at end of file diff --git a/documentation_en/developers/contributing.md b/documentation_en/developers/contributing.md new file mode 100644 index 00000000..e75758cf --- /dev/null +++ b/documentation_en/developers/contributing.md @@ -0,0 +1,2 @@ +```{include} ../../CONTRIBUTING.md +``` \ No newline at end of file diff --git a/documentation_en/developers/documentation.md b/documentation_en/developers/documentation.md new file mode 100644 index 00000000..f2b8ceed --- /dev/null +++ b/documentation_en/developers/documentation.md @@ -0,0 +1,26 @@ +# Documentation + +Sphinx is used to generate documentation from pages written in Markdown (via the [MyST parser](https://myst-parser.readthedocs.io/en/latest/)). + +## Generation of the documentation website + +For the generation: + +```bash +# install additional dependencies +python -m pip install -U -r requirements/documentation.txt +# build it +sphinx-build -b html documentation_en documentation_en/_build +# optimized (quiet, multiprocessing, doctrees separated) +sphinx-build -b html -d documentation_en/_build/cache -j auto -q documentation_en documentation_en/_build/html +``` + +Open `documentation_en/_build/index.html` in a web browser. + +## Write documentation with live rendering + +```bash +sphinx-autobuild -b html -d documentation_en/_build/cache documentation_en/ documentation_en/_build +``` + +Open in a web browser to see updated rendered HTML when a file is saved. \ No newline at end of file diff --git a/documentation_en/developers/functionalities.md b/documentation_en/developers/functionalities.md new file mode 100644 index 00000000..eedfb580 --- /dev/null +++ b/documentation_en/developers/functionalities.md @@ -0,0 +1,188 @@ + +# List of features + +Road2 offers a set of features spread across several groups. + + + +## Function group 1: Interface for calculating routes + +### Define a start point, an end point and intermediate points + +Classic and unavoidable functionalities, they will not be detailed here. Precision: it is possible to specify as many intermediate points as desired. + +### Define Constraints + +The notion of constraint is defined in the [concepts](./concepts.md). Basically, it's about specifying conditions that the route must meet. The best known is certainly the prohibition to take highways. But we can have much more complex conditions and we can apply several of them to the calculation of a route. + +### Specify the resource used + +As specified in the [concepts](./concepts.md), Road2 manages resources. Each request must specify the resources it is querying. + +### Specify the desired profile + +It is possible to specify which means of transport the itinerary concerns. + +### Specify the desired optimization + +It is possible to specify the optimization to be applied during the calculation. + +### Add information on each section + +Depending on the data present in the graphs, it is possible to choose the information to be retrieved in the calculation response. + +### Specify the content of the response + +Via several parameters, it is possible to specify the content of the response: +- The presence or not of the steps of the course. +- The format of the geometries in the response. At the moment, geojson, polyline and wkt are available. +- Whether or not a bbox is present in the response. + +### Choose request and response units + +Using request parameters, it is possible to influence the format of the request itself, and of the response: +- You can define the projection used. +- The format of the durations can be modified. +- The format of the distances is modifiable. + + + + +## Feature group 2: Interface for calculating isochrones and isodistances + +### Define a start or end point + +Inevitable functionality for the calculation of an isochrone. + +### Specify the resource used + +As specified in the [concepts](./concepts.md), Road2 manages resources. Each request must specify the resources it is querying. + +### Specify the type of cost used + +Various costs are possible for the calculation, so it should be specified. + +### Set cost value used + +Inevitable features for such a calculation. + +### Specify the desired profile + +It is possible to specify which means of transport the isochrone concerns. + +### Indicate the direction considered for the calculation result + +An isochrone can be defined in two directions: departure or arrival. It is therefore a question of specifying which one. + +### Define Constraints + +The notion of constraint is defined in the [concepts](./concepts.md). It is limited to prohibitions, such as, for example, the prohibition to take motorways. + +### Specify the content of the response + +Via several parameters, only one for the moment, it is possible to specify the content of the response: +- The format of the geometries in the response. At the moment, geojson, polyline and wkt are available. + +### Choose request and response units + +Using request parameters, it is possible to influence the format of the request itself, and of the response: +- It is possible to define the projection used. +- The format of the durations can be modified. +- The format of the distances is modifiable. + + +## Feature group 3: Provide a web service + +Road2 takes the form of a web server which provides a route calculation service. So it has several features related to that. + +### Use HTTP and HTTPS protocols + +Self-explanatory feature. + +### Configure CORS + +Self-explanatory feature. + +### Have several APIs + +As presented in the [concepts](./concepts.md), Road2 offers the possibility of offering customers different APIs simultaneously. + +### Have multiple engines + +As presented in the [concepts](./concepts.md), Road2 offers the possibility of offering customers different engines simultaneously. + + +## Feature Group 4: Administer Service + +The service can be administered in two ways: configuration and a dedicated API. + +### Configure resources via configuration + +Via the configuration file of the server, it is possible to create [resources](./concepts.md) which will be based on one or more [sources](./concepts.md). + +### Limit certain uses via configuration + +Road2's APIs offer several options, such as the ability to calculate routes with intermediate points. It may be interesting to limit the usage of these options so as not to overload the service. + +### Get server version via API + +Self-explanatory feature. + +### Get server health status via API + +Feature being implemented. The goal is to retrieve the status of each [source](./concepts.md) from each service and report it. + +### Get administrator configuration + +### Get configuration for services managed by an administrator + +### Obtain the configuration of a service via its id + +### Restart a service via its id + +### Obtain the list of projections managed by a service + +### Obtain the presence of a projection via its id + + +## Feature Group 5: OSRM Specific Features + +### Graph optimization for faster responses + +Self-explanatory feature. + +### Route calculation + +Self-explanatory feature. + +### Management of simple constraints or exclusions + +The exclusions are the classic constraints such as the prohibition to use certain types of roads (eg motorways). These are the only constraints available through OSRM. + +### Determine the nearest graph point + +For a given point, OSRM can return the nearest points of the graph. + +## Feature Group 6: PGRouting Specific Features + +### Route calculation + +Self-explanatory feature. + +### Isochrone calculation + +Self-explanatory feature. + +### Management of complex constraints + +PGRouting manages all types of constraints, from the simplest to the most complex. We can therefore prohibit access to motorways or prefer roads that have a width greater than 5 meters. + + +## Feature Group 7: Simple API/1.0.0 Specific Features + +### Getting a GetCapabilities + +The GetCapabilities is a JSON response that describes the parameters for each operation and the values available for an instance of Road2. + +An example can be found in {{ '[documentation]({}/tree/{}/documentation/apis/simple/1.0.0/)'.format(repo_url, repo_branch) }}. + diff --git a/documentation_en/developers/history.md b/documentation_en/developers/history.md new file mode 100644 index 00000000..2d8f6a0e --- /dev/null +++ b/documentation_en/developers/history.md @@ -0,0 +1,2 @@ +```{include} ../../changelog.md +``` diff --git a/documentation_en/developers/modification.md b/documentation_en/developers/modification.md new file mode 100644 index 00000000..993ec71d --- /dev/null +++ b/documentation_en/developers/modification.md @@ -0,0 +1,108 @@ +# Code changes in practice + +It is strongly advised to read the chapter dealing with [concepts](./concepts.md) before reading this one. + +## General principle + +The main principle for Road2 is modularity. This principle can be seen as an objective to be achieved during developments. + +## Procedures for certain types of modifications + +### Engines + +Adding an engine is relatively simple. As stated in [concepts](./concepts.md), an engine is the equivalent of a `source` in Road2. We will therefore refer to the [source](#Source) part to see the possible modifications to the engines. + +### API + +It is possible to add, remove and modify an API. All APIs are defined in the `src/js/apis` folder. This folder follows the following tree `${apiName}/${apiVersion}/index.js`. The `index.js` file contains the API definition. This file corresponds to the definition of an expressJS [router](https://expressjs.com/fr/4x/api.html#router). + +All APIs are loaded by `src/js/apis/apisManager.js`. This file is used to read the APIs folder and their inclusion in the application. + +It is sometimes useful to perform processing when the application is loaded. For example, we want to generate a getCapabilities. We may also want to update it during the life of the application. An ExpressJS router does not store objects, nor perform processing before setting up the server, nor during the life of the application. +To manage such issues, it is possible to create the `init.js` and `update.js` files which will be in the API folder. These files will need to be NodeJS modules that export a `run(app, uid)` function. It is this function that will be called when initializing the application and during the necessary updates. The `app` parameter is the instance of ExpressJS that is used to store references to objects. And the `uid` parameter is an identifier specific to each API which makes it possible to store objects with a low risk of losing it by being overwritten by another. + +#### Modify an existing API + +Just modify the files contained in the `${apiName}/${apiVersion}` folder. + +#### Add an API + +Just create the `${apiName}/${apiVersion}/index.js` tree in the `src/js/apis` folder and add the `init.js` and `update.js` files. + +#### Delete an API + +To delete an API, simply delete the folder that contains its definition. + +### Resource + +The `src/js/resources` folder contains the definition of usable resources and a resource manager that allows the application to manage all of these resources. + +Each resource is a class that derives from the parent `resource.js` class. This class defines a resource as the set of an instance-unique id and a type. + +Each resource can then contain as much additional information as desired. But above all, it makes it possible to make the link with a source, or several, during a request. Each resource must implement the `getSourceIdFromRequest()` function. + +#### Add Resource + +To add a resource, all you have to do is add a file to the `src/js/resources` folder. This file will be the definition of a child class of `resource.js`. + +For this resource to be taken into account in the application, it is enough to modify the resource manager `src/js/resources/resourceManager.js`. This file lets the application know that this new resource is available. It will be enough to copy and paste some parts of the code and adapt them. + +#### Delete a resource + +To delete a resource, simply delete the file that contains its definition and the parts of the code that concerns it in the resource manager. + +### Source + +The `src/js/sources` folder contains a source manager that allows the application to manage all of these sources, and the definition of the sources that can be used. + +Each source is a class that derives from the parent `source.js` class. This class defines a source as the set of an instance-unique id, type, and connection state. + +Each source can then contain as much additional information as desired. But above all, it makes it possible to make the link with one engine, or several, during a request. Each resource must implement the `connect()`, `disconnect()` and `computeRequest()` functions. + +#### Add source + +To add a source, all you have to do is add a file in the `src/js/sources` folder. This file will be the definition of a child class of `source.js`. + +For this source to be taken into account in the application, you just have to modify the sources manager `src/js/sources/sourceManager.js`. This file lets the application know that this new source is available. It will be enough to copy and paste some parts of the code and adapt them. + +Then, you must create or modify a resource so that it uses this new source. + +#### Delete source + +To delete a source, all you have to do is delete the file that contains its definition and the parts of the code that concerns it in the source manager and the resources that use it. + +### Request + +The `src/js/requests` folder contains the definition of the `Request` classes and all those derived from them. When a request arrives, an API must use one of these child classes to query an engine through the service. For example, there is already a child class for calculating routes: `routeRequest`. + +Each child class contains useful information so that the engines can process the request. This information is necessary for some and optional for others. However, for some reason, we sometimes want to add a new child class. For example, to process a new transaction or to otherwise process an existing transaction. This will avoid modifying an existing class and all the impacts it may have on engine management. + +#### Modify a Request + +A `Request` is a central element in Road2 because it makes the link between an API and an engine. For this reason, modifying such a class will have impacts on the APIs and sources that use it. + +#### Delete a Request + +All you have to do is delete the class concerned and its uses in the APIs and sources concerned. + +#### Add a Request + +All you have to do is create a child class of `Request` and implement its use in one or more APIs and sources. + +### Operation + +An operation is defined by an id and parameters. A parameter is defined by an id and other attributes. All of this is defined via JSON configuration files. These documents should be placed in two folders: one for operations and one for settings. Currently they are in `src/resources/`. These folders are specified in the application configuration file. + +The `src/js/operations` and `src/js/parameters` folders contain the code needed to manage operations and parameters. + +There is a distinction between service operations and resource operations. Service operations are the operations permitted on the service. They are described by the JSON of `src/resources/`. Resource operations are the variation of these operations with parameters specific to each resource. They are described in the resource file. +For example, we can declare a service operation that we will name `route`. For the service, this operation exists, is available, and is described via JSON files. This operation may require a `start` parameter. At this level, we know that the operation is available and that the parameter exists and is mandatory. But we do not know what values it can take. It depends on the resource. Each resource can have a different bounding box. + +#### Add/modify/delete an operation + +It is enough to work on the JSON files which describe the operations. + +#### Add/edit/delete a parameter type + +It is enough to work on the child classes of `resourceParameter` and the `parameterManager`. + diff --git a/documentation_en/developers/readme.md b/documentation_en/developers/readme.md new file mode 100644 index 00000000..d27806d8 --- /dev/null +++ b/documentation_en/developers/readme.md @@ -0,0 +1,42 @@ +# Developer documentation for Road2 + +## Presentation of Road2 + +From a developer point of view, *Road2 is a web service written in Javascript and designed to work with NodeJS*. It offers various calculations related to routes. These calculations are made via engines that are not coded in Road2. It can therefore be considered as an interface for route calculation engines. Thus, the calculations are not done in the code but via the call to libraries. + +Moreover, Road2 has been coded to make it easy to add new access APIs or new calculation engines. + +## Useful concepts for developers + +Several concepts have been used during the developments. In order to better understand them and thus better understand the code, a reading of the [concepts](./concepts.md) is advised. + +## Road2 features + +All the functionalities are listed at [part](./functionnalities.md) in order to facilitate the visibility. + +## Participate in development + +Participation in this project is encouraged. Adding engines or APIs, of course. But all other features are welcome. Again, you are asked to make your developments starting from the *develop* branch. + +### Getting started with the project + +We have set up a [documentation](./modification.md) in order to facilitate the handling of the project. + +### GIT + +In order to push developments on the project, they must be provided via `Pull Request` from your branch to the `develop` branch of the project. + +More generally, branch and version management for developments is detailed [here](./version.md). + +## Tools for development + +Until now, several tools have been used to help developments. It was mainly to have an opinion on the quality of the code. For that, we used Sonarqube. + +### Sonarqube + +#### With docker + +It is possible to analyze regularly the code with Sonarqube. We can use the containers proposed by Sonarqube. + +The following command launches a Sonarqube server which will allow to visualize the results: + diff --git a/documentation_en/developers/version.md b/documentation_en/developers/version.md new file mode 100644 index 00000000..910a536a --- /dev/null +++ b/documentation_en/developers/version.md @@ -0,0 +1,93 @@ +# Branch and version management + +This document explains the procedure to follow to keep branches and versions up to date in Road2, and in the various associated projects. + +## Presentation + +By its nature, the Road2 project has a `package.json` that contains a version. However, this project does not work alone. There are projects that complement it by having different roles: +- Route Graph Generator allows to generate data usable by Road2 +- PGRouting Procedures allows to have the procedures used in BDD by Road2 if the PGRouting engine is used. + +Route Graph Generator and PGRouting Procedures are independent as GIT projects. However, they can be pulled in the Road2 project by GIT submodules (`git submodule update --init` at the root of the Road2 project). + +## Branches + +On these three projects have a `master` and `develop` branch. The first allows you to manage the versions put into production. The second makes it possible to carry out the developments. + +We will make sure to start from `develop` and create a branch like this: +- `doc/*` to modify or add documentation only, +- `feat/*` to create new features, +- `fix/*` to make a correction to the source code, +- `docker/*` to modify the docker part only, +- `test/*` to modify only the tests, +- `ci/*` to modify the Github CI + +To merge a branch with `develop`, we will make sure to have done a rebase of develop on this branch. And on the merge method, we will do a squash. So, the `develop` branch will have one commit per feature, fix, etc... + + +## Versions and tags + +It is assumed that versions are managed on the `master` and `develop` branches of the various projects. And it is for these branches that we will explain how to maintain versions and tags. + +### General + +Each project will have, on the `develop` branch, a higher version than the one present on `master`; as well as the mention `-DEVELOP`. + +For example, we will make sure to always have, for each project, a state similar to the following: +- `master` branch: 1.0.0 +- `develop` branch: 1.0.1-DEVELOP + +We will make sure to tag the commits of each project with the right versions. And this on the `master` branch mostly. This is useful for two reasons: +- We must be able to identify, by the tags, the versions of the code used in production. +- We must be able to make all the projects work together from the tags on `master` and `develop`. + +When we merge `develop` on `master`, we will take care not to squash in order to facilitate future merges (as recommended by [github](https://docs.github.com/fr/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#squashing-and-merging-a-long-running-branch)). + +### PGRouting Procedures and Route Graph Generator + +It is advisable to start by managing the versions of these two. *The following describes the process for updating projects, but without going through the GIT* submodules. If you want to use submodules, you can refer to the documentation [proposed by GIT](https://git-scm.com/book/en/v2/Git-Tools-Submodules). + +Initial state for each project: + +- `master` branch: 1.0.0 +- `develop` branch: 1.0.1-DEVELOP + +Steps to follow for each project: + +1. Test `develop` and correct if necessary. +2. Update version on `develop` to 1.0.1. +3. Merge of `develop` on `master`. +4. Update version on `develop` to 1.0.2-DEVELOP. +5. Run tests on `master` and correct if necessary. +6. If there were fixes on `master`, then merge `master` on `develop` and start over at 1. changing the version number. + +### Road2 + +Road2 depends on the other two. This leads to subtleties. + +Initial state for each project: + +- `master` branch: 1.0.0 +- `develop` branch: 1.0.1-DEVELOP + +Procedure for Road2: + +0. Perform version upgrades and merges on Route Graph Generator and PGRouting Procedures. +1. Test `develop` with the `develop` of other projects, and correct if necessary. +2. Update version on `develop` to 1.0.1. +3. Merge of `develop` on `master`. +4. Update version on `develop` to 1.0.2-DEVELOP. +5. Do tests on `master` with the `master` of other projects, and correct if necessary. +6. If there were fixes on `master`, then merge `master` on `develop` and start over at 1. changing the version number. +7. If there were no corrections on `master`, and we have the `master` and the `develop` of the three projects that work together, then tag `master` and `develop` with the versions, on each project. + +### Submodule management + +Currently, Road2 uses PGRouting Procedures and Route Graph Generator to build the various docker images that allow testing and developing the service. The version used in Road2 on its `master` and `develop` branch is *a specific commit* of the `master` of each submodule. + +To point to a more recent commit, we will follow the following procedure: +- go to the `develop` branch of Road2 +- at the root of the project, run the command `git submodule update --remote` +- make the commit of this reference change +- merge `develop` on `master` + diff --git a/documentation_en/docker/demonstration/readme.md b/documentation_en/docker/demonstration/readme.md new file mode 100644 index 00000000..e75b3a0f --- /dev/null +++ b/documentation_en/docker/demonstration/readme.md @@ -0,0 +1,40 @@ +# Local demonstration of Road2 + +This file describes the instructions to follow to have a local demo of Road2 limited to OSRM and PGRouting engines. + +## Principle + +We offer docker images that allow you to test the service locally. We plan to put these images on DockerHub. + +## Using pre-built images available on DockerHub + +## Building images locally + +These are the same images that we may want to build locally. + +To build the image, simply go to the root of the Road2 project and run the following command: +``` +docker build -t road2-demonstration -f docker/demonstration/Dockerfile . +``` + +## Use + +### Data recovery + +Data is required for Road2 to calculate routes. + +### Launching the application + +You can launch the application with the following command: +``` +docker run --rm road2-demonstration +``` + +## Documentation + +### APIs + +It is possible to view API documentation locally. We will run the following command: +``` +docker run --rm -p 8083:8080 -e SWAGGER_JSON=/api.yaml -v {path/to/yaml/directory}/api.yaml:/api.yaml swaggerapi/swagger-ui +``` \ No newline at end of file diff --git a/documentation_en/docker/dev/readme.md b/documentation_en/docker/dev/readme.md new file mode 100644 index 00000000..d2921540 --- /dev/null +++ b/documentation_en/docker/dev/readme.md @@ -0,0 +1,69 @@ +# Docker-Compose to use Road2, Route-Graph-Generator and PGRouting-Procedures + +## Introduction + +Road2 is a route calculation service. To work, it must have access to a volume that contains the data generated by Route-Graph-Generator and to a PGRouting database. + +## Prerequisites + +### General + +To use this `docker-compose.yml`, just: +- Install `docker` and `docker-compose`. +- Retrieve the sources of the tools useful for development. This is done via the GIT submodules: `git submodule update --init` at the root of the Road2 project. +- Place yourself in the `/docker/dev/` folder of the Road2 project. +- Create a `.env` file next to the `docker-compose.yml` which will be an adapted copy of the `compose.env.example` + +### Proxy +If we use these Dockerfiles behind a proxy, it will be necessary to check that docker is already working correctly with the proxy: +- the file `/etc/systemd/system/docker.service.d/http-proxy.conf` is correctly filled in and allows `dockerd` to download images from the internet. +- the `~/.docker/config.json` file is correctly populated and allows the `docker` CLI to provide the proxy to each image launched by the user. + +### DNS +If we use these Dockerfiles with a VPN, we will check that the DNS configurations used by Docker are the correct ones: +- the `/etc/docker/daemon.json` file must be filled in to allow dockerd to specify which DNS to use for each image. We will therefore be careful to fill in the `dns` and `dns-search` attributes. + +### IP +If we use these Dockerfiles on a network with which there may be IP problems, it will be useful to dedicate a range of unused IPs to Docker: +- The ``bip` attribute of the ``/etc/docker/daemon.json` file allows you to specify an IP range. +- If bip has been filled in, we will ensure that this IP range is added to the `docker0` interface. The `sudo ip route add {ip_range} dev docker0` command allows you to do this. +- We may also need to add a different IP range to use this compose: `sudo ip route add {plage_ip_env} dev br-{id_du_network} proto kernel scope link src {ip_env_gateway}` where the id is obtained by doing a `docker network ls`. The IP range and its gate are those defined in the `.env`. + +### HTTPS +If you want to test the server in HTTPS, some actions are necessary upstream: +- generate a self-signed certificate to launch the application in HTTPS (eg `openssl req -nodes -new -x509 -keyout server.key -out server.cert`). +- make sure no server is running on port *443*. + +## Building images + +It is possible to use the Dockerfiles of each project to build the images one by one. But it can be done automatically via docker-compose. + +Just run the `docker-compose build` command. + +## Starting Services + +To launch a service, just run the command `docker-compose up $service` with: +- `$service=road2` for Road2. This will also instantiate a PGRouting. +- `$service=pgrouting` for PGRouting. +- `$service=r2gg` for Route-Graph-Generator. This will also instantiate a PGRouting. + +You can use the `-d` option to run in the background. + +### Services start order + +To make the complete pipeline work, you must for the moment launch the services in the following order: +`docker-compose up -d pgrouting` +`docker-compose up r2gg` to generate data +`docker-compose up road2` + +## Variable management + +When building images and then when using services, there are several parameters that can vary. These parameters are indicated in the `docker-compose.yml` file by the `${var}` syntax or by docker secrets. + +### The .env file + +Parameters of the `${var}` type are initialized in the `.env` file which is located next to the `docker-compose.yml`. This file does not exist. It must be created by copying and adapting the file `compose.env.example`. the `.env` is ignored by git. + +### The secrets + +Secrets are used to transfer sensitive data. In our case, they are useful for connecting to the database which will generate a graph. diff --git a/documentation_en/docker/distributions/readme.md b/documentation_en/docker/distributions/readme.md new file mode 100644 index 00000000..856339da --- /dev/null +++ b/documentation_en/docker/distributions/readme.md @@ -0,0 +1,58 @@ +# Dockerfile to use Road2 on Debian + + +## Building the image + +To build the image, just run the following command at the root of the Road2 project: +``` +docker build -t road2-debian -f docker/debian/Dockerfile . +``` + +## Launch the application + +To launch the application, just use the following command: +``` +docker run --name road2-debian-server --rm -d -p 8080:8080 road2-debian +``` + +### DEBUG mode +``` +docker run --name road2-debian-server --rm -it -p 8080:8080 road2-debian /bin/bash +``` + +## To develop while keeping the source code local +``` +docker run --name road2-debian-server --rm -d -p 8080:8080 -v $src:/home/docker/app/src road2-debian +``` + +## To debug development mode with local sources + +``` +docker run --name road2-debian-server --rm -it -p 8080:8080 -v $src:/home/docker/app/src road2-debian /bin/bash +``` + +## Run the tests + +Unit tests were written with Mocha. To run them, use the following command: + +``` +docker run --name road2-debian-server --rm -v $src:/home/docker/app/src -v $test:/home/docker/app/test road2-debian npm run utest +``` + +## Run eslint + +To linter the code, just run the following command: +``` +docker run --name road2-debian-server --rm -v $src:/home/docker/app/src road2-debian npm run lint +``` + +## Create code documentation via jsdoc + +The code is documented via comments. These comments can be more or less structured with tags. The jsdoc tool makes it possible to generate a website from these comments and these tags. + +To create the documentation, just run the following command: +``` +docker run --name road2-debian-server --rm -v $doc:/home/docker/app/documentation/code road2-debian npm run jsdoc +``` + +The documentation will then be accessible in `$doc`. diff --git a/documentation_en/docker/readme.md b/documentation_en/docker/readme.md new file mode 100644 index 00000000..71f351f9 --- /dev/null +++ b/documentation_en/docker/readme.md @@ -0,0 +1,13 @@ +# Use Road2 with Docker + +This folder gathers the different files allowing to use Road2 with docker. + +There is a sub-folder for the major use cases identified: +- [dev](./dev/readme.md): develop Road2 +- [demonstration](./demonstration/readme.md): obtain a local demonstration of the services offered by Road2. This Dockerfile is limited to OSRM and PGRouting engine testing as there are no Valhalla bindings at this time. +- [test](./test/readme.md): Test Road2 + +Other subfolders are ordered as follows for convenience: +- [web](./web/readme.md): This folder gathers useful files to have a small website which contains several documentations and graphic test pages for Road2. +- {{ '[config]({}/tree/{}/docker/config/)'.format(repo_url, repo_branch) }}: This folder contains several configuration files that are common to the other subfolders. +- [distributions](./distributions/readme.md): This folder can group different `Dockerfile` which are examples of installation under different distributions. Currently there is only one example left for Debian. This is the Dockerfile recommended for development on Road2 because it contains all the binaries useful for the different engines. diff --git a/documentation_en/docker/test/readme.md b/documentation_en/docker/test/readme.md new file mode 100644 index 00000000..39758776 --- /dev/null +++ b/documentation_en/docker/test/readme.md @@ -0,0 +1,52 @@ +# Docker-compose for testing + +## Building and using with docker-compose + +### Prerequisites + +To use `docker-compose`, just: +- install `docker`. +- go to the `/docker/test/` folder of the Road2 project. +- create a `.env` file next to the `docker-compose.yml` which will be an adapted copy of the `compose.env.example` + +### Building images + +It is possible to use the Dockerfiles of each project to build the images one by one. But it can be done automatically via docker-compose. + +Just run the `docker-compose build` command. + +## Run tests + +### Load testing with gatling + +After filling in the `.env` by pointing, for example, to the `user-files` of this {{ '[repository]({}/tree/{}/test/load/gatling/user-files/)' .format(repo_url, repo_branch) }} and having taken care to choose a scenario, just run the command: +``` +# Once the .env has been modified to choose the particular scenario +# Choose the scenario (warning road2Docker does not work until docker-compose up generate-load-data has been called at least once) +docker-compose up load-road2 +``` + +## Generate data for testing + +### Load testing with gatling + +By default, the r2gg docker image allows to generate data for Road2 which comes from OSM data. In this case, this repository already contains queries and gatling scenarios to test Road2 on this data. + +But if r2gg's docker image was used to create a resource pointing to a different location, it will need to generate data for testing. + +To do this, simply modify the bbox of the `.env` and run the following command: +``` +# Once the .env has been modified +docker-compose up generate-load-data +``` + +This command starts the generation of an `ssv` file in a docker volume. This file is then proposed in the gatling scenarios under the name of `dataOsm`. Moreover, this scenario will not work until a data has been generated with this docker-compose. +``` +# After generating via this docker-compose +# Modify the .env to choose the 'dataOsm' scenario +docker-compose up load-road2 +``` + +### Use data and scenarios from the host machine + +If you want to use data and scenarios stored on the host machine, you just have to modify the `.env` to point to another `user-files`. diff --git a/documentation_en/docker/web/readme.md b/documentation_en/docker/web/readme.md new file mode 100644 index 00000000..b9a853b9 --- /dev/null +++ b/documentation_en/docker/web/readme.md @@ -0,0 +1,72 @@ +# Dockerfile to serve web data + +This image allows you to: +- test Road2 on a simple web page containing a map. +- view API and code documentation. + + +## Building the image + +To build the image, just run the following command at the root of the Road2 project: +``` +docker build -t web-road2 -f docker/web/Dockerfile . +``` + +## Launch the web server + +To launch the web server that makes the page accessible, just use the following command: +``` +docker run --name web-road2-page --rm -d -p 8080:80 web-road2 +``` + +## To develop while keeping the source code local +``` +docker run --name web-road2-page --rm -d -p 8080:80 -v $src:/home/docker/web/www/road2 web-road2 +``` + + +## Test Road2 + +We can test Road2 on the following link: http://localhost:8080/road2/ + +# View API documentation + +The API is described via a YAML file which is viewable with Swagger. This file is available via the link http://localhost:8080/api/api.yaml. + +To view or edit it, it is possible to use the dockers offered by Swagger. +``` +# For editing +docker run --rm -d -p 8081:8080 swaggerapi/swagger-editor +# OR +# For visualization +docker run --rm -d -p 8081:8080 swaggerapi/swagger-ui +``` + +Once the swagger docker and the web docker are launched, just go to the following URL: http://localhost:8081/?url=http://localhost:8080/api/api.json. + +It is therefore possible to view and edit the documentation. But for the changes to be taken into account, you have to modify the real file manually in the code repository. + +# View code documentation + +## Create code documentation via jsdoc + +The code is documented via comments. These comments can be more or less structured with tags. The jsdoc tool makes it possible to generate a website from these comments and these tags. + + +To create the documentation, just run the following command: +``` +docker run --rm -v $code:/home/docker/app/documentation/code debian-road2 npm run jsdoc +``` +The documentation will then be accessible in the `$code` folder. + +## View the documentation created + +Once the documentation has been created, it is possible to view it with the image. +``` +docker run --name web-road2-page --rm -d -p 8080:80 -v $code:/home/docker/web/www/documentation/code/web-road2 +``` +The documentation is then visible on the following link: http://localhost:8080/code. + +The documentation will then be accessible in the `$code` folder. + + diff --git a/documentation_en/index.md b/documentation_en/index.md new file mode 100644 index 00000000..f9e182f1 --- /dev/null +++ b/documentation_en/index.md @@ -0,0 +1,76 @@ +# {{ title }} - Documentation + +> **Description:** {{ description }} +> **Authors and contributors:** {{ author }} +> **Version:** {{ version }} +> **Source code:** {{ repo_url }} +> **last update of the documentation:** {{ date_update }} + +## General presentation + +Road2 is a route and isochrone calculation server written in Javascript and designed to work with NodeJS. This server offers the calculation of routes and isochrones via existing engines like [OSRM](https://github.com/Project-OSRM/osrm-backend) or [PGRouting](https://pgrouting.org/ ). Road2 is therefore an interface for calculation engines. These are not done in the Road2 code but via calls to its engines. This can result in a call to a library, or to a database, or to another web service. + +Road2 was designed with the idea of being able to easily add new engines and new APIs, and to do so in a completely transparent way to each other. In other words, adding an engine does not impact existing APIs. The objective is to facilitate the addition of new functionalities while maintaining access to the service. For a longer discussion on the software concepts introduced in Road2, we can refer to the [following documentation](./developers/concepts.md). + +Currently, Road2 offers three engines, OSRM, PGRouting and Valhalla, through a single REST API. + +```{toctree} +--- +caption: Road2 +maxdepth: 1 +numbered: true +--- +Configuration +Data +Production +Changelog +Contributing +Code of conduct +``` + +---- + +```{toctree} +--- +caption: Développement +maxdepth: 1 +numbered: true +--- +Devloppement +Fonctionnalities +Concepts +Modification +Versionning +Documentation +``` + +---- + +```{toctree} +--- +caption: Tests +maxdepth: 1 +numbered: true +--- +Tests +Unit tests +Functionnal tests +Integration tests +Load tests +``` + +---- + +```{toctree} +--- +caption: Images docker +maxdepth: 1 +numbered: true +--- +Presentation +For development +For demonstration +Debian +For tests +Web server +``` diff --git a/documentation_en/production/readme.md b/documentation_en/production/readme.md new file mode 100644 index 00000000..beff3850 --- /dev/null +++ b/documentation_en/production/readme.md @@ -0,0 +1,69 @@ +# Release of Road2 + +Road2 is of course usable in production. This is already the case at IGN. The objective of this document is to provide elements that can help in making certain architecture and scaling choices. Of course, the elements that will be exposed depend on the expected stress and the size of the graphs made available. + + +## Architectural elements + +Road2 has been coded to be exposed directly on the internet. However, it is advisable to consider it as middleware, and therefore to use a classic front, like NGINX. + +Depending on the engine(s) used, it will be necessary to have access to a database. This is the case if one of the engines used is PGRouting. In this case, it is advisable to consider this database as middleware and to put it on a different machine from Road2. In fact, it is what performs the calculation of routes and isochrones and this calculation involves a significant use of CPUs. + +## Scaling Elements + +### CPU + +As it is, Road2 runs on a single thread. An evolution is planned to modify this behavior. However, this part of the code performs little computation. On the other hand, the engines use the CPUs a lot in parallel. This will therefore be the first resource to monitor to establish the scaling. An example of use in production will be given in the [Performances](#Performances) section to illustrate this. + +### RAM + +Road2 doesn't really need RAM. Here too, the need will depend on the motors used. + +OSRM can require a lot of RAM (cf. [OSRM notes](https://github.com/Project-OSRM/osrm-backend/wiki/Disk-and-Memory-Requirements)), but it is not necessary to operate. Also, it may depend on how the graphs are loaded into memory. The NodeJS binding we use does not load the entire graph into memory. A system administrator will certainly know how to optimize the use of RAM according to the data processed and the expected performance. + +PGRouting is a database that has indexes. RAM usage is managed by PostgreSQL according to the parameters provided in the database configuration. We advise asking a database administrator to perform optimizations. + +## Performance + +Performance depends directly on the engine used, and of course on the machine used to host the service. It is the calculation carried out by the engine which takes the most time and gives the order of magnitude of the response time. + +The following information is given as examples. If we consider a graph that covers the whole of France (~25GB for OSRM and 16GB for PGRouting) and two servers with 8 cpu and 32 GB of RAM, one for Road2 (+OSRM bindings) and one for the PGRouting database, we obtain the following performance: +- route via OSRM < 100 ms +- route via PGR < 2000 ms. Of course, the results are highly variable. For example, if we consider a small route, we will have performances < 1000 ms without problem. +- isochronous via PGR have results that are too variable to be averaged: less than one second for small isochronous (<30min) and several seconds for larger ones. Knowing that the response time does not follow linearly the increase in the duration of the isochrone but rather it seems to have an exponential evolution. + +## Industrialization + +This part covers some useful topics for the industrialization of Road2. + +### Installation of dependencies specific to each engine + +By default, the `npm install` command will attempt to install all dependencies. However, some of them are useless if a motor is not used. We can therefore proceed as follows: +``` +# Install strictly necessary dependencies +npm install --no-optional --no-package-lock --no-save +# Then, if we use a single engine, like OSRM for example +npm install --no-package-lock --no-save osrm +``` + +### Pack + +It is possible to make an archive of Road2 via the classic `npm pack` command launched at the root of the GIT project. The package will only contain the `src` folder and the `package.json`. If the `node_modules` are already present, then they are added to the archive. + +## Other Items + +### Error display + +By default, if Road2 encounters an error, it will return the content of that error to the client. This is a suitable behavior during developments. But in production it is better to return a generic error. To do this, simply launch Road2 with the variable `NODE_ENV` at `production`. + +### CORS management + +By default, an API will not handle CORS. Each developer must specify if they want to use CORS within the API they are developing. Thus, it is possible to determine on which route one wishes to use which CORS. For example, we can authorize all origins on certain calculation routes and restrict them on administration routes. + +To apply CORS, we use the `cors` module which integrates well with expressJS. + +By default there are options that are used but they can be overridden. If you want to overload the options, you will make sure to add them in a configuration file independent of the rest of the application configuration, as specified in the paragraph dealing with adding an API. + +### HTTPS management + +Road2 can be queried directly over HTTPS. For this, it uses the `https` module of NodeJS. It is therefore possible to provide it with the [options](https://nodejs.org/docs/latest-v12.x/api/tls.html#tls_tls_createserver_options_secureconnectionlistener) available in this module. diff --git a/documentation_en/test/functional/readme.md b/documentation_en/test/functional/readme.md new file mode 100644 index 00000000..48f0c578 --- /dev/null +++ b/documentation_en/test/functional/readme.md @@ -0,0 +1,24 @@ +# Functional tests of Road2 + +Cucumber will be used to test the APIs of Road2, and this, in their functional aspects. It will also be used to test the Road2 configuration. + +## Using Cucumber + +There are several cucumber features to perform functional testing. There are features to test the requests that can be sent to the server. And there are features to test the different configurations that can be provided to the server in order to distribute the services. + +### Request + +The `request/cucumber/features/req*.feature` features are used to test features accessible via requests. To work, it is necessary to have generated data for each engine on the Ile-de-France. + +In order to launch these tests, we will follow the following procedure: +- generate data for each engine in Ile-de-France +- launch the Road2 server via docker-compose +- run `npm run rtest` command via docker-compose. + +### Setup + +The features `configuration/cucumber/features/conf*.feature` allow to test the functionalities related to the loading of a Road2 configuration. + +In order to launch these tests, we will follow the following procedure: +- launch the Road2 server via docker-compose +- run `npm run ctest` command via docker-compose. diff --git a/documentation_en/test/integration/readme.md b/documentation_en/test/integration/readme.md new file mode 100644 index 00000000..7e85ac24 --- /dev/null +++ b/documentation_en/test/integration/readme.md @@ -0,0 +1,181 @@ +# Description of integration tests + +## Class tests + +To run the integration tests, it is advisable to use docker-compose in order to have a more complete test environment: +``` +docker-compose up -d road2 +docker-compose exec road2 npm run itest +``` + + +The bottom-up approach was chosen for these tests. We will test the classes that depend on another to function. We will therefore test the following classes in the order indicated: + +- First level: + - apisManager (ExpressJS, api, log4js) + - baseManager (base, log4js) + - looseConstraint (constraint) + - line (geometry, proj4, polyline) + - point (geometry, proj4) + - polygon (geoemtry, turf, proj4, polyline) + - operation (parameter) + - resourceParameter (parameter) + - serverManager (server, ExpressJS, log4js, fs, assert) + - healthRequest(request) + - healthResponse (response) + -serviceRequest(request) + +- Second level: + - routeRequest(request, dot) + - isochronousRequest(request, item) + - nearestRequest(request, dot) + - resourceOperation (resourceParameter) + - boolParameter (resourceParameter) + - enumParameter (resourceParameter) + - floatParameter (resourceParameter) + - pointParameter (resourceParameter, point, log4js) + - constraintParameter (resourceParameter, constraint, looseConstraint) + - intParameter (resourceParameter) + - isochroneResponse (response, point, geometry)* + - nearestResponse (response, point, geometry) + - step (line, duration, distance) + - source (baseManager, projectionManager) + +- Third level: + - parameterManager (parameter, boolParameter, enumParameter, floatParameter, pointParameter, constraintParameter, log4js) + - resource (resourceOperation) + - portion (point, step, duration, distance) + +- Fourth level: + - operationManager (parameterManager, operation, resourceOperation, log4js) + - osrmResource(resource, resourceOperation) + - pgrResource (resource, resourceOperation, log4js) + - route (line, portion, duration, distance) + +- Fifth level: + - routeResponse (response, point, route) + +- Sixth level: + - osrmSource (source, osrm, routeResponse, nearestResponse, route, portion, line, point, step, distance, duration, errorManager, log4js) + - pgrSource (source, routeResponse, isochroneResponse, route, portion, line, point, polygon, step, distance, duration, errorManager, gisManager, copyManager, simplify, turf, looseConstraint, log4js) + +- Seventh level: + - sourceManager (osrmSource, pgrSource, errorManager, storageManager, operationManager, log4js) + +- Eighth level: + - resourceManager (osrmResource, pgrResource, sourceManager, operationManager, log4js) + +- Ninth level: + - service (apisManager, resourceManager, sourceManager, operationManager, baseManager, projectionManager, serverManager, errorManager, ExpressJS, log4js) + +- Tenth level: + - serviceManager (service, serviceProcess, log4js) + - serviceProcess (serviceAdministered, service, log4js, fork) + +- Eleventh level: + - administrator (express, log4js, helmet, path, fs, assert, serverManager, serviceManager, apisManager) + +- Others: + - road2.js + - simple api controller.js 1.0.0 + - simple api index.js 1.0.0 + - simple api 1.0.0 init.js + - simple api 1.0.0 update.js + + +## Dependency testing + +### List of dependencies and their uses + +- @mapbox/polyline + - geometry/line.js + - encode() + - toGeoJSON() + - fromGeoJSON() + - geometry/polygon.js + - encode() + - toGeoJSON() + - fromGeoJSON() + +- @turf/turf + - apis/simple/1.0.0/controller/controller.js + - bbox() + - lineSlice() + - geometry/polygon.js + - polygon() + - polygonToLine() + - sources/pgrSource.js + - point() + - truncate() + - lineSlice() + - nearestPointOnLine() + - length() + - cleanCoords() + +- assert + - deepStrictEqual() + - equal() + - deepEqual() + +- cors + - service/service.js + - () + +- express + - administrator/administrator.js + - () + - use() + - Router() + - router.use() + - json() + - set + +- got + - utils/httpQuery.js + - () + - post() + +- helmet + - administrator/administrator.js + - () + - service/service.js + - () + +- https-proxy-agent + - utils/httpQuery.js + - () + +- log4js + - configure() + - getLogger() + +- nconf + - road2.js + - use() + - get() + - argv().get() + - argv().env() + +- proj4 + - geography/projectionManager.js + - defs() + - () + +- osrm + - sources/sourceManager.js + - sources/osrmSource.js + - route() + - nearest() + +- pg {Pool} + - base/base.js + - Pool() + - pool.on('error') + - pool.end() + - () + - connect() + - end() + - base/baseManager.js + - sources/sourcesManager.js + - sources/pgrSource.js + - query() \ No newline at end of file diff --git a/documentation_en/test/load/readme.md b/documentation_en/test/load/readme.md new file mode 100644 index 00000000..68405dd7 --- /dev/null +++ b/documentation_en/test/load/readme.md @@ -0,0 +1,42 @@ +# Load testing + +This folder contains scripts useful for load testing. These tests are performed with Gatling. + +## Gatling + +The `gatling` folder contains the `user-files` folder needed by Getling to perform the tests. We therefore find the definition of the simulations and the necessary resources. As it is, it is possible to launch the scenario contained in `gatling/user-files/simulations/road2.scala` which uses the resource `gatling/user-files/resources/road2_parameters.ssv`. + +If Gatling is installed on the machine, we can point to the `user-files` folder. For more information, see the [official] site (https://gatling.io/). + +Otherwise, it is possible to use the docker image available on [dockerhub](https://hub.docker.com/r/denvazh/gatling). + +This is done in the {{ '[docker-compose]({}/tree/{}/docker/test/)'.format(repo_url, repo_branch) }} dedicated to testing in this repository. See the [readme](../../docker/test/readme.md) for its usage. + +## random-route-generator + +This is an R script that generates ssvs for load testing. Just run in the following way: +`R -f routeGenerator.R --args "/home/user/out.ssv" 100 "bduni" 8 41 9 42` + +The `-f` option specifies the script to run. Each element following `--args` is a script option. These must be in the correct order: +- ssv output file +- number of lines +- Road2 resource tested +-xmin +-ymin +-xmax +-ymax + +## random-iso-generator + +This is an R script that allows you to generate ssvs for load tests on the isochrone calculation. Just run in the following way: +`R -f isoGenerator.R --args "/home/user/out.ssv" 100 "bduni" 8 41 9 42` + +The `-f` option specifies the script to run. Each element following `--args` is a script option. These must be in the correct order: +- ssv output file +- number of lines +- Road2 resource tested +-xmin +-ymin +-xmax +-ymax + diff --git a/documentation_en/test/readme.md b/documentation_en/test/readme.md new file mode 100644 index 00000000..34453908 --- /dev/null +++ b/documentation_en/test/readme.md @@ -0,0 +1,87 @@ +# Road2 reviews + +This file describes all the tests that can be performed on Road2. + +## Test procedures to validate a new version of Road2 + +When a new development has been made, it is advisable to validate them in the following way: + + +### Setting up the environment + +This is to remove the docker images to be sure to start from scratch: +``` +docker-compose down +docker image rm road2 r2gg pgrouting +docker network rm iti-data-network +docker volume rm iti-data-volume pgr-data-volume +``` +For the reconstruction of the images, it is advisable to build them one by one: + +# In the /docker/dev folder +``` +docker-compose build road2 +docker-compose build r2gg +docker-compose build pgrouting +``` + +The road2 image contains data but these are not sufficient to validate the entire application. We will therefore reconstruct data: +``` +docker-compose up r2gg # with the .env filled in correctly: once for osrm and once for pgr +``` + +### Unit test validation + +Unit tests are described [here](./unit/readme.md). + +To summarize, just run the following command, having taken care to start Road2 via docker-compose: + +The road2 image contains data but these are not sufficient to validate the entire application. We will therefore reconstruct data: +``` +docker-compose up r2gg # with the .env filled in correctly: once for osrm and once for pgr +``` + +### Unit test validation + +Unit tests are described [here](./unit/readme.md). + +To summarize, just run the following command, having taken care to start Road2 via docker-compose: +``` +docker-compose exec road2 npm run utest +``` + +### Validation of integration tests + +Integration tests are described [here](./integration/readme.md). + +To summarize, just run the following command, having taken care to start Road2 via docker-compose: +``` +docker-compose exec road2 npm run itest +``` + +### Validation of functional tests + +Functional tests are described [here](./functional/readme.md). + +To summarize, just run the following commands, having taken care to start Road2 via docker-compose: +``` +docker-compose exec road2 npm run rtest #tests on requests +docker-compose exec road2 npm run artest #tests on admin requests +docker-compose exec road2 npm run crtest # further tests on requests +docker-compose exec road2 npm run drtest #tests that depend on data on requests +docker-compose exec road2 npm run ctest #tests on configuration +docker-compose exec road2 npm run cctest # further tests on the configuration + +``` + +### Performance and load testing + +Load tests are described [here](./load/readme.md). + +To summarize, just run the following command, having taken care to start Road2 via docker-compose: +``` +docker-compose up road2-gatling # with the .env correctly filled in to specify the test you want to do +``` + +### Code Quality + diff --git a/documentation_en/test/unit/readme.md b/documentation_en/test/unit/readme.md new file mode 100644 index 00000000..4da51cc5 --- /dev/null +++ b/documentation_en/test/unit/readme.md @@ -0,0 +1,38 @@ +# Description of unit tests + +To run the unit tests, it is advisable to use docker-compose in order to have a more complete test environment: +``` +docker-compose up -d road2 +docker-compose exec road2 npm run utest +``` + +But this should only work with `mocha`. Run the following command from the root of the project: +``` +mocha --recursive './test/unit/mocha/**/*.js' +``` + + +Unit tests are for classes that do not depend on another class in the project to function. The other classes are tested in the integration tests [here](../integration/readme.md). + +We will therefore find the following classes or files: +- api (ExpressJS, log4js) +- basic (pg, log4js) +- constraint +- distance +- projectionManager (proj4, log4js) +- geometry +- parameter +- request +- response +- server (ExpressJS, log4js) +- topology +- duration +- copyManager +- errorManager +- gisManager +- processManager (log4js) +- simplify.js //TODO +- storageManager (log4js) +- validationManager +- wkt +-serviceAdministered diff --git a/package.json b/package.json index a2dcb62b..6a522a5e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "road2", - "version": "2.2.5", + "version": "2.2.7", "description": "Calcul d'itinéraire", "author": "RDEV - IGN", "main": "src/js/road2.js", diff --git a/readme.md b/readme.md index 64f7964c..1ff3829a 100644 --- a/readme.md +++ b/readme.md @@ -1,12 +1,14 @@ # Road2 +[English](./readme_en.md) + ## 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 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. +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 diff --git a/readme_en.md b/readme_en.md new file mode 100644 index 00000000..f2b0a15c --- /dev/null +++ b/readme_en.md @@ -0,0 +1,98 @@ +# Road2 + +[Français](./readme_fr.md) + +## General presentation + +Road2 is a route and isochrone calculation server written in Javascript and designed to work with NodeJS. This server offers the calculation of routes and isochrones via existing engines like [OSRM](https://github.com/Project-OSRM/osrm-backend) or [PGRouting](https://pgrouting.org/ ). Road2 is therefore an interface for calculation engines. These are not done in Road2's code but via calls to its engines. This can mean calling a library, or a database, or another web service. + +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. + +## Features available + +Road2 offers several sets of features : +- Calculate routes +- Calculate isochrones and isodistances +- Manage the service +- etc... + +These sets include all the functionalities and are detailed [here](./documentation_en/developers/functionalities.md). + +## Licence + +Road2 is released under the GPL v3 license. + +## Service discovery + +### Demonstrator + +IGN offers a demonstrator for [the route](https://geoservices.ign.fr/documentation/services_betas/itineraires.html) and [the isochrone](https://geoservices.ign.fr/documentation/services_betas/isochrones.html). These demonstrators allow you to build queries via a map and visualize the results. + +Otherwise, for a first grip of the service locally, it is possible to use the [alpine](./docker/demonstration/Dockerfile) image of Road2. This will make it possible to have a local instance of the service and a web page allowing it to be tested. Setup instructions are given [here](./documentation_en/docker/demonstration/readme.md). + +### Discover and test the service's APIs + +IGN offers a visualization of the user API for [the itinerary](https://geoservices.ign.fr/documentation/services/api-et-services-ogc/itineraires/api) and [the isochrone](https://geoservices.ign.fr/documentation/services/api-et-services-ogc/isochrone/api). + +IGN also offers pages for testing an instance of the service throughout France, with map visualization. There is a page for the [itinerary](https://geoservices.ign.fr/documentation/services/api-et-services-ogc/itineraires) and a page for the [isochrone](https://geoservices.ign.fr/documentation/services/api-et-services-ogc/isochrones). + +Otherwise, all available APIs are documented in this [folder](./documentation_en/apis/). At the moment there is only one user API which is documented via a [file](./documentation_en/apis/simple/1.0.0/api.json) JSON using openapi 3.0.0, and one documented admin API via another [file](./documentation_en/apis/administration/1.0.0/api.json) JSON following the same formating. + +It is possible to view these API documentations locally by following the instructions that are [here](./documentation_en/docker/demonstration/readme.md). + +## Installing and using Road2 + +### Prerequisites + +To use this project, it is necessary to have NodeJS installed on the machine used. The NodeJS version used during development is *12.14.0*. + +### Installing modules + +The installation of the modules is carried out via NPM. Going to the root of the project: +``` +npm install +``` + +NB: There are optional dependencies to manage those of each engine. For more information, see this [document](./documentation_en/production/readme.md). + +### Data generation + +Whatever the data source, it is necessary to provide it in one of the formats used by Road2. Since Road2 can use several calculation engines, it accepts several data formats: +- OSRM 5.26.0 makes it possible to use OSRM data generated with this version. +- PGRouting 3.1.3 makes it possible to use a database using this version. It will be necessary to add the project procedures [pgrouting-procedures](https://github.com/IGNF/pgrouting-procedures) so that Road2 can communicate with the database. + +This data can therefore be generated from any database, or from OSM files. The [route-graph-generator](https://github.com/IGNF/route-graph-generator) project offers tools to generate graphs from any database or OSM files. (If the database does not correspond to the format of the database expected by route-graph-generator, it will suffice to derive it.)#TODO + + +For a detailed discussion of the expected data, refer to this [documentation](./documentation_en/data/readme.md). + +### Setup + +In order for the server to work, it is necessary to [configure](./documentation_en/configuration/readme.md). This involves creating a tree structure of a few JSON files allowing instantiation of the server with resources. + +### Launch + +Once configured, it is possible to launch an instance of Road2 with the command: +``` +node ${road2}/src/js/road2.js --ROAD2_CONF_FILE=${configuration}/administration.json +``` + +### For more details + +In the [docker/distributions](./docker/distributions) folder, you will find different Dockerfiles that allow you to see the installation and test the service on different platforms. At the moment, Debian 10 is available. + +## Participate in developments + +Contributing to this project is welcome (see our [code of conduct](./CODE_OF_CONDUCT.md) on this subject). We have set up a [guide](./CONTRIBUTING.md) of contributions to help you in this process. + +Developer documentation can be found [here](./documentation_en/developers/readme.md). It indicates the useful concepts to carry out developments on Road2. + +To learn more about our roadmap, you can look at the [IGNF/Road2 Roadmap](https://github.com/orgs/IGNF/projects/3) project. + +Finally, it is possible to use this [docker-compose](./documentation_en/docker/dev/readme.md) to have a development environment including building binaries, modules and generating data. + +## Production use + +In order to use Road2 in production, several informations are given in this [document](./documentation_en/production/readme.md). These are mainly the needs already observed for a production launch of the service covering the whole of French territory. diff --git a/src/js/sources/valhallaSource.js b/src/js/sources/valhallaSource.js index b15adb55..eaeedbe0 100644 --- a/src/js/sources/valhallaSource.js +++ b/src/js/sources/valhallaSource.js @@ -17,6 +17,8 @@ const { exec } = require('child_process'); // Création du LOGGER const log4js = require('log4js'); const LOGGER = log4js.getLogger("VALHALLASOURCE"); +// Récupération de la valeur du maxBuffer en variable d'environment variable ou valorisation par défaut (1MB) +const maxBuffer = process.env.EXEC_MAX_BUFFER_SIZE ? parseInt(process.env.EXEC_MAX_BUFFER_SIZE, 10) : 1024 * 1024; /** * @@ -186,12 +188,13 @@ module.exports = class valhallaSource extends Source { // Permet de grandement se simplifier le parsing !! const optionsString = `"directions_options":{"format":"osrm"}`; const commandString = `valhalla_service ${this._configuration.storage.config} route '{${locationsString},${costingString},${optionsString}}' `; + const options = { maxBuffer: maxBuffer }; LOGGER.info(commandString); return new Promise( (resolve, reject) => { try { - exec(commandString, (err, stdout, stderr) => { + exec(commandString, options, (err, stdout, stderr) => { // Du moment qu'OSRM a répondu, on considère que la source est joignable this.state = "green"; @@ -287,12 +290,13 @@ module.exports = class valhallaSource extends Source { const reverseString = `"reverse":${reverse}`; const polygonsString = `"polygons":true`; const commandString = `valhalla_service ${this._configuration.storage.config} isochrone '{${locationsString},${costingString},${costingOptionsString},${contoursString},${reverseString},${polygonsString}}' `; + const options = { maxBuffer: maxBuffer }; LOGGER.info(commandString); return new Promise( (resolve, reject) => { try { - exec(commandString, (err, stdout, stderr) => { + exec(commandString, options, (err, stdout, stderr) => { // Du moment qu'OSRM a répondu, on considère que la source est joignable this.state = "green";