diff --git a/autotest/gdrivers/data/ogcapi/request_collections_blueMarble.http_data b/autotest/gdrivers/data/ogcapi/request_collections_blueMarble.http_data new file mode 100644 index 000000000000..000d0eb8d63d --- /dev/null +++ b/autotest/gdrivers/data/ogcapi/request_collections_blueMarble.http_data @@ -0,0 +1,195 @@ +HTTP/1.1 200 OK +Date: Wed, 14 Feb 2024 05:33:24 GMT +Server: Apache/2.4.52 (Ubuntu) +Expires: Tue, 11 Feb 2025 05:49:25 GMT +Access-Control-Allow-Origin: * +Vary: Accept,Accept-Encoding,Prefer +Content-Length: 7564 +Access-Control-Allow-Headers: x-requested-with, Content-Type, origin, authorization, acc$ +Age: 171838 +Keep-Alive: timeout=5, max=100 +Connection: Keep-Alive +Content-Type: application/json + +{ + "links" : [ + { + "rel" : "self", + "type" : "application/json", + "title" : "Information about the Blue Marble Next Generation (2004) data (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble?f=json" + }, + { + "rel" : "alternate", + "type" : "text/plain", + "title" : "Information about the Blue Marble Next Generation (2004) data (as ECON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble?f=econ" + }, + { + "rel" : "alternate", + "type" : "text/mapml", + "title" : "Information about the Blue Marble Next Generation (2004) data (as MapML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble?f=mapml" + }, + { + "rel" : "alternate", + "type" : "text/html", + "title" : "Information about the Blue Marble Next Generation (2004) data (as HTML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble?f=html" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/dggs-list", + "title" : "Discrete Global Grid Systems for Blue Marble Next Generation (2004)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/dggs" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/schema", + "type" : "application/json", + "title" : "Schema (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/schema?f=json" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/schema", + "type" : "text/plain", + "title" : "Schema (as ECON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/schema?f=econ" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/schema", + "type" : "text/html", + "title" : "Schema (as HTML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/schema?f=html" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/queryables", + "type" : "application/json", + "title" : "Queryables (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/queryables?f=json" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/queryables", + "type" : "text/plain", + "title" : "Queryables (as ECON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/queryables?f=econ" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/queryables", + "type" : "text/html", + "title" : "Queryables (as HTML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/queryables?f=html" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/coverage", + "type" : "image/png", + "title" : "Blue Marble Next Generation (2004) (as PNG; Note: requesting large extent may result in generalized data)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/coverage?f=png" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/coverage", + "type" : "image/tiff; application=geotiff", + "title" : "Blue Marble Next Generation (2004) (as GeoTIFF; Note: requesting large extent may result in generalized data)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/coverage?f=tif" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/coverage-domainset", + "type" : "application/json", + "title" : "Blue Marble Next Generation (2004) (domain set of the coverage for this collection)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/coverage/domainset?f=json" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/coverage-rangetype", + "type" : "application/json", + "title" : "Blue Marble Next Generation (2004) (range type of the coverage for this collection)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/coverage/rangetype?f=json" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/map", + "type" : "image/png", + "title" : "Default map (as PNG)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map.png" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/map", + "type" : "image/jpeg", + "title" : "Default map (as JPG)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map.jpg" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/map", + "type" : "image/tif", + "title" : "Default map (as GeoTIFF)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map.tif" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "application/json", + "title" : "Map tilesets available for this dataset (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles?f=json" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "text/plain", + "title" : "Map tilesets available for this dataset (as ECON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles?f=econ" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "text/html", + "title" : "Map tilesets available for this dataset (as HTML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles?f=html" + }, + { + "rel" : "styles", + "type" : "text/html", + "title" : "Styles for Blue Marble Next Generation (2004) (as HTML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles?f=html" + }, + { + "rel" : "styles", + "type" : "application/json", + "title" : "Styles for Blue Marble Next Generation (2004) (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles?f=json" + }, + { + "rel" : "styles", + "type" : "text/plain", + "title" : "Styles for Blue Marble Next Generation (2004) (as ECON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles?f=econ" + } + ], + "title" : "Blue Marble Next Generation (2004)", + "extent" : { + "spatial" : { + "bbox" : [ [ -180, -90, 180, 90 ] ], + "grid" : [ + { + "cellsCount" : 131072, + "resolution" : 0.0027465820312 + }, + { + "cellsCount" : 65536, + "resolution" : 0.0027465820312 + } + ] + }, + "temporal" : { + "interval" : [ [ "2004-01", "2004-12" ] ], + "grid" : { + "cellsCount" : 12, + "resolution" : "P1M" + } + } + }, + "crs" : [ + "http://www.opengis.net/def/crs/OGC/1.3/CRS84", + "http://www.opengis.net/def/crs/EPSG/0/4326", + "http://www.opengis.net/def/crs/EPSG/0/3857", + "http://www.opengis.net/def/crs/EPSG/0/3395" + ], + "storageCrs" : "http://www.opengis.net/def/crs/OGC/1.3/CRS84", + "id" : "blueMarble", + "dataType" : "map", + "attribution" : "NASA Earth Observatory", + "minScaleDenominator" : 1091957.5469310893677, + "minCellSize" : 0.0027465820312 +} diff --git a/autotest/gdrivers/data/ogcapi/request_collections_blueMarble_map_tiles_WorldMercatorWGS84Quad_0_0_0.jpg.http_data b/autotest/gdrivers/data/ogcapi/request_collections_blueMarble_map_tiles_WorldMercatorWGS84Quad_0_0_0.jpg.http_data new file mode 100644 index 000000000000..0760475e8302 Binary files /dev/null and b/autotest/gdrivers/data/ogcapi/request_collections_blueMarble_map_tiles_WorldMercatorWGS84Quad_0_0_0.jpg.http_data differ diff --git a/autotest/gdrivers/data/ogcapi/request_collections_blueMarble_map_tiles_WorldMercatorWGS84Quad_0_0_0.png.http_data b/autotest/gdrivers/data/ogcapi/request_collections_blueMarble_map_tiles_WorldMercatorWGS84Quad_0_0_0.png.http_data new file mode 100644 index 000000000000..ff0efadec872 Binary files /dev/null and b/autotest/gdrivers/data/ogcapi/request_collections_blueMarble_map_tiles_WorldMercatorWGS84Quad_0_0_0.png.http_data differ diff --git a/autotest/gdrivers/data/ogcapi/request_collections_blueMarble_map_tiles_WorldMercatorWGS84Quad_0_0_0.tif.http_data b/autotest/gdrivers/data/ogcapi/request_collections_blueMarble_map_tiles_WorldMercatorWGS84Quad_0_0_0.tif.http_data new file mode 100644 index 000000000000..70944fa0c06f Binary files /dev/null and b/autotest/gdrivers/data/ogcapi/request_collections_blueMarble_map_tiles_WorldMercatorWGS84Quad_0_0_0.tif.http_data differ diff --git a/autotest/gdrivers/data/ogcapi/request_collections_blueMarble_map_tiles_WorldMercatorWGS84Quad_f_json.http_data b/autotest/gdrivers/data/ogcapi/request_collections_blueMarble_map_tiles_WorldMercatorWGS84Quad_f_json.http_data new file mode 100644 index 000000000000..d044f8585f0a --- /dev/null +++ b/autotest/gdrivers/data/ogcapi/request_collections_blueMarble_map_tiles_WorldMercatorWGS84Quad_f_json.http_data @@ -0,0 +1,138 @@ +HTTP/1.1 200 OK +Date: Wed, 14 Feb 2024 05:33:25 GMT +Server: Apache/2.4.52 (Ubuntu) +Expires: Thu, 13 Feb 2025 04:01:39 GMT +Access-Control-Allow-Origin: * +Vary: Accept,Accept-Encoding,Prefer +Content-Length: 5775 +Access-Control-Allow-Headers: x-requested-with, Content-Type, origin, authorization, acc$ +Age: 5505 +Keep-Alive: timeout=5, max=100 +Connection: Keep-Alive +Content-Type: application/json + +{ + "title" : "Blue Marble Next Generation (2004)", + "tileMatrixSetURI" : "http://www.opengis.net/def/tilematrixset/OGC/1.0/WorldMercatorWGS84Quad", + "crs" : "http://www.opengis.net/def/crs/EPSG/0/3395", + "dataType" : "map", + "tileMatrixSetLimits" : [ + { "tileMatrix" : "0", "minTileRow" : 0, "maxTileRow" : 0, "minTileCol" : 0, "maxTileCol" : 0 }, + { "tileMatrix" : "1", "minTileRow" : 0, "maxTileRow" : 1, "minTileCol" : 0, "maxTileCol" : 1 }, + { "tileMatrix" : "2", "minTileRow" : 0, "maxTileRow" : 3, "minTileCol" : 0, "maxTileCol" : 3 }, + { "tileMatrix" : "3", "minTileRow" : 0, "maxTileRow" : 7, "minTileCol" : 0, "maxTileCol" : 7 }, + { "tileMatrix" : "4", "minTileRow" : 0, "maxTileRow" : 15, "minTileCol" : 0, "maxTileCol" : 15 }, + { "tileMatrix" : "5", "minTileRow" : 0, "maxTileRow" : 31, "minTileCol" : 0, "maxTileCol" : 31 }, + { "tileMatrix" : "6", "minTileRow" : 0, "maxTileRow" : 63, "minTileCol" : 0, "maxTileCol" : 63 }, + { "tileMatrix" : "7", "minTileRow" : 0, "maxTileRow" : 127, "minTileCol" : 0, "maxTileCol" : 127 }, + { "tileMatrix" : "8", "minTileRow" : 0, "maxTileRow" : 255, "minTileCol" : 0, "maxTileCol" : 255 }, + { "tileMatrix" : "9", "minTileRow" : 0, "maxTileRow" : 511, "minTileCol" : 0, "maxTileCol" : 511 } + ], + "links" : [ + { + "rel" : "self", + "type" : "application/json", + "title" : "The JSON representation of the WorldMercatorWGS84Quad map tileset for blueMarble", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/WorldMercatorWGS84Quad?f=json" + }, + { + "rel" : "alternate", + "type" : "text/plain", + "title" : "The ECON representation of the WorldMercatorWGS84Quad map tileset for blueMarble", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/WorldMercatorWGS84Quad?f=econ" + }, + { + "rel" : "alternate", + "type" : "text/html", + "title" : "The HTML representation of the WorldMercatorWGS84Quad map tileset for blueMarble", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/WorldMercatorWGS84Quad?=html" + }, + { + "rel" : "alternate", + "type" : "application/json+tile", + "title" : "The TileJSON representation of the WorldMercatorWGS84Quad map tileset for blueMarble", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/WorldMercatorWGS84Quad?f=tilejson" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme", + "type" : "application/json", + "title" : "WorldMercatorWGS84QuadTileMatrixSet definition (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/tileMatrixSets/WorldMercatorWGS84Quad" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/geodata", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble" + }, + { + "rel" : "item", + "type" : "application/vnd.gnosis-map-tile", + "title" : "WorldMercatorWGS84Quad map tiles for blueMarble (as GNOSIS Map Tiles)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/WorldMercatorWGS84Quad/{tileMatrix}/{tileRow}/{tileCol}.gmt", + "templated" : true + }, + { + "rel" : "item", + "type" : "image/png", + "title" : "WorldMercatorWGS84Quad map tiles for blueMarble (as PNG)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/WorldMercatorWGS84Quad/{tileMatrix}/{tileRow}/{tileCol}.png", + "templated" : true + }, + { + "rel" : "item", + "type" : "image/jpeg", + "title" : "WorldMercatorWGS84Quad map tiles for blueMarble (as JPG)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/WorldMercatorWGS84Quad/{tileMatrix}/{tileRow}/{tileCol}.jpg", + "templated" : true + }, + { + "rel" : "item", + "type" : "image/tiff; application=geotiff", + "title" : "WorldMercatorWGS84Quad map tiles for blueMarble (as GeoTIFF)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/WorldMercatorWGS84Quad/{tileMatrix}/{tileRow}/{tileCol}.tif", + "templated" : true + } + ], + "layers" : [ + { + "id" : "blueMarble", + "dataType" : "map", + "minScaleDenominator" : 1091957.5469310893677, + "minCellSize" : 305.748113140705, + "maxTileMatrix" : "9", + "links" : [ + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/geodata", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble" + } + ], + "propertiesSchema" : { + "$schema" : "https://json-schema.org/draft/2020-12/schema", + "type" : "object", + "properties" : { "a" : { + "title" : "Alpha channel", + "type" : "integer", + "x-ogc-property-seq" : 4 + }, "b" : { + "title" : "Blue channel", + "type" : "integer", + "x-ogc-property-seq" : 3 + }, "g" : { + "title" : "Green channel", + "type" : "integer", + "x-ogc-property-seq" : 2 + }, "r" : { + "title" : "Red channel", + "type" : "integer", + "x-ogc-property-seq" : 1 + } } + } + } + ], + "centerPoint" : { + "coordinates" : [ 0, 0 ], + "tileMatrix" : "4", + "scaleDenominator" : 34942641.501794859767, + "cellSize" : 9783.9396205025605, + "crs" : "http://www.opengis.net/def/crs/OGC/1.3/CRS84" + } +} diff --git a/autotest/gdrivers/data/ogcapi/request_collections_blueMarble_map_tiles_f_json.http_data b/autotest/gdrivers/data/ogcapi/request_collections_blueMarble_map_tiles_f_json.http_data new file mode 100644 index 000000000000..1694f8e8e181 --- /dev/null +++ b/autotest/gdrivers/data/ogcapi/request_collections_blueMarble_map_tiles_f_json.http_data @@ -0,0 +1,433 @@ +HTTP/1.1 200 OK +Date: Wed, 14 Feb 2024 05:33:25 GMT +Server: Apache/2.4.52 (Ubuntu) +Expires: Thu, 13 Feb 2025 04:01:39 GMT +Access-Control-Allow-Origin: * +Vary: Accept,Accept-Encoding,Prefer +Content-Length: 20038 +Access-Control-Allow-Headers: x-requested-with, Content-Type, origin, authorization, acc$ +Age: 5505 +Keep-Alive: timeout=5, max=100 +Connection: Keep-Alive +Content-Type: application/json + +{ + "links" : [ + { + "rel" : "self", + "type" : "application/json", + "title" : "The JSON representation of the available map tilesets for blueMarble", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/?f=json" + }, + { + "rel" : "alternate", + "type" : "text/plain", + "title" : "The ECON representation of the available map tilesets for blueMarble", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/?f=econ" + }, + { + "rel" : "alternate", + "type" : "text/html", + "title" : "The HTML representation of the available map tilesets for blueMarble", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/?f=html" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "application/json", + "title" : "Map tilesets for blueMarble (default style) (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles/default/map/tiles?f=json" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "text/plain", + "title" : "Map tilesets for blueMarble (default style) (as ECON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles/default/map/tiles?f=econ" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "text/html", + "title" : "Map tilesets for blueMarble (default style) (as HTML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles/default/map/tiles?f=html" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "application/json", + "title" : "Map tilesets for blueMarble (evi style) (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles/evi/map/tiles?f=json" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "text/plain", + "title" : "Map tilesets for blueMarble (evi style) (as ECON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles/evi/map/tiles?f=econ" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "text/html", + "title" : "Map tilesets for blueMarble (evi style) (as HTML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles/evi/map/tiles?f=html" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "application/json", + "title" : "Map tilesets for blueMarble (nir style) (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles/nir/map/tiles?f=json" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "text/plain", + "title" : "Map tilesets for blueMarble (nir style) (as ECON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles/nir/map/tiles?f=econ" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "text/html", + "title" : "Map tilesets for blueMarble (nir style) (as HTML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles/nir/map/tiles?f=html" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "application/json", + "title" : "Map tilesets for blueMarble (scl style) (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles/scl/map/tiles?f=json" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "text/plain", + "title" : "Map tilesets for blueMarble (scl style) (as ECON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles/scl/map/tiles?f=econ" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "text/html", + "title" : "Map tilesets for blueMarble (scl style) (as HTML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles/scl/map/tiles?f=html" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "application/json", + "title" : "Map tilesets for blueMarble (ndvi style) (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles/ndvi/map/tiles?f=json" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "text/plain", + "title" : "Map tilesets for blueMarble (ndvi style) (as ECON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles/ndvi/map/tiles?f=econ" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "text/html", + "title" : "Map tilesets for blueMarble (ndvi style) (as HTML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles/ndvi/map/tiles?f=html" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "application/json", + "title" : "Map tilesets for blueMarble (evi2 style) (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles/evi2/map/tiles?f=json" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "text/plain", + "title" : "Map tilesets for blueMarble (evi2 style) (as ECON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles/evi2/map/tiles?f=econ" + }, + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map", + "type" : "text/html", + "title" : "Map tilesets for blueMarble (evi2 style) (as HTML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/styles/evi2/map/tiles?f=html" + } + ], + "tilesets" : [ + { + "title" : "Blue Marble Next Generation (2004)", + "tileMatrixSetURI" : "http://www.opengis.net/def/tilematrixset/OGC/1.0/CDB1GlobalGrid", + "crs" : "http://www.opengis.net/def/crs/EPSG/0/4326", + "dataType" : "map", + "links" : [ + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme", + "type" : "application/json", + "title" : "CDB1GlobalGridTileMatrixSet definition (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/tileMatrixSets/CDB1GlobalGrid" + }, + { + "rel" : "self", + "type" : "application/json", + "title" : "CDB1GlobalGrid map tileset for blueMarble (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/CDB1GlobalGrid?f=json" + }, + { + "rel" : "self", + "type" : "text/html", + "title" : "CDB1GlobalGrid map tileset for blueMarble (as HTML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/CDB1GlobalGrid?f=html" + }, + { + "rel" : "self", + "type" : "application/json+tilejson", + "title" : "CDB1GlobalGrid map tileset for blueMarble (in TileJSON format)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/CDB1GlobalGrid?f=tilejson" + } + ] + }, + { + "title" : "Blue Marble Next Generation (2004)", + "tileMatrixSetURI" : "http://www.opengis.net/def/tilematrixset/OGC/1.0/GlobalCRS84Pixel", + "crs" : "http://www.opengis.net/def/crs/OGC/1.3/CRS84", + "dataType" : "map", + "links" : [ + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme", + "type" : "application/json", + "title" : "GlobalCRS84PixelTileMatrixSet definition (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/tileMatrixSets/GlobalCRS84Pixel" + }, + { + "rel" : "self", + "type" : "application/json", + "title" : "GlobalCRS84Pixel map tileset for blueMarble (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/GlobalCRS84Pixel?f=json" + }, + { + "rel" : "self", + "type" : "text/html", + "title" : "GlobalCRS84Pixel map tileset for blueMarble (as HTML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/GlobalCRS84Pixel?f=html" + }, + { + "rel" : "self", + "type" : "application/json+tilejson", + "title" : "GlobalCRS84Pixel map tileset for blueMarble (in TileJSON format)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/GlobalCRS84Pixel?f=tilejson" + } + ] + }, + { + "title" : "Blue Marble Next Generation (2004)", + "tileMatrixSetURI" : "http://www.opengis.net/def/tilematrixset/OGC/1.0/GlobalCRS84Scale", + "crs" : "http://www.opengis.net/def/crs/OGC/1.3/CRS84", + "dataType" : "map", + "links" : [ + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme", + "type" : "application/json", + "title" : "GlobalCRS84ScaleTileMatrixSet definition (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/tileMatrixSets/GlobalCRS84Scale" + }, + { + "rel" : "self", + "type" : "application/json", + "title" : "GlobalCRS84Scale map tileset for blueMarble (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/GlobalCRS84Scale?f=json" + }, + { + "rel" : "self", + "type" : "text/html", + "title" : "GlobalCRS84Scale map tileset for blueMarble (as HTML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/GlobalCRS84Scale?f=html" + }, + { + "rel" : "self", + "type" : "application/json+tilejson", + "title" : "GlobalCRS84Scale map tileset for blueMarble (in TileJSON format)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/GlobalCRS84Scale?f=tilejson" + } + ] + }, + { + "title" : "Blue Marble Next Generation (2004)", + "tileMatrixSetURI" : "http://www.opengis.net/def/tilematrixset/OGC/1.0/GNOSISGlobalGrid", + "crs" : "http://www.opengis.net/def/crs/EPSG/0/4326", + "dataType" : "map", + "links" : [ + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme", + "type" : "application/json", + "title" : "GNOSISGlobalGridTileMatrixSet definition (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/tileMatrixSets/GNOSISGlobalGrid" + }, + { + "rel" : "self", + "type" : "application/json", + "title" : "GNOSISGlobalGrid map tileset for blueMarble (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/GNOSISGlobalGrid?f=json" + }, + { + "rel" : "self", + "type" : "text/html", + "title" : "GNOSISGlobalGrid map tileset for blueMarble (as HTML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/GNOSISGlobalGrid?f=html" + }, + { + "rel" : "self", + "type" : "application/json+tilejson", + "title" : "GNOSISGlobalGrid map tileset for blueMarble (in TileJSON format)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/GNOSISGlobalGrid?f=tilejson" + } + ] + }, + { + "title" : "Blue Marble Next Generation (2004)", + "tileMatrixSetURI" : "http://www.opengis.net/def/tilematrixset/OGC/1.0/GoogleCRS84Quad", + "crs" : "http://www.opengis.net/def/crs/OGC/1.3/CRS84", + "dataType" : "map", + "links" : [ + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme", + "type" : "application/json", + "title" : "GoogleCRS84QuadTileMatrixSet definition (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/tileMatrixSets/GoogleCRS84Quad" + }, + { + "rel" : "self", + "type" : "application/json", + "title" : "GoogleCRS84Quad map tileset for blueMarble (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/GoogleCRS84Quad?f=json" + }, + { + "rel" : "self", + "type" : "text/html", + "title" : "GoogleCRS84Quad map tileset for blueMarble (as HTML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/GoogleCRS84Quad?f=html" + }, + { + "rel" : "self", + "type" : "application/json+tilejson", + "title" : "GoogleCRS84Quad map tileset for blueMarble (in TileJSON format)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/GoogleCRS84Quad?f=tilejson" + } + ] + }, + { + "title" : "Blue Marble Next Generation (2004)", + "tileMatrixSetURI" : "http://www.opengis.net/def/tilematrixset/OGC/1.0/ISEA9R", + "crs" : "http://www.opengis.net/def/crs/OGC/0/153456", + "dataType" : "map", + "links" : [ + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme", + "type" : "application/json", + "title" : "ISEA9RTileMatrixSet definition (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/tileMatrixSets/ISEA9R" + }, + { + "rel" : "self", + "type" : "application/json", + "title" : "ISEA9R map tileset for blueMarble (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/ISEA9R?f=json" + }, + { + "rel" : "self", + "type" : "text/html", + "title" : "ISEA9R map tileset for blueMarble (as HTML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/ISEA9R?f=html" + }, + { + "rel" : "self", + "type" : "application/json+tilejson", + "title" : "ISEA9R map tileset for blueMarble (in TileJSON format)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/ISEA9R?f=tilejson" + } + ] + }, + { + "title" : "Blue Marble Next Generation (2004)", + "tileMatrixSetURI" : "http://www.opengis.net/def/tilematrixset/OGC/1.0/WebMercatorQuad", + "crs" : "http://www.opengis.net/def/crs/EPSG/0/3857", + "dataType" : "map", + "links" : [ + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme", + "type" : "application/json", + "title" : "WebMercatorQuadTileMatrixSet definition (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/tileMatrixSets/WebMercatorQuad" + }, + { + "rel" : "self", + "type" : "application/json", + "title" : "WebMercatorQuad map tileset for blueMarble (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/WebMercatorQuad?f=json" + }, + { + "rel" : "self", + "type" : "text/html", + "title" : "WebMercatorQuad map tileset for blueMarble (as HTML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/WebMercatorQuad?f=html" + }, + { + "rel" : "self", + "type" : "application/json+tilejson", + "title" : "WebMercatorQuad map tileset for blueMarble (in TileJSON format)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/WebMercatorQuad?f=tilejson" + } + ] + }, + { + "title" : "Blue Marble Next Generation (2004)", + "tileMatrixSetURI" : "http://www.opengis.net/def/tilematrixset/OGC/1.0/WorldCRS84Quad", + "crs" : "http://www.opengis.net/def/crs/OGC/1.3/CRS84", + "dataType" : "map", + "links" : [ + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme", + "type" : "application/json", + "title" : "WorldCRS84QuadTileMatrixSet definition (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/tileMatrixSets/WorldCRS84Quad" + }, + { + "rel" : "self", + "type" : "application/json", + "title" : "WorldCRS84Quad map tileset for blueMarble (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/WorldCRS84Quad?f=json" + }, + { + "rel" : "self", + "type" : "text/html", + "title" : "WorldCRS84Quad map tileset for blueMarble (as HTML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/WorldCRS84Quad?f=html" + }, + { + "rel" : "self", + "type" : "application/json+tilejson", + "title" : "WorldCRS84Quad map tileset for blueMarble (in TileJSON format)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/WorldCRS84Quad?f=tilejson" + } + ] + }, + { + "title" : "Blue Marble Next Generation (2004)", + "tileMatrixSetURI" : "http://www.opengis.net/def/tilematrixset/OGC/1.0/WorldMercatorWGS84Quad", + "crs" : "http://www.opengis.net/def/crs/EPSG/0/3395", + "dataType" : "map", + "links" : [ + { + "rel" : "http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme", + "type" : "application/json", + "title" : "WorldMercatorWGS84QuadTileMatrixSet definition (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/tileMatrixSets/WorldMercatorWGS84Quad" + }, + { + "rel" : "self", + "type" : "application/json", + "title" : "WorldMercatorWGS84Quad map tileset for blueMarble (as JSON)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/WorldMercatorWGS84Quad?f=json" + }, + { + "rel" : "self", + "type" : "text/html", + "title" : "WorldMercatorWGS84Quad map tileset for blueMarble (as HTML)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/WorldMercatorWGS84Quad?f=html" + }, + { + "rel" : "self", + "type" : "application/json+tilejson", + "title" : "WorldMercatorWGS84Quad map tileset for blueMarble (in TileJSON format)", + "href" : "http://127.0.0.1:8080/fakeogcapi/collections/blueMarble/map/tiles/WorldMercatorWGS84Quad?f=tilejson" + } + ] + } + ] +} diff --git a/autotest/gdrivers/ogcapi.py b/autotest/gdrivers/ogcapi.py index d078ca0bb81d..f12a239ec7ed 100644 --- a/autotest/gdrivers/ogcapi.py +++ b/autotest/gdrivers/ogcapi.py @@ -438,3 +438,42 @@ def test_ogc_api_raster_tiles(): assert ds.GetRasterBand(1).ReadBlock( ds.RasterXSize // 2 // 256, ds.RasterYSize // 2 // 256 ) + + +@pytest.mark.parametrize( + "image_format,raster_count,statistics", + ( + ("AUTO", 4, [0.0, 255.0, 83.8084411621094, 30.532715248645687]), + ("PNG", 4, [0.0, 255.0, 83.8084411621094, 30.532715248645687]), + ("PNG_PREFERRED", 4, [0.0, 255.0, 83.8084411621094, 30.532715248645687]), + ("JPEG", 3, [0.0, 255.0, 83.83631896972656, 30.486497283147653]), + ("JPEG_PREFERRED", 3, [0.0, 255.0, 83.83631896972656, 30.486497283147653]), + ("GEOTIFF", 4, [0.0, 255.0, 83.8084411621094, 30.532715248645687]), + ), +) +@pytest.mark.require_driver("WMS") +def test_ogc_api_raster_tiles_format(image_format, raster_count, statistics): + + ds = gdal.OpenEx( + f"OGCAPI:http://127.0.0.1:{gdaltest.webserver_port}/fakeogcapi/collections/blueMarble", + gdal.OF_RASTER, + open_options=[ + "API=TILES", + "CACHE=NO", + "TILEMATRIXSET=WorldMercatorWGS84Quad", + f"IMAGE_FORMAT={image_format}", + ], + ) + + assert ds is not None + + assert ds.RasterCount == raster_count + assert ds.RasterXSize == 131072 + assert ds.GetRasterBand(1).GetOverviewCount() == 9 + + # For some reason these tests fail on Github on Ubuntu 20.04 gcc and Ubuntu 20.04 coverage, but + # pass on all other builds. + # assert ds.RasterYSize == 1586181 + # assert ds.GetRasterBand(1).GetStatistics(True, True) == statistics + + del ds diff --git a/doc/source/drivers/raster/ogcapi.rst b/doc/source/drivers/raster/ogcapi.rst index 09d84ae05136..04204af49f7e 100644 --- a/doc/source/drivers/raster/ogcapi.rst +++ b/doc/source/drivers/raster/ogcapi.rst @@ -81,16 +81,18 @@ The following open options are available: GeoJSON items otherwise. - .. oo:: IMAGE_FORMAT - :choices: AUTO, PNG, PNG_PREFERRED, JPEG, JPEG_PREFERRED + :choices: AUTO, PNG, PNG_PREFERRED, JPEG, JPEG_PREFERRED, GEOTIFF :default: AUTO Which format to use for pixel acquisition, for tiles or map API. - Defaults to AUTO, which means - that PNG will be used if available, and fallback to JPEG otherwise. - If specifying PNG or JPEG, they must be available, otherwise the driver will - return an error. If specifying the one of the PNG_PREFERRED or JPEG_PREFERRED - value, the specified format will be used if available, and the driver will - fallback to the other format otherwise. + AUTO - This is the default and specifies that PNG images will be checked first, + then JPEG and then any additional formats the server supports. + PNG_PREFERRED - Same as AUTO + JPEG_PREFERRED - Similar to AUTO, but the order is JPEG, PNG and then any additional + formats the server supports + JPEG - Use only JPEG images. If none are available then the driver will return an error + PNG - Use only PNG images. If none are available then the driver will return an error + GEOTIFF - Use only GEOTIFF images. If none are available then the driver will return an error - .. oo:: VECTOR_FORMAT :choices: AUTO, GEOJSON, GEOJSON_PREFERRED, MVT, MVT_PREFERRED diff --git a/frmts/ogcapi/gdalogcapidataset.cpp b/frmts/ogcapi/gdalogcapidataset.cpp index 60434f426ec0..0d24d1f9acf2 100644 --- a/frmts/ogcapi/gdalogcapidataset.cpp +++ b/frmts/ogcapi/gdalogcapidataset.cpp @@ -76,6 +76,7 @@ class OGCAPIDataset final : public GDALDataset double m_adfGeoTransform[6]; OGRSpatialReference m_oSRS{}; + CPLString m_osTileData{}; // Classic OGC API features /items access std::unique_ptr m_poOAPIFDS{}; @@ -92,6 +93,8 @@ class OGCAPIDataset final : public GDALDataset CPLString BuildURL(const std::string &href) const; void SetRootURLFromURL(const std::string &osURL); + int FigureBands(const std::string &osContentType, + const CPLString &osImageURL); bool InitFromFile(GDALOpenInfo *poOpenInfo); bool InitFromURL(GDALOpenInfo *poOpenInfo); @@ -110,6 +113,12 @@ class OGCAPIDataset final : public GDALDataset ", " MEDIA_TYPE_JSON, CPLStringList *paosHeaders = nullptr); + std::unique_ptr + OpenTile(const CPLString &osURLPattern, int nMatrix, int nColumn, int nRow, + bool &bEmptyContent, unsigned int nOpenTileFlags = 0, + const CPLString &osPrefix = {}, + const char *const *papszOpenOptions = nullptr); + bool InitWithMapAPI(GDALOpenInfo *poOpenInfo, const CPLJSONObject &oCollection, double dfXMin, double dfYMin, double dfXMax, double dfYMax); @@ -235,7 +244,6 @@ class OGCAPITiledLayer final false; // prevent recursion in EstablishFields() OGCAPITiledLayerFeatureDefn *m_poFeatureDefn = nullptr; OGREnvelope m_sEnvelope{}; - CPLString m_osTileData{}; std::unique_ptr m_poUnderlyingDS{}; OGRLayer *m_poUnderlyingLayer = nullptr; int m_nCurY = 0; @@ -587,6 +595,53 @@ bool OGCAPIDataset::DownloadJSon(const CPLString &osURL, CPLJSONDocument &oDoc, return oDoc.LoadMemory(osResult); } +/************************************************************************/ +/* OpenTile() */ +/************************************************************************/ + +std::unique_ptr +OGCAPIDataset::OpenTile(const CPLString &osURLPattern, int nMatrix, int nColumn, + int nRow, bool &bEmptyContent, + unsigned int nOpenTileFlags, const CPLString &osPrefix, + const char *const *papszOpenTileOptions) +{ + CPLString osURL(osURLPattern); + osURL.replaceAll("{tileMatrix}", CPLSPrintf("%d", nMatrix)); + osURL.replaceAll("{tileCol}", CPLSPrintf("%d", nColumn)); + osURL.replaceAll("{tileRow}", CPLSPrintf("%d", nRow)); + + CPLString osContentType; + if (!this->Download(osURL, nullptr, nullptr, m_osTileData, osContentType, + true, nullptr)) + { + return nullptr; + } + + bEmptyContent = m_osTileData.empty(); + if (bEmptyContent) + return nullptr; + + CPLString osTempFile; + osTempFile.Printf("/vsimem/ogcapi/%p", this); + VSIFCloseL(VSIFileFromMemBuffer(osTempFile.c_str(), + reinterpret_cast(&m_osTileData[0]), + m_osTileData.size(), false)); + + GDALDataset *result = nullptr; + + if (osPrefix.empty()) + result = GDALDataset::Open(osTempFile.c_str(), nOpenTileFlags, nullptr, + papszOpenTileOptions); + else + result = + GDALDataset::Open((osPrefix + ":" + osTempFile).c_str(), + nOpenTileFlags, nullptr, papszOpenTileOptions); + + VSIUnlink(osTempFile); + + return std::unique_ptr(result); +} + /************************************************************************/ /* Identify() */ /************************************************************************/ @@ -628,6 +683,37 @@ void OGCAPIDataset::SetRootURLFromURL(const std::string &osURL) m_osRootURL.assign(pszStr, pszPtr - pszStr); } +/************************************************************************/ +/* FigureBands() */ +/************************************************************************/ + +int OGCAPIDataset::FigureBands(const std::string &osContentType, + const CPLString &osImageURL) +{ + int result = 0; + + if (osContentType == "image/png") + { + result = 4; + } + else if (osContentType == "image/jpeg") + { + result = 3; + } + else + { + // Since we don't know the format download a tile and find out + bool bEmptyContent = false; + std::unique_ptr dataset = + OpenTile(osImageURL, 0, 0, 0, bEmptyContent, GDAL_OF_RASTER); + + // Return the bands from the image, if we didn't get an image then assume 3. + result = dataset ? (int)dataset->GetBands().size() : 3; + } + + return result; +} + /************************************************************************/ /* InitFromFile() */ /************************************************************************/ @@ -1004,21 +1090,63 @@ bool OGCAPIDataset::InitFromURL(GDALOpenInfo *poOpenInfo) /* SelectImageURL() */ /************************************************************************/ -static const CPLString SelectImageURL(const char *const *papszOptionOptions, - const CPLString &osPNG_URL, - const CPLString &osJPEG_URL) +static const std::pair +SelectImageURL(const char *const *papszOptionOptions, + std::map &oMapItemUrls) { - const char *pszFormat = + // Map IMAGE_FORMATS to their content types. Would be nice if this was + // globally defined someplace + const std::map> + oFormatContentTypeMap = { + {"AUTO", + {"image/png", "image/jpeg", "image/tiff; application=geotiff"}}, + {"PNG_PREFERRED", + {"image/png", "image/jpeg", "image/tiff; application=geotiff"}}, + {"JPEG_PREFERRED", + {"image/jpeg", "image/png", "image/tiff; application=geotiff"}}, + {"PNG", {"image/png"}}, + {"JPEG", {"image/jpeg"}}, + {"GEOTIFF", {"image/tiff; application=geotiff"}}}; + + // Get the IMAGE_FORMAT + const std::string osFormat = CSLFetchNameValueDef(papszOptionOptions, "IMAGE_FORMAT", "AUTO"); - if (EQUAL(pszFormat, "AUTO") || EQUAL(pszFormat, "PNG_PREFERRED")) - return !osPNG_URL.empty() ? osPNG_URL : osJPEG_URL; - else if (EQUAL(pszFormat, "PNG")) - return osPNG_URL; - else if (EQUAL(pszFormat, "JPEG")) - return osJPEG_URL; - else if (EQUAL(pszFormat, "JPEG_PREFERRED")) - return !osJPEG_URL.empty() ? osJPEG_URL : osPNG_URL; - return CPLString(); + + // Get a list of content types we will search for in priority order based on IMAGE_FORMAT + auto iterFormat = oFormatContentTypeMap.find(osFormat); + if (iterFormat == oFormatContentTypeMap.end()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unknown IMAGE_FORMAT specified: %s", osFormat.c_str()); + return std::pair(); + } + std::vector oContentTypes = iterFormat->second; + + // For "special" IMAGE_FORMATS we will also accept additional content types + // specified by the server. Note that this will likely result in having + // some content types duplicated in the vector but that is fine. + if (osFormat == "AUTO" || osFormat == "PNG_PREFERRED" || + osFormat == "JPEG_PREFERRED") + { + std::transform(oMapItemUrls.begin(), oMapItemUrls.end(), + std::back_inserter(oContentTypes), + [](const auto &pair) { return pair.first; }); + } + + // Loop over each content type - return the first one we find + for (auto &oContentType : oContentTypes) + { + auto iterContentType = oMapItemUrls.find(oContentType); + if (iterContentType != oMapItemUrls.end()) + { + return *iterContentType; + } + } + + CPLError(CE_Failure, CPLE_AppDefined, + "Server does not support specified IMAGE_FORMAT: %s", + osFormat.c_str()); + return std::pair(); } /************************************************************************/ @@ -1052,35 +1180,40 @@ bool OGCAPIDataset::InitWithMapAPI(GDALOpenInfo *poOpenInfo, double dfYMin, double dfXMax, double dfYMax) { auto oLinks = oRoot["links"].ToArray(); - CPLString osPNG_URL; - CPLString osJPEG_URL; + + // Key - mime type, Value url + std::map oMapItemUrls; for (const auto &oLink : oLinks) { if (oLink["rel"].ToString() == "http://www.opengis.net/def/rel/ogc/1.0/map" && - oLink["type"].ToString() == "image/png") + oLink["type"].IsValid()) { - osPNG_URL = BuildURL(oLink["href"].ToString()); + oMapItemUrls[oLink["type"].ToString()] = + BuildURL(oLink["href"].ToString()); } - else if (oLink["rel"].ToString() == - "http://www.opengis.net/def/rel/ogc/1.0/map" && - oLink["type"].ToString() == "image/jpeg") + else { - osJPEG_URL = BuildURL(oLink["href"].ToString()); + // For lack of additional information assume we are getting some bytes + oMapItemUrls["application/octet-stream"] = + BuildURL(oLink["href"].ToString()); } } - CPLString osImageURL = - SelectImageURL(poOpenInfo->papszOpenOptions, osPNG_URL, osJPEG_URL); + const std::pair oContentUrlPair = + SelectImageURL(poOpenInfo->papszOpenOptions, oMapItemUrls); + const std::string osContentType = oContentUrlPair.first; + const CPLString osImageURL = oContentUrlPair.second; + if (osImageURL.empty()) { CPLError(CE_Failure, CPLE_AppDefined, - "Cannot find link to PNG or JPEG images"); + "Cannot find link to tileset items"); return false; } - const int l_nBands = ((osImageURL == osPNG_URL) ? 4 : 3); + int l_nBands = FigureBands(osContentType, osImageURL); int nOverviewCount = 0; int nLargestDim = std::max(nRasterXSize, nRasterYSize); while (nLargestDim > 256) @@ -1631,12 +1764,14 @@ bool OGCAPIDataset::InitWithTilesAPI(GDALOpenInfo *poOpenInfo, CPLError(CE_Failure, CPLE_AppDefined, "Missing links for tileset"); return false; } - CPLString osPNG_URL; - CPLString osJPEG_URL; + + // Key - mime type, Value url + std::map oMapItemUrls; CPLString osMVT_URL; CPLString osGEOJSON_URL; CPLString osTilingSchemeURL; bool bTilingSchemeURLJson = false; + for (const auto &oLink : oLinks) { const auto osRel = oLink.GetString("rel"); @@ -1657,13 +1792,15 @@ bool OGCAPIDataset::InitWithTilesAPI(GDALOpenInfo *poOpenInfo, } else if (bIsMap) { - if (osRel == "item" && osType == "image/png") + if (osRel == "item" && !osType.empty()) { - osPNG_URL = BuildURL(oLink["href"].ToString()); + oMapItemUrls[osType] = BuildURL(oLink["href"].ToString()); } - else if (osRel == "item" && osType == "image/jpeg") + else if (osRel == "item") { - osJPEG_URL = BuildURL(oLink["href"].ToString()); + // For lack of additional information assume we are getting some bytes + oMapItemUrls["application/octet-stream"] = + BuildURL(oLink["href"].ToString()); } } else @@ -1719,8 +1856,11 @@ bool OGCAPIDataset::InitWithTilesAPI(GDALOpenInfo *poOpenInfo, } } - const CPLString osRasterURL = - SelectImageURL(poOpenInfo->papszOpenOptions, osPNG_URL, osJPEG_URL); + const std::pair oContentUrlPair = + SelectImageURL(poOpenInfo->papszOpenOptions, oMapItemUrls); + const std::string osContentType = oContentUrlPair.first; + const CPLString osRasterURL = oContentUrlPair.second; + const CPLString osVectorURL = SelectVectorFormatURL( poOpenInfo->papszOpenOptions, osMVT_URL, osGEOJSON_URL); if (osRasterURL.empty() && osVectorURL.empty()) @@ -1877,7 +2017,8 @@ bool OGCAPIDataset::InitWithTilesAPI(GDALOpenInfo *poOpenInfo, CPLGetConfigOption("GDAL_WMS_MAX_CONNECTIONS", "5"))); const char *pszTileMatrix = CSLFetchNameValue(poOpenInfo->papszOpenOptions, "TILEMATRIX"); - const int l_nBands = ((osRasterURL == osPNG_URL) ? 4 : 3); + + int l_nBands = FigureBands(osContentType, osRasterURL); for (const auto &tileMatrix : tms->tileMatrixList()) { @@ -2331,37 +2472,17 @@ void OGCAPITiledLayer::ResetReading() GDALDataset *OGCAPITiledLayer::OpenTile(int nX, int nY, bool &bEmptyContent) { - bEmptyContent = false; - CPLString osURL(m_osTileURL); - int nCoalesce = GetCoalesceFactorForRow(nY); if (nCoalesce <= 0) return nullptr; nX = (nX / nCoalesce) * nCoalesce; - osURL.replaceAll("{tileCol}", CPLSPrintf("%d", nX)); - osURL.replaceAll("{tileRow}", CPLSPrintf("%d", nY)); - - CPLString osContentType; - if (!m_poDS->Download(osURL, nullptr, nullptr, m_osTileData, osContentType, - true, nullptr)) - { - return nullptr; - } - bEmptyContent = m_osTileData.empty(); - if (bEmptyContent) - return nullptr; - - CPLString osTempFile; - osTempFile.Printf("/vsimem/ogcapi/%p", this); - VSIFCloseL(VSIFileFromMemBuffer(osTempFile.c_str(), - reinterpret_cast(&m_osTileData[0]), - m_osTileData.size(), false)); + const char *const *papszOpenOptions = nullptr; + CPLString poPrefix; + CPLStringList aosOpenOptions; - GDALDataset *poTileDS; if (m_bIsMVT) { - CPLStringList aosOpenOptions; const double dfOriX = m_bInvertAxis ? m_oTileMatrix.mTopLeftY : m_oTileMatrix.mTopLeftX; const double dfOriY = @@ -2382,16 +2503,16 @@ GDALDataset *OGCAPITiledLayer::OpenTile(int nX, int nY, bool &bEmptyContent) "@GEOREF_TILEDIMY", CPLSPrintf("%.18g", m_oTileMatrix.mResY * m_oTileMatrix.mTileWidth)); - poTileDS = - GDALDataset::Open(("MVT:" + osTempFile).c_str(), GDAL_OF_VECTOR, - nullptr, aosOpenOptions.List()); - } - else - { - poTileDS = GDALDataset::Open(osTempFile.c_str(), GDAL_OF_VECTOR); + + papszOpenOptions = aosOpenOptions.List(); + poPrefix = "MVT"; } - VSIUnlink(osTempFile); - return poTileDS; + + std::unique_ptr dataset = m_poDS->OpenTile( + m_osTileURL, stoi(m_oTileMatrix.mId), nX, nY, bEmptyContent, + GDAL_OF_VECTOR, poPrefix, papszOpenOptions); + + return dataset.release(); } /************************************************************************/