diff --git a/.github/labeler.yml b/.github/labeler.yml index dd91ab3..6a2014d 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,19 +1,31 @@ ci-cd: - - .github/**/* +- changed-files: + - any-glob-to-any-file: + - .github/** dependencies: - - cmake/**/* +- changed-files: + - any-glob-to-any-file: + - cmake/** documentation: - - docs/**/* +- changed-files: + - any-glob-to-any-file: + - docs/** enhancement: - - src/**/* +- changed-files: + - any-glob-to-any-file: + - src/** quality: - - tests/**/* +- changed-files: + - any-glob-to-any-file: + - tests/** tooling: - - CMakeLists.txt - - config.h.in - - '**/CMakeLists.txt' +- changed-files: + - any-glob-to-any-file: + - CMakeLists.txt + - config.h.in + - '**/CMakeLists.txt' diff --git a/.github/workflows/pr-auto-labeler.yml b/.github/workflows/pr-auto-labeler.yml index cc5272e..ec24fd0 100644 --- a/.github/workflows/pr-auto-labeler.yml +++ b/.github/workflows/pr-auto-labeler.yml @@ -2,14 +2,11 @@ name: "🏷 PR Labeler" on: - pull_request -permissions: - contents: read - pull-requests: write - jobs: - triage: + labeler: + permissions: + contents: read + pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@v5 - with: - repo-token: "${{ secrets.GITHUB_TOKEN }}" + - uses: actions/labeler@v5 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6637ffa..20b27eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,18 @@ -## 6.0.0 +## 6.1.0 + +### [Added] + +* OGC API Tiles : + * ajout des routes de découvertes pour connaître les éventuels styles, TMS et tuiles limites pour les couches + * ajout des routes de lecture des TMS + * ajout d'une route de découverte de ce service + * ajout de la route de conformité aux classes + +### [Changed] + +* Les CRS et TMS additionnels dans le descripteur de couche sont à la racine pour pouvoir être exploités dans plusieurs services + +## 6.0.1 ### [Added] diff --git a/config/layer.schema.json b/config/layer.schema.json index 6988650..153d2a1 100644 --- a/config/layer.schema.json +++ b/config/layer.schema.json @@ -191,12 +191,6 @@ "properties": { "enabled": { "type": "boolean" - }, - "crs": { - "type": "array", - "items": { - "type": "string" - } } } }, @@ -205,12 +199,6 @@ "properties": { "enabled": { "type": "boolean" - }, - "tms": { - "type": "array", - "items": { - "type": "string" - } } } }, @@ -229,6 +217,18 @@ "type": "boolean" } } + }, + "extra_crs": { + "type": "array", + "items": { + "type": "string" + } + }, + "extra_tilematrixsets": { + "type": "array", + "items": { + "type": "string" + } } }, "required": [ diff --git a/config/services.json b/config/services.json index 052d6db..f781d7b 100644 --- a/config/services.json +++ b/config/services.json @@ -98,6 +98,7 @@ "abstract": "Ce service permet la visulation de couches de données raster IGN au travers d'un flux OGC API Tiles", "keywords": [ "OGC API Tiles" - ] + ], + "reprojection": true } } \ No newline at end of file diff --git a/config/services.schema.json b/config/services.schema.json index 462a654..893bc70 100644 --- a/config/services.schema.json +++ b/config/services.schema.json @@ -411,6 +411,11 @@ "metadata": { "$ref": "#/$defs/metadata", "description": "OGC API Tiles service's metadata" + }, + "reprojection": { + "type": "boolean", + "default": false, + "description": "OGC API Tiles reprojection activation" } } } diff --git a/docs/openapi.yaml b/docs/openapi.yaml index e5abdf5..58b5371 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -847,6 +847,51 @@ paths: ######################################### OGC API Tiles + + /tiles: + get: + tags: + - OGC API Tiles + summary: Retourne la page d'accueil du service OGC API Tiles + operationId: tiles_get_landing_page + parameters: + - name: f + required: false + in: query + description: format de sortie demandé + schema: + type: string + enum: ['json'] + responses: + 200: + description: Services assurés par le service OGC API Tiles + content: + application/json: + schema: + $ref: '#/components/schemas/tiles_get_landing_page' + + /tiles/conformance: + get: + tags: + - OGC API Tiles + summary: Retourne la liste des classes de conformité du service OGC API Tiles + operationId: tiles_get_conformance + parameters: + - name: f + required: false + in: query + description: format de sortie demandé + schema: + type: string + enum: ['json'] + responses: + 200: + description: Services assurés par le serveur + content: + application/json: + schema: + $ref: '#/components/schemas/ogc_get_conformance' + /tiles/collections: get: tags: @@ -854,14 +899,13 @@ paths: summary: Récupère la liste des couches disponibles operationId: ogc_get_collections parameters: - - name: f required: false in: query - description: format de sortie demandée + description: format de sortie demandé schema: type: string - enum: ['application/json'] + enum: ['json'] # - name: bbox # required: false @@ -922,14 +966,14 @@ paths: description: couche demandée schema: type: string - + - name: f required: false in: query - description: format de sortie demandée + description: format de sortie demandé schema: type: string - enum: ['application/json'] + enum: ['json'] responses: 200: @@ -951,6 +995,254 @@ paths: schema: $ref: '#/components/schemas/tiles_error' + + /tiles/collections/{layer}/tiles: + get: + tags: + - OGC API Tiles + summary: Récupère les TMS disponibles pour une couche vecteur + operationId: ogc_get_vector_tilesets + parameters: + + - name: layer + required: true + in: path + description: couche demandée + schema: + type: string + + - name: f + required: false + in: query + description: format de sortie demandé + schema: + type: string + enum: ['json'] + + responses: + 200: + description: Couche demandée + content: + application/json: + schema: + $ref: '#/components/schemas/tiles_tilesets' + 400: + description: Format demandé invalide ou la couche demandée correspond à de la donnée raster + content: + application/json: + schema: + $ref: '#/components/schemas/tiles_error' + 404: + description: Couche inconnue + content: + application/json: + schema: + $ref: '#/components/schemas/tiles_error' + + + /tiles/collections/{layer}/tiles/{tms}: + get: + tags: + - OGC API Tiles + summary: Récupère les niveaux disponibles pour une couche vecteur + operationId: ogc_get_vector_tileset + parameters: + + - name: layer + required: true + in: path + description: couche demandée + schema: + type: string + + - name: tms + required: true + in: path + description: TMS demandé + schema: + type: string + + - name: f + required: false + in: query + description: format de sortie demandé + schema: + type: string + enum: ['json'] + + responses: + 200: + description: Couche demandée + content: + application/json: + schema: + $ref: '#/components/schemas/tiles_tileset' + 400: + description: Format demandé invalide ou la couche demandée correspond à de la donnée raster + content: + application/json: + schema: + $ref: '#/components/schemas/tiles_error' + 404: + description: Couche ou TMS inconnu + content: + application/json: + schema: + $ref: '#/components/schemas/tiles_error' + + + /tiles/collections/{layer}/styles: + get: + tags: + - OGC API Tiles + summary: Récupère les styles disponibles pour une couche raster + operationId: ogc_get_raster_styles + parameters: + + - name: layer + required: true + in: path + description: couche demandée + schema: + type: string + + - name: f + required: false + in: query + description: format de sortie demandé + schema: + type: string + enum: ['json'] + + responses: + 200: + description: Couche demandée + content: + application/json: + schema: + $ref: '#/components/schemas/tiles_styles' + 400: + description: Format demandé invalide ou la couche demandée correspond à de la donnée vecteur + content: + application/json: + schema: + $ref: '#/components/schemas/tiles_error' + 404: + description: Couche inconnue + content: + application/json: + schema: + $ref: '#/components/schemas/tiles_error' + + + /tiles/collections/{layer}/styles/{style}/map/tiles: + get: + tags: + - OGC API Tiles + summary: Récupère les TMS disponibles pour une couche raster + operationId: ogc_get_raster_tilesets + parameters: + + - name: layer + required: true + in: path + description: couche demandée + schema: + type: string + + - name: style + required: true + in: path + description: style demandé + schema: + type: string + + - name: f + required: false + in: query + description: format de sortie demandé + schema: + type: string + enum: ['json'] + + responses: + 200: + description: Couche demandée + content: + application/json: + schema: + $ref: '#/components/schemas/tiles_tilesets' + 400: + description: Format demandé invalide ou la couche demandée correspond à de la donnée vecteur + content: + application/json: + schema: + $ref: '#/components/schemas/tiles_error' + 404: + description: Couche ou style inconnu + content: + application/json: + schema: + $ref: '#/components/schemas/tiles_error' + + + /tiles/collections/{layer}/styles/{style}/map/tiles/{tms}: + get: + tags: + - OGC API Tiles + summary: Récupère les niveaux disponibles pour une couche raster + operationId: ogc_get_raster_tileset + parameters: + + - name: layer + required: true + in: path + description: couche demandée + schema: + type: string + + - name: style + required: true + in: path + description: style demandé + schema: + type: string + + - name: tms + required: true + in: path + description: TMS demandé + schema: + type: string + + - name: f + required: false + in: query + description: format de sortie demandé + schema: + type: string + enum: ['json'] + + responses: + 200: + description: Couche demandée + content: + application/json: + schema: + $ref: '#/components/schemas/tiles_tileset' + 400: + description: Format demandé invalide ou la couche demandée correspond à de la donnée vecteur + content: + application/json: + schema: + $ref: '#/components/schemas/tiles_error' + 404: + description: Couche, style ou TMS inconnu + content: + application/json: + schema: + $ref: '#/components/schemas/tiles_error' + /tiles/collections/{layer}/styles/{style}/map/tiles/{tms}/{level}/{row}/{col}: get: tags: @@ -1117,66 +1409,60 @@ paths: schema: $ref: '#/components/schemas/tiles_error' - # /tiles/tilematrixsets: - # get: - # tags: - # - OGC API Tiles - # summary: Récupère la liste des tileMatreixSet disponibles - # operationId: ogc_get_tilematrixsets - # parameters: - - # - name: f - # required: false - # in: query - # description: format de sortie demandée - # schema: - # type: string - # default: - # application/json - # example: - # application/json - - # responses: - # 200: - # description: Liste des tileMatreixSet disponibles - # content: - # application/json: - # schema: - # $ref: '#/components/schemas/ogc_tilematrixsets' + /tiles/tileMatrixSets: + get: + tags: + - OGC API Tiles + summary: Récupère la liste des tile matrix sets disponibles + operationId: ogc_get_tilematrixsets + parameters: + + - name: f + required: false + in: query + description: format de sortie demandé + schema: + type: string + enum: ['json'] + + responses: + 200: + description: Liste des tile matrix sets disponibles + content: + application/json: + schema: + $ref: '#/components/schemas/tiles_tilematrixsets' - # /tiles/tilematrixsets/{tms}: - # get: - # tags: - # - OGC API Tiles - # summary: Récupère les informations sur un tileMatrixSet - # operationId: ogc_get_tilematrixsets_id - # parameters: - - # - name: tms - # required: true - # in: path - # description: tileMatrixSet demandée - # schema: - # type: string - - # - name: f - # required: false - # in: query - # description: format de sortie demandée - # schema: - # type: string - # default: - # application/json - # example: - # application/json + /tiles/tileMatrixSets/{tms}: + get: + tags: + - OGC API Tiles + summary: Récupère les informations sur un tile matrix set + operationId: ogc_get_tilematrixset + parameters: + + - name: tms + required: true + in: path + description: tile matrix set demandé + schema: + type: string + + - name: f + required: false + in: query + description: format de sortie demandé + schema: + type: string + enum: ['json'] - # responses: - # 200: - # description: ... - # content: - # application/json: - # schema: - # $ref: '#/components/schemas/ogc_tilematrixsets_id' + responses: + 200: + description: Définition du TMS + content: + application/json: + schema: + $ref: '#/components/schemas/tiles_tilematrixset' ######################################### ADMIN @@ -1432,26 +1718,48 @@ components: maxItems: 4 example: [-5, 40, 5, 50] - tiles_metadata: + tiles_link: type: object properties: href: type: string rel: type: string - enum: ["describedby", "self"] + enum: ["describedby", "self", "item"] type: type: string title: type: string + + tiles_get_landing_page: + type: object + properties: + title: + type: string + description: + type: string + links: + type: array + items: + $ref: '#/components/schemas/common_metadata' + + ogc_get_conformance: + type: object + properties: + conformsTo: + type: array + items: + type: string + format: uri + tiles_collections: type: object properties: links: type: array items: - $ref: '#/components/schemas/tiles_metadata' + $ref: '#/components/schemas/tiles_link' collections: type: array items: @@ -1479,7 +1787,7 @@ components: links: type: array items: - $ref: '#/components/schemas/tiles_metadata' + $ref: '#/components/schemas/tiles_link' crs: type: array items: @@ -1487,11 +1795,160 @@ components: dataType: type: string enum: ["map", "vector"] + mapTiles: + type: boolean + dataTiles: + type: boolean minCellSize: type: number maxCellSize: type: number + tiles_styles: + type: object + properties: + id: + type: string + title: + type: string + links: + type: array + items: + $ref: '#/components/schemas/tiles_link' + styles: + type: array + items: + type: object + properties: + id: + type: string + title: + type: string + links: + type: array + items: + $ref: '#/components/schemas/tiles_link' + + tiles_tilesets: + type: object + properties: + links: + type: array + items: + $ref: '#/components/schemas/tiles_link' + tilesets: + type: array + items: + type: object + properties: + title: + type: string + dataType: + type: string + enum: ["map", "vector"] + crs: + type: string + tileMatrixSetURI: + type: string + links: + type: array + items: + $ref: '#/components/schemas/tiles_link' + + tiles_tileset: + type: object + properties: + title: + type: string + description: + type: string + crs: + type: string + tileMatrixSetURI: + type: string + dataType: + type: string + enum: ["map", "vector"] + links: + type: array + items: + $ref: '#/components/schemas/tiles_link' + tileMatrixSetLimits: + type: array + items: + type: object + properties: + tileMatrix: + type: string + minTileRow: + type: integer + maxTileRow: + type: integer + minTileCol: + type: integer + maxTileCol: + type: integer + + tiles_tilematrixsets: + type: object + properties: + tilematrixsets: + type: object + properties: + id: + type: string + title: + type: string + crs: + type: string + links: + type: array + items: + $ref: '#/components/schemas/tiles_link' + + tiles_tilematrixset: + type: object + properties: + id: + type: string + title: + type: string + description: + type: string + keywords: + type: array + items: + type: string + crs: + type: string + tileMatrices: + type: array + items: + type: object + properties: + id: + type: string + cellSize: + type: number + cornerOfOrigin: + type: string + enum: ["topLeft"] + pointOfOrigin: + type: array + items: + type: number + minItems: 2 + maxItems: 2 + tileWidth: + type: integer + tileHeight: + type: integer + matrixHeight: + type: integer + matrixWidth: + type: integer + + tiles_error: type: object additionalProperties: false @@ -1709,6 +2166,16 @@ components: - lanczos_3 - lanczos_4 + crs: + type: array + items: + type: string + + tms: + type: array + items: + type: string + get_feature_info: oneOf: - type: object @@ -1744,20 +2211,12 @@ components: properties: pyramid: type: boolean - crs: - type: array - items: - type: string wmts: type: object properties: pyramid: type: boolean - tms: - type: array - items: - type: string tms: type: object diff --git a/src/configurations/Layer.cpp b/src/configurations/Layer.cpp index cfb7003..38a7777 100644 --- a/src/configurations/Layer.cpp +++ b/src/configurations/Layer.cpp @@ -218,16 +218,18 @@ bool Layer::parse(json11::Json& doc, ServicesConfiguration* services) { } // On a forcément le TMS natif de la donnée pour le WMTS - WmtsTmsInfos infos_tms_natif; - infos_tms_natif.tms = pyramid->get_tms(); - infos_tms_natif.top_level = pyramid->get_highest_level()->get_id(); - infos_tms_natif.bottom_level = pyramid->get_lowest_level()->get_id(); - infos_tms_natif.wmts_id = infos_tms_natif.tms->get_id() + "_" + infos_tms_natif.top_level + "_" + infos_tms_natif.bottom_level; - wmts_tilematrixsets.push_back(infos_tms_natif); + TileMatrixSetInfos* infos_tms_natif = new TileMatrixSetInfos(pyramid->get_tms()); + infos_tms_natif->set_bottom_top(pyramid->get_lowest_level()->get_id(), pyramid->get_highest_level()->get_id()); + + for (Level* l : pyramid->get_ordered_levels(false)) { + infos_tms_natif->limits.push_back(l->get_tile_limits()); + } + + available_tilematrixsets.push_back(infos_tms_natif); // On a forcément le CRS natif de la donnée pour le WMS - wms_crss.push_back ( pyramid->get_tms()->get_crs() ); + available_crss.push_back ( pyramid->get_tms()->get_crs() ); /********************** Gestion de l'étendue des données */ @@ -334,7 +336,7 @@ bool Layer::parse(json11::Json& doc, ServicesConfiguration* services) { BOOST_LOG_TRIVIAL(warning) << "Style " << styleName << " not usable for broadcast" ; continue; } - styles.push_back ( sty ); + available_styles.push_back ( sty ); } else { error_message = "styles have to be a string array"; return false; @@ -345,18 +347,18 @@ bool Layer::parse(json11::Json& doc, ServicesConfiguration* services) { return false; } - if ( styles.size() == 0 ) { + if ( available_styles.size() == 0 ) { error_message = "No provided valid style, the layer is not valid" ; return false; } // Configuration des reprojections possibles - // TMS additionnels pour le WMTS - if (services->get_wmts_service()->reprojection_enabled() && doc["wmts"].is_object() && doc["wmts"]["tms"].is_array()) { - for (json11::Json t : doc["wmts"]["tms"].array_items()) { + // TMS additionnels (pour le WMTS et l'API Tiles) + if (doc["extra_tilematrixsets"].is_array()) { + for (json11::Json t : doc["extra_tilematrixsets"].array_items()) { if (! t.is_string()) { - error_message = "wmts.tms have to be a string array" ; + error_message = "extra_tilematrixsets have to be a string array" ; return false; } TileMatrixSet* tms = TmsBook::get_tms(t.string_value()); @@ -366,22 +368,21 @@ bool Layer::parse(json11::Json& doc, ServicesConfiguration* services) { } if (get_tilematrixset(tms->get_id()) != NULL) { - // Le TMS est déjà dans la liste ou est celui natif de la pyramide + // Le TMS est déjà dans la liste continue; } - WmtsTmsInfos infos; - infos.tms = tms; + TileMatrixSetInfos* infos = new TileMatrixSetInfos(tms); // On calculera plus tard (dans calculate_tilematrix_limits) les informations de niveaux de haut et bas (et donc l'ID WMTS de ce TMS) - wmts_tilematrixsets.push_back(infos); + available_tilematrixsets.push_back(infos); } } - // Projections additionnelles pour le WMS - if (services->get_wms_service()->reprojection_enabled() && doc["wms"].is_object() && doc["wms"]["crs"].is_array()) { - for (json11::Json c : doc["wms"]["crs"].array_items()) { + // Projections additionnelles (pour le WMS) + if (doc["extra_crs"].is_array()) { + for (json11::Json c : doc["extra_crs"].array_items()) { if (! c.is_string()) { - error_message = "wms.crs have to be a string array" ; + error_message = "extra_crs have to be a string array" ; return false; } std::string str_crs = c.string_value(); @@ -404,7 +405,7 @@ bool Layer::parse(json11::Json& doc, ServicesConfiguration* services) { continue; } - wms_crss.push_back ( crs ); + available_crss.push_back ( crs ); } } @@ -433,7 +434,6 @@ bool Layer::parse(json11::Json& doc, ServicesConfiguration* services) { gfi_enabled = false; } - return true; } @@ -457,92 +457,86 @@ void Layer::calculate_native_tilematrix_limits() { void Layer::calculate_tilematrix_limits() { - std::vector new_list; - - // Le premier TMS est celui natif : les tuiles limites sont déjà calculée et stockées dans les niveaux - - WmtsTmsInfos infos = wmts_tilematrixsets.at(0); - infos.limits = std::vector(); - - for (Level* l : pyramid->get_ordered_levels(false)) { - infos.limits.push_back(l->get_tile_limits()); - } - - new_list.push_back(infos); - + // Le premier TMS est celui natif : toutes les informations sont déjà présentes // On calcule les tuiles limites pour tous les TMS supplémentaires, que l'on filtre - for (unsigned i = 1 ; i < wmts_tilematrixsets.size(); i++) { - TileMatrixSet* tms = wmts_tilematrixsets.at(i).tms; + for (unsigned i = 1 ; i < available_tilematrixsets.size(); i++) { + TileMatrixSetInfos* tmsi = available_tilematrixsets.at(i); - BOOST_LOG_TRIVIAL(debug) << "Tile limits calculation for TMS " << tms->get_id() ; + BOOST_LOG_TRIVIAL(debug) << "Tile limits calculation for TMS " << tmsi->tms->get_id() ; - infos.tms = tms; - infos.limits = std::vector(); + tmsi->limits = std::vector(); BoundingBox tmp = geographic_bbox; tmp.print(); - tmp = tmp.crop_to_crs_area(tms->get_crs()); + tmp = tmp.crop_to_crs_area(tmsi->tms->get_crs()); if ( tmp.has_null_area() ) { - BOOST_LOG_TRIVIAL(warning) << "La couche n'est pas dans l'aire de définition du CRS du TMS supplémentaire " << tms->get_id() ; + BOOST_LOG_TRIVIAL(warning) << "La couche n'est pas dans l'aire de définition du CRS du TMS supplémentaire " << tmsi->tms->get_id() ; + available_tilematrixsets.erase (available_tilematrixsets.begin() + i); + delete tmsi; + i--; continue; } - if ( ! tmp.reproject ( CRS::get_epsg4326(), tms->get_crs() ) ) { - BOOST_LOG_TRIVIAL(warning) << "Impossible de reprojeter la bbox de la couche dans le CRS du TMS supplémentaire " << tms->get_id() ; + if ( ! tmp.reproject ( CRS::get_epsg4326(), tmsi->tms->get_crs() ) ) { + BOOST_LOG_TRIVIAL(warning) << "Impossible de reprojeter la bbox de la couche dans le CRS du TMS supplémentaire " << tmsi->tms->get_id() ; + available_tilematrixsets.erase (available_tilematrixsets.begin() + i); + delete tmsi; + i--; continue; } // Recherche du niveau du haut du TMS supplémentaire TileMatrix* tm_top = NULL; for (Level* l : pyramid->get_ordered_levels(false)) { - tm_top = tms->get_corresponding_tm(l->get_tm(), pyramid->get_tms()) ; + tm_top = tmsi->tms->get_corresponding_tm(l->get_tm(), pyramid->get_tms()) ; if (tm_top != NULL) { // On a trouvé un niveau du haut du TMS supplémentaire satisfaisant pour la pyramide break; } } if ( tm_top == NULL ) { - BOOST_LOG_TRIVIAL(warning) << "Impossible de trouver un niveau du haut satisfaisant dans le TMS supplémentaire " << tms->get_id() ; + BOOST_LOG_TRIVIAL(warning) << "Impossible de trouver un niveau du haut satisfaisant dans le TMS supplémentaire " << tmsi->tms->get_id() ; + available_tilematrixsets.erase (available_tilematrixsets.begin() + i); + delete tmsi; + i--; continue; } // Recherche du niveau du bas du TMS supplémentaire TileMatrix* tm_bottom = NULL; for (Level* l : pyramid->get_ordered_levels(true)) { - tm_bottom = tms->get_corresponding_tm(l->get_tm(), pyramid->get_tms()) ; + tm_bottom = tmsi->tms->get_corresponding_tm(l->get_tm(), pyramid->get_tms()) ; if (tm_bottom != NULL) { // On a trouvé un niveau du bas du TMS supplémentaire satisfaisant pour la pyramide break; } } if ( tm_bottom == NULL ) { - BOOST_LOG_TRIVIAL(warning) << "Impossible de trouver un niveau du bas satisfaisant dans le TMS supplémentaire " << tms->get_id() ; + BOOST_LOG_TRIVIAL(warning) << "Impossible de trouver un niveau du bas satisfaisant dans le TMS supplémentaire " << tmsi->tms->get_id() ; + available_tilematrixsets.erase (available_tilematrixsets.begin() + i); + delete tmsi; + i--; continue; } // Pour chaque niveau entre le haut et le base, on va calculer les tuiles limites bool begin = false; - for (TileMatrix* tm : tms->get_ordered_tm(false)) { + for (TileMatrix* tm : tmsi->tms->get_ordered_tm(false)) { if (! begin && tm->get_id() != tm_top->get_id()) { continue; } begin = true; - infos.limits.push_back(tm->bbox_to_tile_limits(tmp)); + tmsi->limits.push_back(tm->bbox_to_tile_limits(tmp)); if (tm->get_id() == tm_bottom->get_id()) { break; } } - infos.bottom_level = tm_bottom->get_id(); - infos.top_level = tm_top->get_id(); - infos.wmts_id = infos.tms->get_id() + "_" + infos.top_level + "_" + infos.bottom_level; - - new_list.push_back(infos); + tmsi->bottom_level = tm_bottom->get_id(); + tmsi->top_level = tm_top->get_id(); + tmsi->request_id = tmsi->tms->get_id() + "_" + tmsi->top_level + "_" + tmsi->bottom_level; } - - // La nouvelle liste ne contient pas les TMS pour lesquels nous n'avons pas pu calculer les niveaux et les limites - wmts_tilematrixsets = new_list; } Layer::Layer(std::string path, ServicesConfiguration* services) : Configuration(path), pyramid(NULL), attribution(NULL) { @@ -551,7 +545,7 @@ Layer::Layer(std::string path, ServicesConfiguration* services) : Configuration( id = Configuration::get_filename(file_path, ".json"); - if ( contain_chars(id, "<>") ) { + if ( contain_chars(id, "<>\"") ) { error_message = "Layer identifier contains forbidden chars" ; return; } @@ -602,7 +596,7 @@ Layer::Layer(std::string layer_name, std::string content, ServicesConfiguration* /********************** Id */ - if ( contain_chars(id, "<>") ) { + if ( contain_chars(id, "<>\"") ) { error_message = "Layer identifier contains forbidden chars" ; return; } @@ -633,6 +627,9 @@ Layer::~Layer() { if (attribution != NULL) delete attribution; if (pyramid != NULL) delete pyramid; + for (TileMatrixSetInfos* tmsi : available_tilematrixsets) { + delete tmsi; + } } @@ -646,30 +643,28 @@ bool Layer::is_tiles_enabled() { return tiles; } std::vector* Layer::get_keywords() { return &keywords; } Pyramid* Layer::get_pyramid() { return pyramid; } Style* Layer::get_default_style() { - if (styles.size() > 0) { - return styles[0]; + if (available_styles.size() > 0) { + return available_styles[0]; } else { return NULL; } } -std::vector* Layer::get_styles() { return &styles; } - -std::vector* Layer::get_available_tilematrixsets_wmts() { return &wmts_tilematrixsets; } -std::vector* Layer::get_available_crs_wms() { return &wms_crss; } -TileMatrixSet* Layer::get_tilematrixset(std::string wmts_id) { - for ( unsigned int k = 0; k < wmts_tilematrixsets.size(); k++ ) { - if ( wmts_id == wmts_tilematrixsets.at(k).wmts_id || wmts_id == wmts_tilematrixsets.at(k).tms->get_id() ) { - return wmts_tilematrixsets.at(k).tms; +std::vector* Layer::get_styles() { return &available_styles; } + +TileMatrixSetInfos* Layer::get_tilematrixset(std::string request_id) { + for ( unsigned int k = 0; k < available_tilematrixsets.size(); k++ ) { + if ( request_id == available_tilematrixsets.at(k)->request_id || request_id == available_tilematrixsets.at(k)->tms->get_id() ) { + return available_tilematrixsets.at(k); } } return NULL; } TileMatrixLimits* Layer::get_tilematrix_limits(TileMatrixSet* tms, TileMatrix* tm) { - for ( unsigned int k = 0; k < wmts_tilematrixsets.size(); k++ ) { - if ( tms->get_id() == wmts_tilematrixsets.at (k).tms->get_id() ) { - for ( unsigned int l = 0; l < wmts_tilematrixsets.at (k).limits.size(); l++ ) { - if ( tm->get_id() == wmts_tilematrixsets.at (k).limits.at(l).tm_id ) { - return &(wmts_tilematrixsets.at (k).limits.at(l)); + for ( unsigned int k = 0; k < available_tilematrixsets.size(); k++ ) { + if ( tms->get_id() == available_tilematrixsets.at(k)->tms->get_id() ) { + for ( unsigned int l = 0; l < available_tilematrixsets.at(k)->limits.size(); l++ ) { + if ( tm->get_id() == available_tilematrixsets.at(k)->limits.at(l).tm_id ) { + return &(available_tilematrixsets.at(k)->limits.at(l)); } } } @@ -678,25 +673,25 @@ TileMatrixLimits* Layer::get_tilematrix_limits(TileMatrixSet* tms, TileMatrix* t } Style* Layer::get_style_by_identifier(std::string identifier) { - for ( unsigned int i = 0; i < styles.size(); i++ ) { - if ( identifier == styles[i]->get_identifier() ) - return styles[i]; + for ( unsigned int i = 0; i < available_styles.size(); i++ ) { + if ( identifier == available_styles[i]->get_identifier() ) + return available_styles[i]; } return NULL; } std::string Layer::get_title() { return title; } -bool Layer::is_available_crs_wms(CRS* c) { - for ( unsigned int k = 0; k < wms_crss.size(); k++ ) { - if ( c->cmp_request_code ( wms_crss.at (k)->get_request_code() ) ) { +bool Layer::is_available_crs(CRS* c) { + for ( unsigned int k = 0; k < available_crss.size(); k++ ) { + if ( c->cmp_request_code ( available_crss.at (k)->get_request_code() ) ) { return true; } } return false; } -bool Layer::is_available_crs_wms(std::string c) { - for ( unsigned int k = 0; k < wms_crss.size(); k++ ) { - if ( wms_crss.at (k)->cmp_request_code ( c ) ) { +bool Layer::is_available_crs(std::string c) { + for ( unsigned int k = 0; k < available_crss.size(); k++ ) { + if ( available_crss.at (k)->cmp_request_code ( c ) ) { return true; } } @@ -715,7 +710,7 @@ std::string Layer::get_gfi_layers() { return gfi_layers; } std::string Layer::get_gfi_query_layers() { return gfi_query_layers; } Interpolation::KernelType Layer::get_resampling() { return resampling; } -void Layer::add_node_wmts(ptree& parent, WmtsService* service, bool only_inspire, std::map< std::string, WmtsTmsInfos>* used_tms_list) { +void Layer::add_node_wmts(ptree& parent, WmtsService* service, bool only_inspire, std::map< std::string, TileMatrixSetInfos*>* used_tms_list) { if (! wmts) { return; @@ -749,14 +744,14 @@ void Layer::add_node_wmts(ptree& parent, WmtsService* service, bool only_inspire m.add_node_wmts(node); } - for ( Style* s : styles) { + for ( Style* s : available_styles) { /* - Pour qu'un style soit applicable en WMTS, il faut que : - - quel style soit l'identité + Un style est applicable en WMTS si : + - le style est l'identité Ou : - - que le style soit une palette (c'est forcément le cas, on n'accepte pas les styles plus complexes à la diffusion) - - que la donnée soit sur un canal - - que la donnée soit en PNG + - le style est une palette (c'est forcément le cas, on n'accepte pas les styles plus complexes à la diffusion) + - la donnée est sur un canal + - la donnée est en PNG Cela permet de n'avoir à modifier que l'en tête de la tuile PNG pour que le style "soit appliqué" */ if (s->is_identity() || (pyramid->get_channels() == 1 && pyramid->get_sample_compression() == Compression::PNG)) { @@ -773,28 +768,30 @@ void Layer::add_node_wmts(ptree& parent, WmtsService* service, bool only_inspire } // On ajoute les TMS disponibles avec les tuiles limites - // Si la reprojection WMTS n'est pas activée, nous n'avons les limites que pour le TMS natif - for (WmtsTmsInfos wti : wmts_tilematrixsets) { + + for (TileMatrixSetInfos* tmsi : available_tilematrixsets) { ptree& tms_node = node.add("TileMatrixSetLink", ""); - tms_node.add("TileMatrixSet", wti.wmts_id); + tms_node.add("TileMatrixSet", tmsi->request_id); ptree& tms_limits_node = tms_node.add("TileMatrixSetLimits", ""); // Niveaux - for ( unsigned int j = 0; j < wti.limits.size(); j++ ) { - wti.limits.at(j).add_node(tms_limits_node); + for ( unsigned int j = 0; j < tmsi->limits.size(); j++ ) { + tmsi->limits.at(j).add_node(tms_limits_node); } - used_tms_list->insert ( std::pair ( wti.wmts_id , wti) ); + used_tms_list->emplace ( tmsi->request_id , tmsi ); + + // Si la reprojection WMTS n'est pas activée, nous n'exposons que le premier TMS, celui natif + if (! service->reprojection_enabled()) { + break; + } } // On veut ajouter le TMS natif des données dans sa version originale - WmtsTmsInfos origin_infos; - origin_infos.tms = pyramid->get_tms(); - origin_infos.top_level = ""; - origin_infos.bottom_level = ""; - origin_infos.wmts_id = origin_infos.tms->get_id(); - used_tms_list->insert ( std::pair ( origin_infos.wmts_id, origin_infos) ); + if (used_tms_list->find(pyramid->get_tms()->get_id()) == used_tms_list->end()) { + used_tms_list->emplace ( pyramid->get_tms()->get_id(), new TileMatrixSetInfos(pyramid->get_tms()) ); + } } void Layer::add_node_wms(ptree& parent, WmsService* service, bool only_inspire) { @@ -823,15 +820,20 @@ void Layer::add_node_wms(ptree& parent, WmsService* service, bool only_inspire) } } - for ( CRS* c : wms_crss) { + for ( CRS* c : available_crss) { node.add("CRS", c->get_request_code()); + + // Si la reprojection WMS n'est pas activée, nous n'exposons que le premier CRS, celui natif + if (! service->reprojection_enabled()) { + break; + } } geographic_bbox.add_node(node, true, true); // BoundingBox if ( only_inspire ) { - for ( CRS* c : wms_crss) { + for ( CRS* c : available_crss) { BoundingBox bbox ( 0,0,0,0 ); if ( geographic_bbox.is_in_crs_area(c)) { bbox = geographic_bbox; @@ -841,18 +843,25 @@ void Layer::add_node_wms(ptree& parent, WmsService* service, bool only_inspire) bbox.reproject(CRS::get_epsg4326(), c); bbox.add_node(node, false, c->is_lat_lon() ); - } - for ( unsigned int i = 0; i < service->get_available_crs()->size(); i++ ) { - CRS* crs = service->get_available_crs()->at(i); - BoundingBox bbox ( 0,0,0,0 ); - if ( geographic_bbox.is_in_crs_area(crs)) { - bbox = geographic_bbox; - } else { - bbox = geographic_bbox.crop_to_crs_area(crs); + + // Si la reprojection WMS n'est pas activée, nous n'exposons que la bbox en projection native + if (! service->reprojection_enabled()) { + break; } + } + if (! service->reprojection_enabled()) { + for ( unsigned int i = 0; i < service->get_available_crs()->size(); i++ ) { + CRS* crs = service->get_available_crs()->at(i); + BoundingBox bbox ( 0,0,0,0 ); + if ( geographic_bbox.is_in_crs_area(crs)) { + bbox = geographic_bbox; + } else { + bbox = geographic_bbox.crop_to_crs_area(crs); + } - bbox.reproject(CRS::get_epsg4326(), crs); - bbox.add_node(node, false, crs->is_lat_lon() ); + bbox.reproject(CRS::get_epsg4326(), crs); + bbox.add_node(node, false, crs->is_lat_lon() ); + } } } else { BoundingBox bbox = native_bbox; @@ -867,7 +876,7 @@ void Layer::add_node_wms(ptree& parent, WmsService* service, bool only_inspire) m.add_node_wms(node); } - for ( Style* s : styles) { + for ( Style* s : available_styles) { s->add_node_wms(node); } @@ -908,7 +917,7 @@ json11::Json Layer::to_json_tiles(TilesService* service) { std::vector links; links.push_back(json11::Json::object { - { "href", service->get_endpoint_uri() + "/collections/" + id + "?f=application/json"}, + { "href", service->get_endpoint_uri() + "/collections/" + id + "?f=json"}, { "rel", "self"}, { "type", "application/json"}, { "title", "this document"} @@ -918,13 +927,37 @@ json11::Json Layer::to_json_tiles(TilesService* service) { links.push_back(m.to_json_tiles("Dataset metadata", "describedby")); } + if (Rok4Format::is_raster(pyramid->get_format())) { + links.push_back(json11::Json::object { + { "href", service->get_endpoint_uri() + "/collections/" + id + "/styles?f=json"}, + { "rel", "describedby"}, + { "type", "application/json"}, + { "title", "Styles list for " + id} + }); + } else { + links.push_back(json11::Json::object { + { "href", service->get_endpoint_uri() + "/collections/" + id + "/tiles?f=json"}, + { "rel", "describedby"}, + { "type", "application/json"}, + { "title", "Tilesets list for " + id} + }); + } + res["links"] = links; res["crs"] = json11::Json::array { pyramid->get_tms()->get_crs()->get_url() }; - res["dataType"] = Rok4Format::is_raster(pyramid->get_format()) ? "map" : "vector"; + if (Rok4Format::is_raster(pyramid->get_format())) { + res["dataType"] = "map"; + res["dataTiles"] = false; + res["mapTiles"] = true; + } else { + res["dataType"] = "vector"; + res["dataTiles"] = true; + res["mapTiles"] = false; + } res["minCellSize"] = pyramid->get_lowest_level()->get_res(); res["maxCellSize"] = pyramid->get_highest_level()->get_res(); @@ -932,6 +965,166 @@ json11::Json Layer::to_json_tiles(TilesService* service) { return res; } + +json11::Json Layer::to_json_styles(TilesService* service) { + + if (! tiles) { + return json11::Json::NUL; + } + + json11::Json::object res = json11::Json::object { + { "id", id }, + { "title", title }, + { "links", json11::Json::array { + json11::Json::object { + { "href", service->get_endpoint_uri() + "/collections/" + id + "/styles?f=json"}, + { "rel", "self"}, + { "type", "application/json"}, + { "title", "this document"} + } + }} + }; + + std::vector json_styles; + for ( Style* s : available_styles) { + json_styles.push_back(json11::Json::object { + { "id", s->get_identifier()}, + { "title", s->get_titles().at(0)}, + { "links", json11::Json::array { + json11::Json::object { + { "href", service->get_endpoint_uri() + "/collections/" + id + "/styles/" + s->get_identifier() + "/map/tiles?f=json"}, + { "rel", "describedby"}, + { "type", "application/json"}, + { "title", "Tilesets list for " + id + " with style " + s->get_identifier()} + } + }} + }); + } + + res["styles"] = json_styles; + + return res; +} + +json11::Json Layer::to_json_tilesets(TilesService* service, Style* style) { + + json11::Json::object res = json11::Json::object {}; + + std::vector links; + + if (style == NULL) { + // Interrogation de donnée vecteur + links.push_back(json11::Json::object { + { "href", service->get_endpoint_uri() + "/collections/" + id + "/tiles?f=json"}, + { "rel", "self"}, + { "type", "application/json"}, + { "title", "this document"} + }); + } else { + // Interrogation de donnée raster + links.push_back(json11::Json::object { + { "href", service->get_endpoint_uri() + "/collections/" + id + "/styles/" + style->get_identifier() + "/map/tiles?f=json"}, + { "rel", "self"}, + { "type", "application/json"}, + { "title", "this document"} + }); + } + + res["links"] = links; + + std::vector tilesets; + + for (TileMatrixSetInfos* t : available_tilematrixsets) { + std::string data_type; + std::string tileset_uri; + + if (style == NULL) { + data_type = "vector"; + tileset_uri = service->get_endpoint_uri() + "/collections/" + id + "/tiles/" + t->tms->get_id() + "?f=json"; + } else { + data_type = "map"; + tileset_uri = service->get_endpoint_uri() + "/collections/" + id + "/styles/" + style->get_identifier() + "/map/tiles/" + t->tms->get_id() + "?f=json"; + } + + tilesets.push_back(json11::Json::object { + { "title", title }, + { "dataType", data_type}, + { "crs", t->tms->get_crs()->get_url()}, + { "tileMatrixSetURI", service->get_endpoint_uri() + "/tileMatrixSets/" + t->tms->get_id() + "?f=json"}, + { "links", json11::Json::array { + json11::Json::object { + { "href", tileset_uri}, + { "rel", "describedby"}, + { "type", "application/json"}, + { "title", "Tileset " + t->tms->get_id() + " for " + id} + } + }} + }); + + // Si la reprojection API Tiles n'est pas activée, nous n'exposons que le premier TMS, celui natif + if (! service->reprojection_enabled()) { + break; + } + } + + res["tilesets"] = tilesets; + + return res; +} + +json11::Json Layer::to_json_tileset(TilesService* service, Style* style, TileMatrixSetInfos* tmsi) { + + json11::Json::object res = json11::Json::object { + { "title", title}, + { "description", abstract}, + { "crs", tmsi->tms->get_crs()->get_url()}, + { "tileMatrixSetURI", service->get_endpoint_uri() + "/tileMatrixSets/" + tmsi->tms->get_id() + "?f=json"}, + }; + + std::vector links; + + if (style == NULL) { + // Interrogation de donnée vecteur + res["dataType"] = "vector"; + links.push_back(json11::Json::object { + { "href", service->get_endpoint_uri() + "/collections/" + id + "/tiles/" + tmsi->tms->get_id() + "?f=json"}, + { "rel", "self"}, + { "type", "application/json"}, + { "title", "this document"} + }); + // Lien vers la tuile + links.push_back(json11::Json::object { + { "href", service->get_endpoint_uri() + "/collections/" + id + "/tiles/" + tmsi->tms->get_id() + "/{tileMatrix}/{tileRow}/{tileCol}?f=" + Rok4Format::to_tiles_format(pyramid->get_format())}, + { "rel", "item"}, + { "type", Rok4Format::to_mime_type(pyramid->get_format())}, + { "title", id + " tiles as " + Rok4Format::to_mime_type(pyramid->get_format())}, + { "templated", true} + }); + } else { + // Interrogation de donnée raster + res["dataType"] = "map"; + links.push_back(json11::Json::object { + { "href", service->get_endpoint_uri() + "/collections/" + id + "/styles/" + style->get_identifier() + "/map/tiles/" + tmsi->tms->get_id() + "?f=json"}, + { "rel", "self"}, + { "type", "application/json"}, + { "title", "this document"} + }); + // Lien vers la tuile + links.push_back(json11::Json::object { + { "href", service->get_endpoint_uri() + "/collections/" + id + "/styles/" + style->get_identifier() + "/map/tiles/" + tmsi->tms->get_id() + "/{tileMatrix}/{tileRow}/{tileCol}?f=" + Rok4Format::to_tiles_format(pyramid->get_format())}, + { "rel", "item"}, + { "type", Rok4Format::to_mime_type(pyramid->get_format())}, + { "title", id + " tiles as " + Rok4Format::to_mime_type(pyramid->get_format())}, + { "templated", true} + }); + } + + res["links"] = links; + res["tileMatrixSetLimits"] = tmsi->limits; + + return res; +} + std::string Layer::get_description_tilejson(TmsService* service) { int order = 0; diff --git a/src/configurations/Layer.h b/src/configurations/Layer.h index 4708823..321f19a 100644 --- a/src/configurations/Layer.h +++ b/src/configurations/Layer.h @@ -69,12 +69,20 @@ using boost::property_tree::ptree; #include "services/tms/Service.h" #include "services/tiles/Service.h" -struct WmtsTmsInfos { +struct TileMatrixSetInfos { TileMatrixSet* tms; - std::string wmts_id; + std::string request_id; std::string top_level; std::string bottom_level; std::vector limits; + + TileMatrixSetInfos(TileMatrixSet* tms) : tms(tms) { }; + + void set_bottom_top(std::string b, std::string t) { + top_level = t; + bottom_level = b; + request_id = tms->get_id() + "_" + top_level + "_" + bottom_level; + }; }; /** @@ -185,18 +193,18 @@ class Layer : public Configuration { * \~french \brief Liste des styles associés * \~english \brief Linked styles list */ - std::vector styles; + std::vector available_styles; /** * \~french \brief Liste des systèmes de coordonnées authorisées pour le WMS * \~english \brief Authorised coordinates systems list for WMS */ - std::vector wms_crss; + std::vector available_crss; /** * \~french \brief Liste des TMS d'interrogation autorisés en WMTS * \~english \brief TMS list for WMTS requests */ - std::vector wmts_tilematrixsets; + std::vector available_tilematrixsets; /** * \~french \brief Interpolation utilisée pour reprojeter ou recadrer les tuiles @@ -376,25 +384,6 @@ class Layer : public Configuration { * \return styles list */ std::vector* get_styles() ; - /** - * \~french - * \brief Retourne la liste des TMS disponibles - * \return liste d'informations sur les TMS - * \~english - * \brief Return the available TMS list - * \return TMS infos list - */ - std::vector* get_available_tilematrixsets_wmts() ; - - /** - * \~french - * \brief Retourne la liste des CRS disponibles en WMS - * \return liste des CRS de la couche - * \~english - * \brief Return the available CRS list for WMS - * \return CRS list - */ - std::vector* get_available_crs_wms() ; /** * \~french @@ -424,7 +413,7 @@ class Layer : public Configuration { * \brief Test if CRS is in the CRS list * \return Present or not */ - bool is_available_crs_wms(CRS* c) ; + bool is_available_crs(CRS* c) ; /** * \~french @@ -434,7 +423,7 @@ class Layer : public Configuration { * \brief Test if CRS is in the CRS list * \return Present or not */ - bool is_available_crs_wms(std::string c) ; + bool is_available_crs(std::string c) ; /** * \~french @@ -445,7 +434,7 @@ class Layer : public Configuration { * \brief Get available TMS for the layer with identifiant * \return NULL if not available */ - TileMatrixSet* get_tilematrixset(std::string id) ; + TileMatrixSetInfos* get_tilematrixset(std::string id) ; /** * \~french @@ -585,7 +574,7 @@ class Layer : public Configuration { * \param[in] only_inspire Only if layer is inspire compliant * \param[in] used_tms_list Used TMS, to add the layer ones */ - void add_node_wmts(ptree& parent, WmtsService* service, bool only_inspire, std::map< std::string, WmtsTmsInfos>* used_tms_list); + void add_node_wmts(ptree& parent, WmtsService* service, bool only_inspire, std::map< std::string, TileMatrixSetInfos*>* used_tms_list); /** * \~french \brief Récupère la description OGC API Tiles de la couche au format JSON @@ -595,6 +584,30 @@ class Layer : public Configuration { */ json11::Json to_json_tiles(TilesService* service); + /** + * \~french \brief Récupère la description OGC API Tiles des styles disponibles de la couche au format JSON + * \param[in] service Service OGC API Tiles appelant + * \~english \brief Get available styles OGC API Tiles description for the layer as JSON + * \param[in] service Calling OGC API Tiles service + */ + json11::Json to_json_styles(TilesService* service); + + /** + * \~french \brief Récupère la description OGC API Tiles des TMS disponibles de la couche au format JSON + * \param[in] service Service OGC API Tiles appelant + * \~english \brief Get available TMS OGC API Tiles description for the layer as JSON + * \param[in] service Calling OGC API Tiles service + */ + json11::Json to_json_tilesets(TilesService* service, Style* style); + + /** + * \~french \brief Récupère la description OGC API Tiles de la couche pour un TMS en particulier au format JSON + * \param[in] service Service OGC API Tiles appelant + * \~english \brief Get layer OGC API Tiles description for an available TMS as JSON + * \param[in] service Calling OGC API Tiles service + */ + json11::Json to_json_tileset(TilesService* service, Style* style, TileMatrixSetInfos* tmsi); + /** * \~french \brief Récupère la description TileJSON de la couche au format JSON * \details En accord avec https://github.com/mapbox/tilejson-spec diff --git a/src/configurations/Services.cpp b/src/configurations/Services.cpp index 01ef182..556d683 100644 --- a/src/configurations/Services.cpp +++ b/src/configurations/Services.cpp @@ -291,7 +291,7 @@ std::vector ServicesConfiguration::get_equals_crs(std::string crs) bool ServicesConfiguration::are_crs_equals( std::string crs1, std::string crs2 ) { - if (crs1 == crs2) { + if (to_upper_case(crs1) == to_upper_case(crs2)) { return true; } @@ -322,13 +322,6 @@ ServicesConfiguration::~ServicesConfiguration(){ delete admin_service; delete tiles_service; delete contact; - - // Les CRS équivalents - std::map >::iterator it; - for ( it = crs_equivalences.begin(); it != crs_equivalences.end(); it++ ) - for (unsigned int i = 0 ; i < it->second.size() ; i++) { - delete it->second.at(i); - } } void ServicesConfiguration::clean_cache() { diff --git a/src/services/admin/layers.cpp b/src/services/admin/layers.cpp index 4a01e23..6799ff8 100644 --- a/src/services/admin/layers.cpp +++ b/src/services/admin/layers.cpp @@ -50,12 +50,11 @@ DataStream* AdminService::add_layer ( Request* req, Rok4Server* serv ) { - std::string str_layer = req->path_params.at(0); Layer* layer = serv->get_server_configuration()->get_layer(str_layer); - if ( layer != NULL ) throw AdminException::get_error_message("Layer " + str_layer +" already exists.", "Configuration conflict", 409); + if ( layer != NULL ) throw AdminException::get_error_message("Layer already exists.", "Configuration conflict", 409); layer = new Layer( str_layer, req->body, serv->get_services_configuration() ); if ( ! layer->is_ok() ) { @@ -73,6 +72,10 @@ DataStream* AdminService::add_layer ( Request* req, Rok4Server* serv ) { DataStream* AdminService::update_layer ( Request* req, Rok4Server* serv ) { std::string str_layer = req->path_params.at(0); + if ( contain_chars(str_layer, "\"")) { + BOOST_LOG_TRIVIAL(warning) << "Forbidden char detected in TILES layer: " << str_layer ; + throw AdminException::get_error_message("Layer does not exists.", "Not found", 404); + } Layer* layer = serv->get_server_configuration()->get_layer(str_layer); @@ -96,6 +99,10 @@ DataStream* AdminService::update_layer ( Request* req, Rok4Server* serv ) { DataStream* AdminService::delete_layer ( Request* req, Rok4Server* serv ) { std::string str_layer = req->path_params.at(0); + if ( contain_chars(str_layer, "\"")) { + BOOST_LOG_TRIVIAL(warning) << "Forbidden char detected in TILES layer: " << str_layer ; + throw AdminException::get_error_message("Layer does not exists.", "Not found", 404); + } Layer* layer = serv->get_server_configuration()->get_layer(str_layer); diff --git a/src/services/common/getcapabilities.cpp b/src/services/common/getcapabilities.cpp index 989ff06..2c9691a 100644 --- a/src/services/common/getcapabilities.cpp +++ b/src/services/common/getcapabilities.cpp @@ -52,7 +52,7 @@ DataStream* CommonService::get_landing_page ( Request* req, Rok4Server* serv ) { std::string f = req->get_query_param("f"); - if (f != "" && f != "application/json") { + if (f != "" && f != "application/json" && f != "json") { throw CommonException::get_error_message("InvalidParameter", "Format unknown", 400); } @@ -100,7 +100,7 @@ DataStream* CommonService::get_landing_page ( Request* req, Rok4Server* serv ) { if (services->get_tiles_service()->is_enabled()) { links.push_back(json11::Json::object { - { "href", services->get_tiles_service()->get_endpoint_uri() + "/collections?f=application/json"}, + { "href", services->get_tiles_service()->get_endpoint_uri() + "/collections?f=json"}, { "rel", "data"}, { "type", "application/json"}, { "title", "OGC API Tiles capabilities"} diff --git a/src/services/tiles/Service.cpp b/src/services/tiles/Service.cpp index 4ac818c..c1b9158 100644 --- a/src/services/tiles/Service.cpp +++ b/src/services/tiles/Service.cpp @@ -119,30 +119,77 @@ TilesService::TilesService (json11::Json& doc) : Service(doc), metadata(NULL) { return ; } } + + if (doc["reprojection"].is_bool()) { + reprojection = doc["reprojection"].bool_value(); + } else if (! doc["reprojection"].is_null()) { + error_message = "WMTS service: reprojection have to be a boolean"; + return; + } else { + reprojection = false; + } } DataStream* TilesService::process_request(Request* req, Rok4Server* serv) { BOOST_LOG_TRIVIAL(debug) << "TILES service"; - if ( match_route( "/collections", {"GET"}, req ) ) { + if ( match_route( "", {"GET"}, req ) ) { + BOOST_LOG_TRIVIAL(debug) << "GETLANDINGPAGE request"; + return get_landing_page(req, serv); + } + else if ( match_route( "/conformance", {"GET"}, req ) ) { + BOOST_LOG_TRIVIAL(debug) << "GETCONFORMANCE request"; + return get_conformance(req, serv); + } + else if ( match_route( "/collections", {"GET"}, req ) ) { BOOST_LOG_TRIVIAL(debug) << "GETCAPABILITIES request"; return get_capabilities(req, serv); } + else if ( match_route( "/tileMatrixSets", {"GET"}, req ) ) { + BOOST_LOG_TRIVIAL(debug) << "GETTILEMATRIXSETS request"; + return get_tilematrixsets(req, serv); + } + else if ( match_route( "/tileMatrixSets/([^/]+)", {"GET"}, req ) ) { + BOOST_LOG_TRIVIAL(debug) << "GETTILEMATRIXSET request"; + return get_tilematrixset(req, serv); + } else if ( match_route( "/collections/([^/]+)", {"GET"}, req ) ) { BOOST_LOG_TRIVIAL(debug) << "GETTILES request"; return get_tiles(req, serv); } - else if ( match_route( "/collections/([^/]+)/styles/([^/]+)/map/tiles/([^/]+)/([^/]+)/([^/]+)/([^/]+)/info", {"GET"}, req ) ) { - BOOST_LOG_TRIVIAL(debug) << "GETFEATUREINFO request"; - return get_feature_info(req, serv); + // Données vecteur + else if ( match_route( "/collections/([^/]+)/tiles", {"GET"}, req ) ) { + BOOST_LOG_TRIVIAL(debug) << "GETTILESETS vector request"; + return get_tilesets(req, serv, false); + } + else if ( match_route( "/collections/([^/]+)/tiles/([^/]+)", {"GET"}, req ) ) { + BOOST_LOG_TRIVIAL(debug) << "GETTILESET vector request"; + return get_tileset(req, serv, false); } else if ( match_route( "/collections/([^/]+)/tiles/([^/]+)/([^/]+)/([^/]+)/([^/]+)", {"GET"}, req ) ) { BOOST_LOG_TRIVIAL(debug) << "GETTILE vector request"; return get_tile(req, serv, false); } + // Données raster + else if ( match_route( "/collections/([^/]+)/styles", {"GET"}, req ) ) { + BOOST_LOG_TRIVIAL(debug) << "GETSTYLES map request"; + return get_styles(req, serv); + } + else if ( match_route( "/collections/([^/]+)/styles/([^/]+)/map/tiles", {"GET"}, req ) ) { + BOOST_LOG_TRIVIAL(debug) << "GETTILESETS map request"; + return get_tilesets(req, serv, true); + } + else if ( match_route( "/collections/([^/]+)/styles/([^/]+)/map/tiles/([^/]+)", {"GET"}, req ) ) { + BOOST_LOG_TRIVIAL(debug) << "GETTILESET map request"; + return get_tileset(req, serv, true); + } else if ( match_route( "/collections/([^/]+)/styles/([^/]+)/map/tiles/([^/]+)/([^/]+)/([^/]+)/([^/]+)", {"GET"}, req ) ) { BOOST_LOG_TRIVIAL(debug) << "GETTILE map request"; return get_tile(req, serv, true); + } + else if ( match_route( "/collections/([^/]+)/styles/([^/]+)/map/tiles/([^/]+)/([^/]+)/([^/]+)/([^/]+)/info", {"GET"}, req ) ) { + BOOST_LOG_TRIVIAL(debug) << "GETFEATUREINFO request"; + return get_feature_info(req, serv); } else { throw TilesException::get_error_message("ResourceNotFound", "Unknown tiles request path", 404); } diff --git a/src/services/tiles/Service.h b/src/services/tiles/Service.h index 63eb990..0c5d993 100644 --- a/src/services/tiles/Service.h +++ b/src/services/tiles/Service.h @@ -59,16 +59,24 @@ class TilesService : public Service { private: + DataStream* get_conformance ( Request* req, Rok4Server* serv ); + DataStream* get_landing_page ( Request* req, Rok4Server* serv ); /** * \todo Gérer la pagination * \todo Filtrer selon la bbox */ DataStream* get_capabilities ( Request* req, Rok4Server* serv ); + DataStream* get_tilematrixsets ( Request* req, Rok4Server* serv ); + DataStream* get_tilematrixset ( Request* req, Rok4Server* serv ); DataStream* get_tiles ( Request* req, Rok4Server* serv ); DataStream* get_feature_info ( Request* req, Rok4Server* serv ); + DataStream* get_styles ( Request* req, Rok4Server* serv); + DataStream* get_tilesets ( Request* req, Rok4Server* serv, bool is_map_request ); + DataStream* get_tileset ( Request* req, Rok4Server* serv, bool is_map_request ); DataStream* get_tile ( Request* req, Rok4Server* serv, bool is_map_request ); Metadata* metadata; + bool reprojection; std::string cache_getcapabilities; @@ -95,6 +103,16 @@ class TilesService : public Service { cache_mtx.unlock(); }; + /** + * \~french + * \brief La reprojection est-elle activée + * \~english + * \brief Is reprojection enabled + */ + bool reprojection_enabled() { + return reprojection; + }; + /** * \~french * \brief Destructeur diff --git a/src/services/tiles/getcapabilities.cpp b/src/services/tiles/getcapabilities.cpp index c924ee2..4d0312f 100644 --- a/src/services/tiles/getcapabilities.cpp +++ b/src/services/tiles/getcapabilities.cpp @@ -49,10 +49,76 @@ #include "services/tiles/Service.h" #include "Rok4Server.h" + +DataStream* TilesService::get_landing_page ( Request* req, Rok4Server* serv ) { + std::string f = req->get_query_param("f"); + if (f != "" && f != "application/json" && f != "json") { + throw TilesException::get_error_message("InvalidParameter", "Format unknown", 400); + } + + std::vector links; + + links.push_back(json11::Json::object { + { "href", endpoint_uri}, + { "rel", "self"}, + { "type", "application/json"}, + { "title", "this document"} + }); + + links.push_back(json11::Json::object { + { "href", endpoint_uri + "/collections?f=json"}, + { "rel", "data"}, + { "type", "application/json"}, + { "title", "Information about the collections"} + }); + + links.push_back(json11::Json::object { + { "href", endpoint_uri + "/conformance?f=json"}, + { "rel", "data"}, + { "type", "application/json"}, + { "title", "OGC API conformance classes implemented by this service"} + }); + + json11::Json::object res = json11::Json::object { + { "title", title }, + { "description", abstract }, + { "links", links } + }; + + return new MessageDataStream ( json11::Json{ res }.dump(), "application/json", 200 ); +} + +DataStream* TilesService::get_conformance ( Request* req, Rok4Server* serv ) { + std::string f = req->get_query_param("f"); + if (f != "" && f != "application/json" && f != "json") { + throw TilesException::get_error_message("InvalidParameter", "Format unknown", 400); + } + + json11::Json::object res = json11::Json::object { + { "conformsTo", json11::Json::array{ + "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/core", + "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/json", + "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/oas30", + "http://www.opengis.net/spec/ogcapi-common-2/1.0/conf/collections", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/core", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tileset", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tilesets-list", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/geodata-tilesets", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/dataset-tilesets", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/jpeg", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/png", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/mvt", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tiff" + } } + }; + + return new MessageDataStream ( json11::Json{ res }.dump(), "application/json", 200 ); +} + DataStream* TilesService::get_capabilities ( Request* req, Rok4Server* serv ) { std::string f = req->get_query_param("f"); - if (f != "" && f != "application/json") { + if (f != "" && f != "application/json" && f != "json") { throw TilesException::get_error_message("InvalidParameter", "Format unknown", 400); } @@ -62,7 +128,7 @@ DataStream* TilesService::get_capabilities ( Request* req, Rok4Server* serv ) { std::vector links; links.push_back(json11::Json::object { - { "href", endpoint_uri + "/collections?f=application/json"}, + { "href", endpoint_uri + "/collections?f=json"}, { "rel", "self"}, { "type", "application/json"}, { "title", "this document"} @@ -95,12 +161,10 @@ DataStream* TilesService::get_capabilities ( Request* req, Rok4Server* serv ) { return new MessageDataStream ( cache_getcapabilities, "application/json", 200 ); } - - DataStream* TilesService::get_tiles ( Request* req, Rok4Server* serv ) { std::string f = req->get_query_param("f"); - if (f != "" && f != "application/json") { + if (f != "" && f != "application/json" && f != "json") { throw TilesException::get_error_message("InvalidParameter", "Format unknown", 400); } @@ -117,4 +181,154 @@ DataStream* TilesService::get_tiles ( Request* req, Rok4Server* serv ) { } return new MessageDataStream ( json11::Json{ layer->to_json_tiles(this) }.dump(), "application/json", 200 ); +} + +DataStream* TilesService::get_styles ( Request* req, Rok4Server* serv ) { + + std::string f = req->get_query_param("f"); + if (f != "" && f != "application/json" && f != "json") { + throw TilesException::get_error_message("InvalidParameter", "Format unknown", 400); + } + + // La couche + std::string str_layer = req->path_params.at(0); + if ( contain_chars(str_layer, "\"")) { + BOOST_LOG_TRIVIAL(warning) << "Forbidden char detected in TILES layer: " << str_layer ; + throw TilesException::get_error_message("ResourceNotFound", "Layer unknown", 404); + } + + Layer* layer = serv->get_server_configuration()->get_layer(str_layer); + if ( layer == NULL || ! layer->is_tiles_enabled() ) { + throw TilesException::get_error_message("ResourceNotFound", "Layer "+str_layer+" unknown", 404); + } + + if (! Rok4Format::is_raster(layer->get_pyramid()->get_format())) { + throw TilesException::get_error_message("InvalidParameter", "Vector dataset have to be requested without style", 400); + } + + return new MessageDataStream ( json11::Json{ layer->to_json_styles(this) }.dump(), "application/json", 200 ); +} + + +DataStream* TilesService::get_tilesets ( Request* req, Rok4Server* serv, bool is_map_request ) { + + std::string f = req->get_query_param("f"); + if (f != "" && f != "application/json" && f != "json") { + throw TilesException::get_error_message("InvalidParameter", "Format unknown", 400); + } + + // La couche + std::string str_layer = req->path_params.at(0); + if ( contain_chars(str_layer, "\"")) { + BOOST_LOG_TRIVIAL(warning) << "Forbidden char detected in TILES layer: " << str_layer ; + throw TilesException::get_error_message("ResourceNotFound", "Layer unknown", 404); + } + + Layer* layer = serv->get_server_configuration()->get_layer(str_layer); + if ( layer == NULL || ! layer->is_tiles_enabled() ) { + throw TilesException::get_error_message("ResourceNotFound", "Layer "+str_layer+" unknown", 404); + } + + bool is_raster_data = Rok4Format::is_raster(layer->get_pyramid()->get_format()); + + Style* style = NULL; + + if (is_map_request) { + // Map tiles request -> pour les pyramides raster + if (! is_raster_data) { + throw TilesException::get_error_message("InvalidParameter", "Vector dataset have to be requested without style", 400); + } + + std::string str_style = req->path_params.at(1); + + // Traitement du style + if ( contain_chars(str_style, "\"")) { + BOOST_LOG_TRIVIAL(warning) << "Forbidden char detected in TILES style: " << str_layer ; + throw TilesException::get_error_message("InvalidParameter", "Style unknown", 400); + } + + style = layer->get_style_by_identifier(str_style); + + if (style == NULL) { + throw TilesException::get_error_message("InvalidParameter", "Style " + str_style + " unknown", 400); + } + + } else { + // Tiles request -> pour les pyramides vecteur + if (is_raster_data) { + throw TilesException::get_error_message("InvalidParameter", "Raster dataset have to be requested with style", 400); + } + } + + return new MessageDataStream ( json11::Json{ layer->to_json_tilesets(this, style) }.dump(), "application/json", 200 ); +} + + +DataStream* TilesService::get_tileset ( Request* req, Rok4Server* serv, bool is_map_request ) { + + std::string f = req->get_query_param("f"); + if (f != "" && f != "application/json" && f != "json") { + throw TilesException::get_error_message("InvalidParameter", "Format unknown", 400); + } + + // La couche + std::string str_layer = req->path_params.at(0); + if ( contain_chars(str_layer, "\"")) { + BOOST_LOG_TRIVIAL(warning) << "Forbidden char detected in TILES layer: " << str_layer ; + throw TilesException::get_error_message("ResourceNotFound", "Layer unknown", 404); + } + + Layer* layer = serv->get_server_configuration()->get_layer(str_layer); + if ( layer == NULL || ! layer->is_tiles_enabled() ) { + throw TilesException::get_error_message("ResourceNotFound", "Layer "+str_layer+" unknown", 404); + } + + bool is_raster_data = Rok4Format::is_raster(layer->get_pyramid()->get_format()); + + std::string str_tms; + Style* style = NULL; + + if (is_map_request) { + // Map tiles request -> pour les pyramides raster + if (! is_raster_data) { + throw TilesException::get_error_message("InvalidParameter", "Vector dataset have to be requested without style", 400); + } + + str_tms = req->path_params.at(2); + + std::string str_style = req->path_params.at(1); + + // Traitement du style + if ( contain_chars(str_style, "\"")) { + BOOST_LOG_TRIVIAL(warning) << "Forbidden char detected in TILES style: " << str_layer ; + throw TilesException::get_error_message("InvalidParameter", "Style unknown", 400); + } + + style = layer->get_style_by_identifier(str_style); + + if (style == NULL) { + throw TilesException::get_error_message("InvalidParameter", "Style " + str_style + " unknown", 400); + } + + } else { + // Tiles request -> pour les pyramides vecteur + if (is_raster_data) { + throw TilesException::get_error_message("InvalidParameter", "Raster dataset have to be requested with style", 400); + } + + str_tms = req->path_params.at(1); + } + + // Le tile matrix set + if (contain_chars(str_tms, "\"")) { + BOOST_LOG_TRIVIAL(warning) << "Forbidden char detected in TILES tile matrix set: " << str_tms; + throw TilesException::get_error_message("InvalidParameter", "Tile matrix set unknown", 400); + } + + TileMatrixSetInfos* tmsi = layer->get_tilematrixset(str_tms); + if (tmsi == NULL || (! reprojection && tmsi->tms->get_id() != layer->get_pyramid()->get_tms()->get_id())) { + throw TilesException::get_error_message("InvalidParameter", "Tile matrix set " + str_tms + " unknown", 400); + } + + return new MessageDataStream ( json11::Json{ layer->to_json_tileset(this, style, tmsi) }.dump(), "application/json", 200 ); } \ No newline at end of file diff --git a/src/services/tiles/gettile.cpp b/src/services/tiles/gettile.cpp index 3696cc6..be9bd40 100644 --- a/src/services/tiles/gettile.cpp +++ b/src/services/tiles/gettile.cpp @@ -63,7 +63,6 @@ DataStream* TilesService::get_tile ( Request* req, Rok4Server* serv, bool is_map_request ) { - // La couche std::string str_layer = req->path_params.at(0); if ( contain_chars(str_layer, "\"")) { @@ -150,8 +149,8 @@ DataStream* TilesService::get_tile ( Request* req, Rok4Server* serv, bool is_map throw TilesException::get_error_message("InvalidParameter", "Tile matrix set unknown", 400); } - TileMatrixSet* tms = layer->get_tilematrixset(str_tms); - if (tms == NULL) { + TileMatrixSetInfos* tmsi = layer->get_tilematrixset(str_tms); + if (tmsi == NULL) { throw TilesException::get_error_message("InvalidParameter", "Tile matrix set " + str_tms + " unknown", 400); } @@ -161,7 +160,7 @@ DataStream* TilesService::get_tile ( Request* req, Rok4Server* serv, bool is_map throw TilesException::get_error_message("InvalidParameter", "Tile matrix unknown", 400); } - TileMatrix* tm = tms->get_tm(str_tm); + TileMatrix* tm = tmsi->tms->get_tm(str_tm); if (tm == NULL) { throw TilesException::get_error_message("InvalidParameter", "Tile matrix " + str_tm + " unknown", 400); } @@ -178,7 +177,7 @@ DataStream* TilesService::get_tile ( Request* req, Rok4Server* serv, bool is_map throw TilesException::get_error_message("InvalidParameter", "Invalid row value", 400); } - TileMatrixLimits* tml = layer->get_tilematrix_limits(tms, tm); + TileMatrixLimits* tml = layer->get_tilematrix_limits(tmsi->tms, tm); if (tml == NULL) { // On est hors niveau -> erreur throw TilesException::get_error_message("ResourceNotFound", "Level out of limits", 404); @@ -191,7 +190,7 @@ DataStream* TilesService::get_tile ( Request* req, Rok4Server* serv, bool is_map // Traitement de la requête - if (tms->get_id() == layer->get_pyramid()->get_tms()->get_id()) { + if (tmsi->tms->get_id() == layer->get_pyramid()->get_tms()->get_id()) { // TMS d'interrogation natif Level* level = layer->get_pyramid()->get_level(tm->get_id()); @@ -205,13 +204,13 @@ DataStream* TilesService::get_tile ( Request* req, Rok4Server* serv, bool is_map } else { return new DataStreamFromDataSource(d); } - } else { + } else if (reprojection) { // TMS d'interrogation à la demande BoundingBox bbox = tm->tile_indices_to_bbox(column, row); int height = tm->get_tile_height(); int width = tm->get_tile_width(); - CRS* crs = tms->get_crs(); + CRS* crs = tmsi->tms->get_crs(); bbox.crs = crs->get_request_code(); bool crs_equals = serv->get_services_configuration()->are_crs_equals(layer->get_pyramid()->get_tms()->get_crs()->get_proj_code(), crs->get_proj_code()); @@ -265,6 +264,9 @@ DataStream* TilesService::get_tile ( Request* req, Rok4Server* serv, bool is_map return new JPEGEncoder(image, 90); } } + } else { + // TMS d'interrogation à la demande mais reprojection non activée + throw TilesException::get_error_message("InvalidParameter", "Tile matrix set " + str_tms + " unknown", 400); } BOOST_LOG_TRIVIAL(error) << "On ne devrait pas passer par là"; diff --git a/src/services/tiles/tilematrixsets.cpp b/src/services/tiles/tilematrixsets.cpp new file mode 100644 index 0000000..7131306 --- /dev/null +++ b/src/services/tiles/tilematrixsets.cpp @@ -0,0 +1,131 @@ +/* + * Copyright © (2011-2013) Institut national de l'information + * géographique et forestière + * + * Géoportail SAV + * + * This software is a computer program whose purpose is to publish geographic + * data using OGC WMS and WMTS protocol. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * + * knowledge of the CeCILL-C license and that you accept its terms. + */ + +/** + * \file services/tiles/getcapabilities.cpp + ** \~french + * \brief Implémentation de la classe TilesService + ** \~english + * \brief Implements classe TilesService + */ + +#include + +#include "services/tiles/Exception.h" +#include "services/tiles/Service.h" +#include "Rok4Server.h" + +DataStream* TilesService::get_tilematrixsets ( Request* req, Rok4Server* serv ) { + + std::string f = req->get_query_param("f"); + if (f != "" && f != "application/json" && f != "json") { + throw TilesException::get_error_message("InvalidParameter", "Format unknown", 400); + } + + json11::Json::object res = json11::Json::object {}; + + std::vector tmss; + + for(auto const& t: TmsBook::get_book()) { + tmss.push_back(json11::Json::object { + { "id", t.second->get_id()}, + { "title", t.second->get_title()}, + { "crs", t.second->get_crs()->get_url()}, + { "links", json11::Json::array { + json11::Json::object { + { "href", endpoint_uri + "/tileMatrixSets/" + t.second->get_id() + "?f=json"}, + { "rel", "describedby"}, + { "type", "application/json"}, + { "title", t.second->get_id() + " definition as application/json"} + } + }} + }); + } + + res["tileMatrixSets"] = tmss; + + return new MessageDataStream ( json11::Json{ res }.dump(), "application/json", 200 ); +} + + +DataStream* TilesService::get_tilematrixset ( Request* req, Rok4Server* serv ) { + + std::string f = req->get_query_param("f"); + if (f != "" && f != "application/json" && f != "json") { + throw TilesException::get_error_message("InvalidParameter", "Format unknown", 400); + } + + // Le TMS + std::string str_tms = req->path_params.at(0); + if ( contain_chars(str_tms, "\"")) { + BOOST_LOG_TRIVIAL(warning) << "Forbidden char detected in TILES tms: " << str_tms ; + throw TilesException::get_error_message("ResourceNotFound", "Tile matrix set unknown", 404); + } + + TileMatrixSet* tms = TmsBook::get_tms(str_tms); + if ( tms == NULL) { + throw TilesException::get_error_message("ResourceNotFound", "Tile matrix set "+str_tms+" unknown", 404); + } + + json11::Json::object res = json11::Json::object { + { "id", tms->get_id()}, + { "title", tms->get_title()}, + { "description", tms->get_abstract()}, + { "keywords", *(tms->get_keywords())}, + { "crs", tms->get_crs()->get_url()}, + }; + + std::vector tile_matrices; + + for(TileMatrix* tm: tms->get_ordered_tm(false)) { + tile_matrices.push_back(json11::Json::object { + { "id", tm->get_id()}, + { "cellSize", tm->get_res()}, + { "cornerOfOrigin", "topLeft"}, + { "pointOfOrigin", json11::Json::array{ tm->get_x0(), tm->get_y0()}}, + { "tileWidth", tm->get_tile_width()}, + { "tileHeight", tm->get_tile_height()}, + { "matrixHeight", int(tm->get_matrix_width())}, + { "matrixWidth", int(tm->get_matrix_height())} + }); + } + + res["tileMatrices"] = tile_matrices; + + return new MessageDataStream ( json11::Json{ res }.dump(), "application/json", 200 ); +} + diff --git a/src/services/wms/Service.cpp b/src/services/wms/Service.cpp index a23eb28..8bb78d5 100644 --- a/src/services/wms/Service.cpp +++ b/src/services/wms/Service.cpp @@ -394,6 +394,9 @@ bool WmsService::is_available_crs(CRS* c) { return is_available_crs(c->get_request_code()); } bool WmsService::is_available_crs(std::string c) { + if (! reprojection) { + return false; + } for ( unsigned int k = 0; k < crss.size(); k++ ) { if ( crss.at (k)->cmp_request_code ( c ) ) { return true; diff --git a/src/services/wms/getcapabilities.cpp b/src/services/wms/getcapabilities.cpp index 35000a8..e627f27 100644 --- a/src/services/wms/getcapabilities.cpp +++ b/src/services/wms/getcapabilities.cpp @@ -67,10 +67,6 @@ DataStream* WmsService::get_capabilities ( Request* req, Rok4Server* serv ) { ServicesConfiguration* services = serv->get_services_configuration(); - // On va mémoriser les TMS utilisés, avec les niveaux du haut et du bas - // La clé est un triplet : nom du TMS, niveau du haut, niveau du bas - std::map< std::string, WmtsTmsInfos> used_tms_list; - ptree tree; ptree& root = tree.add("WMS_Capabilities", ""); diff --git a/src/services/wms/getfeatureinfo.cpp b/src/services/wms/getfeatureinfo.cpp index e9b02af..05083a5 100644 --- a/src/services/wms/getfeatureinfo.cpp +++ b/src/services/wms/getfeatureinfo.cpp @@ -159,7 +159,7 @@ DataStream* WmsService::get_feature_info ( Request* req, Rok4Server* serv ) { if (! is_available_crs(str_crs) ) { for ( unsigned int i = 0; i < layers.size() ; i++ ) { bool crs_equals = serv->get_services_configuration()->are_crs_equals(crs->get_request_code(), layers.at(i)->get_pyramid()->get_tms()->get_crs()->get_request_code()); - if (! crs_equals && ! layers.at ( i )->is_available_crs_wms(str_crs) ) + if (! crs_equals && ! layers.at ( i )->is_available_crs(str_crs) ) throw WmsException::get_error_message("CRS is not available for the layer " + layers.at ( i )->get_id(), "InvalidParameterValue", 400); } } diff --git a/src/services/wms/getmap.cpp b/src/services/wms/getmap.cpp index df7ad75..04afd0a 100644 --- a/src/services/wms/getmap.cpp +++ b/src/services/wms/getmap.cpp @@ -135,8 +135,9 @@ DataStream* WmsService::get_map ( Request* req, Rok4Server* serv ) { if (! is_available_crs(str_crs) ) { for ( unsigned int i = 0; i < layers.size() ; i++ ) { bool crs_equals = serv->get_services_configuration()->are_crs_equals(crs->get_request_code(), layers.at(i)->get_pyramid()->get_tms()->get_crs()->get_request_code()); - if (! crs_equals && ! layers.at ( i )->is_available_crs_wms(str_crs) ) + if (! crs_equals && (! reprojection || ! layers.at ( i )->is_available_crs(str_crs)) ) { throw WmsException::get_error_message("CRS is not available for the layer " + layers.at ( i )->get_id(), "InvalidParameterValue", 400); + } } } diff --git a/src/services/wmts/getcapabilities.cpp b/src/services/wmts/getcapabilities.cpp index 834d42d..6d10b61 100644 --- a/src/services/wmts/getcapabilities.cpp +++ b/src/services/wmts/getcapabilities.cpp @@ -69,7 +69,7 @@ DataStream* WmtsService::get_capabilities ( Request* req, Rok4Server* serv ) { // On va mémoriser les TMS utilisés, avec les niveaux du haut et du bas // La clé est un triplet : nom du TMS, niveau du haut, niveau du bas - std::map< std::string, WmtsTmsInfos> used_tms_list; + std::map< std::string, TileMatrixSetInfos*> used_tms_list; ptree tree; @@ -152,14 +152,14 @@ DataStream* WmtsService::get_capabilities ( Request* req, Rok4Server* serv ) { layers_iterator->second->add_node_wmts(contents_node, this, req->is_inspire(default_inspire), &used_tms_list); } - std::map::iterator tms_iterator ( used_tms_list.begin() ), tms_end ( used_tms_list.end() ); + std::map::iterator tms_iterator ( used_tms_list.begin() ), tms_end ( used_tms_list.end() ); for ( ; tms_iterator!=tms_end; ++tms_iterator ) { ptree& tms_node = contents_node.add("TileMatrixSet", ""); tms_node.add( "ows:Identifier", tms_iterator->first ); - TileMatrixSet* tms = tms_iterator->second.tms; + TileMatrixSet* tms = tms_iterator->second->tms; if ( ! ( tms->get_title().empty() ) ) { tms_node.add ( "ows:Title", tms->get_title() ); @@ -180,13 +180,13 @@ DataStream* WmtsService::get_capabilities ( Request* req, Rok4Server* serv ) { // TileMatrix bool keep = false; - if (tms_iterator->second.top_level == "") { + if (tms_iterator->second->top_level == "") { // On est sur un TMS d'origine, on l'exporte en entier keep = true; } for (TileMatrix* tm : tms->get_ordered_tm(false)) { - if (! keep && tm->get_id() != tms_iterator->second.top_level) { + if (! keep && tm->get_id() != tms_iterator->second->top_level) { continue; } else { keep = true; @@ -206,10 +206,15 @@ DataStream* WmtsService::get_capabilities ( Request* req, Rok4Server* serv ) { tm_node.add( "MatrixWidth",Utils::int_to_string ( tm->get_matrix_width() ) ); tm_node.add( "MatrixHeight",Utils::int_to_string ( tm->get_matrix_height() ) ); - if (tm->get_id() == tms_iterator->second.bottom_level) { + if (tm->get_id() == tms_iterator->second->bottom_level) { break; } } + + if (tms_iterator->second->top_level == "") { + // On est sur un TMS d'origine, TileMatrixSetInfos créé pour cette occasion, on doit le nettoyer + delete tms_iterator->second; + } } std::stringstream ss; diff --git a/src/services/wmts/getfeatureinfo.cpp b/src/services/wmts/getfeatureinfo.cpp index aa76f54..72be18b 100644 --- a/src/services/wmts/getfeatureinfo.cpp +++ b/src/services/wmts/getfeatureinfo.cpp @@ -77,8 +77,8 @@ DataStream* WmtsService::get_feature_info ( Request* req, Rok4Server* serv ) { throw WmtsException::get_error_message("Tile matrix set unknown", "InvalidParameterValue", 400); } - TileMatrixSet* tms = layer->get_tilematrixset(str_tms); - if (tms == NULL) { + TileMatrixSetInfos* tmsi = layer->get_tilematrixset(str_tms); + if (tmsi == NULL) { throw WmtsException::get_error_message("Tile matrix set " + str_tms + " unknown", "InvalidParameterValue", 400); } @@ -91,7 +91,7 @@ DataStream* WmtsService::get_feature_info ( Request* req, Rok4Server* serv ) { throw WmtsException::get_error_message("Tile matrix unknown", "InvalidParameterValue", 400); } - TileMatrix* tm = tms->get_tm(str_tm); + TileMatrix* tm = tmsi->tms->get_tm(str_tm); if (tm == NULL) throw WmtsException::get_error_message("Tile matrix " + str_tm + " unknown", "InvalidParameterValue", 400); // La colonne @@ -108,7 +108,7 @@ DataStream* WmtsService::get_feature_info ( Request* req, Rok4Server* serv ) { if (sscanf(str_row.c_str(), "%d", &row) != 1) throw WmtsException::get_error_message("Invalid row value", "InvalidParameterValue", 400); - TileMatrixLimits* tml = layer->get_tilematrix_limits(tms, tm); + TileMatrixLimits* tml = layer->get_tilematrix_limits(tmsi->tms, tm); if (tml == NULL) { // On est hors niveau -> erreur throw WmtsException::get_error_message("No data found", "TileOutOfRange", 404); @@ -180,7 +180,7 @@ DataStream* WmtsService::get_feature_info ( Request* req, Rok4Server* serv ) { Level* level = layer->get_pyramid()->get_level(tm->get_id()); std::string gfi_type = layer->get_gfi_type(); - if (gfi_type.compare("PYRAMID") == 0 && tms->get_id() == layer->get_pyramid()->get_tms()->get_id() ) { + if (gfi_type.compare("PYRAMID") == 0 && tmsi->tms->get_id() == layer->get_pyramid()->get_tms()->get_id() ) { BOOST_LOG_TRIVIAL(debug) << "GFI sur pyramide dans le TMS natif"; Image* image = level->get_tile(column, row, 0, 0, 0, 0, true); @@ -247,12 +247,12 @@ DataStream* WmtsService::get_feature_info ( Request* req, Rok4Server* serv ) { query_params.emplace("INFO_FORMAT", info_format); query_params.emplace("FEATURE_COUNT", "1"); query_params.emplace("FORMAT", "image/tiff"); - query_params.emplace("CRS", tms->get_crs()->get_request_code()); + query_params.emplace("CRS", tmsi->tms->get_crs()->get_request_code()); query_params.emplace("WIDTH", std::to_string(width)); query_params.emplace("HEIGHT", std::to_string(height)); query_params.emplace("I", std::to_string(i)); query_params.emplace("J", std::to_string(j)); - query_params.emplace("BBOX", bbox.to_string(tms->get_crs()->is_lat_lon())); + query_params.emplace("BBOX", bbox.to_string(tmsi->tms->get_crs()->is_lat_lon())); std::map extra_query_params = layer->get_gfi_extra_params(); query_params.insert(extra_query_params.begin(), extra_query_params.end()); diff --git a/src/services/wmts/gettile.cpp b/src/services/wmts/gettile.cpp index b61b11d..6bba29b 100644 --- a/src/services/wmts/gettile.cpp +++ b/src/services/wmts/gettile.cpp @@ -86,8 +86,8 @@ DataStream* WmtsService::get_tile(Request* req, Rok4Server* serv) { throw WmtsException::get_error_message("Tile matrix set unknown", "InvalidParameterValue", 400); } - TileMatrixSet* tms = layer->get_tilematrixset(str_tms); - if (tms == NULL) { + TileMatrixSetInfos* tmsi = layer->get_tilematrixset(str_tms); + if (tmsi == NULL) { throw WmtsException::get_error_message("Tile matrix set " + str_tms + " unknown", "InvalidParameterValue", 400); } @@ -100,7 +100,7 @@ DataStream* WmtsService::get_tile(Request* req, Rok4Server* serv) { throw WmtsException::get_error_message("Tile matrix unknown", "InvalidParameterValue", 400); } - TileMatrix* tm = tms->get_tm(str_tm); + TileMatrix* tm = tmsi->tms->get_tm(str_tm); if (tm == NULL) throw WmtsException::get_error_message("Tile matrix " + str_tm + " unknown", "InvalidParameterValue", 400); // La colonne @@ -117,7 +117,7 @@ DataStream* WmtsService::get_tile(Request* req, Rok4Server* serv) { if (sscanf(str_row.c_str(), "%d", &row) != 1) throw WmtsException::get_error_message("Invalid row value", "InvalidParameterValue", 400); - TileMatrixLimits* tml = layer->get_tilematrix_limits(tms, tm); + TileMatrixLimits* tml = layer->get_tilematrix_limits(tmsi->tms, tm); if (tml == NULL) { // On est hors niveau -> erreur throw WmtsException::get_error_message("No data found", "Not Found", 404); @@ -159,7 +159,7 @@ DataStream* WmtsService::get_tile(Request* req, Rok4Server* serv) { // Traitement de la requête - if (tms->get_id() == layer->get_pyramid()->get_tms()->get_id()) { + if (tmsi->tms->get_id() == layer->get_pyramid()->get_tms()->get_id()) { // TMS d'interrogation natif Level* level = layer->get_pyramid()->get_level(tm->get_id()); @@ -173,13 +173,13 @@ DataStream* WmtsService::get_tile(Request* req, Rok4Server* serv) { } else { return new DataStreamFromDataSource(d); } - } else { + } else if (reprojection) { // TMS d'interrogation à la demande BoundingBox bbox = tm->tile_indices_to_bbox(column, row); int height = tm->get_tile_height(); int width = tm->get_tile_width(); - CRS* crs = tms->get_crs(); + CRS* crs = tmsi->tms->get_crs(); bbox.crs = crs->get_request_code(); bool crs_equals = serv->get_services_configuration()->are_crs_equals(layer->get_pyramid()->get_tms()->get_crs()->get_proj_code(), crs->get_proj_code()); @@ -279,7 +279,6 @@ DataStream* WmtsService::get_tile(Request* req, Rok4Server* serv) { } } } - - BOOST_LOG_TRIVIAL(error) << "On ne devrait pas passer par là"; - throw WmtsException::get_error_message("No data found", "Not Found", 404); + // TMS d'interrogation à la demande mais reprojection non activée + throw WmtsException::get_error_message("Tile matrix set " + str_tms + " unknown", "InvalidParameterValue", 400); } \ No newline at end of file