From 25c07378e52d08655748bfb38fbf1b1998c87680 Mon Sep 17 00:00:00 2001 From: Florence Haudin Date: Wed, 28 Jan 2026 19:46:17 +0100 Subject: [PATCH 1/8] Extend the gallery of layer to support more layer types and sources. --- .../Esri-NatGeoWorldMap.png | Bin .../Esri-OceanBasemap.png | Bin .../Esri-WorldGrayCanvas.png | Bin .../Esri-WorldImagery.png | Bin .../Esri-WorldPhysical.png | Bin .../Esri-WorldShadedRelief.png | Bin .../Esri-WorldStreetMap.png | Bin .../Esri-WorldTerrain.png | Bin .../Esri-WorldTopoMap.png | Bin .../Gaode-Normal.png | Bin .../Gaode-Satellite.png | Bin .../MacroStrat-CartoRaster.png | Bin .../layer_gallery/MacroStrat-CartoVector.png | Bin 0 -> 89683 bytes ...IBS-ASTER_GDEM_Greyscale_Shaded_Relief.png | Bin .../NASAGIBS-ModisAquaBands721CR.png | Bin .../NASAGIBS-ModisAquaTrueColorCR.png | Bin .../NASAGIBS-ModisTerraAOD.png | Bin .../NASAGIBS-ModisTerraBands367CR.png | Bin .../NASAGIBS-ModisTerraBands721CR.png | Bin .../NASAGIBS-ModisTerraChlorophyll.png | Bin .../NASAGIBS-ModisTerraLSTDay.png | Bin .../NASAGIBS-ModisTerraSnowCover.png | Bin .../NASAGIBS-ModisTerraTrueColorCR.png | Bin .../NASAGIBS-ViirsEarthAtNight2012.png | Bin .../NASAGIBS-ViirsTrueColorCR.png | Bin .../OPNVKarte.png | Bin .../OpenRailwayMap.png | Bin .../OpenStreetMap-BZH.png | Bin .../OpenStreetMap-CAT.png | Bin .../OpenStreetMap-CH.png | Bin .../OpenStreetMap-DE.png | Bin .../OpenStreetMap-France.png | Bin .../OpenStreetMap-HOT.png | Bin .../OpenStreetMap-Mapnik.png | Bin .../OpenTopoMap.png | Bin .../Strava-All.png | Bin .../Strava-Ride.png | Bin .../Strava-Run.png | Bin .../Strava-Water.png | Bin .../Strava-Winter.png | Bin .../USGS-USImagery.png | Bin .../USGS-USImageryTopo.png | Bin .../USGS-USTopo.png | Bin .../WaymarkedTrails-cycling.png | Bin .../WaymarkedTrails-hiking.png | Bin .../WaymarkedTrails-mtb.png | Bin .../WaymarkedTrails-riding.png | Bin .../WaymarkedTrails-skating.png | Bin .../WaymarkedTrails-slopes.png | Bin .../custom_raster.png | Bin .../base/layer_gallery/layer_gallery.json | 1 + packages/base/layer_gallery_generator.py | 618 ++++++++++++++++++ .../raster_layer_gallery.json | 1 - .../base/rasterlayer_gallery_generator.py | 259 -------- .../base/src/dialogs/layerBrowserDialog.tsx | 30 +- packages/base/src/tools.ts | 70 +- packages/schema/src/interfaces.ts | 32 +- .../src/layerBrowserRegistry.ts | 8 +- 58 files changed, 695 insertions(+), 324 deletions(-) rename packages/base/{rasterlayer_gallery => layer_gallery}/Esri-NatGeoWorldMap.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/Esri-OceanBasemap.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/Esri-WorldGrayCanvas.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/Esri-WorldImagery.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/Esri-WorldPhysical.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/Esri-WorldShadedRelief.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/Esri-WorldStreetMap.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/Esri-WorldTerrain.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/Esri-WorldTopoMap.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/Gaode-Normal.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/Gaode-Satellite.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/MacroStrat-CartoRaster.png (100%) create mode 100644 packages/base/layer_gallery/MacroStrat-CartoVector.png rename packages/base/{rasterlayer_gallery => layer_gallery}/NASAGIBS-ASTER_GDEM_Greyscale_Shaded_Relief.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/NASAGIBS-ModisAquaBands721CR.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/NASAGIBS-ModisAquaTrueColorCR.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/NASAGIBS-ModisTerraAOD.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/NASAGIBS-ModisTerraBands367CR.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/NASAGIBS-ModisTerraBands721CR.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/NASAGIBS-ModisTerraChlorophyll.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/NASAGIBS-ModisTerraLSTDay.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/NASAGIBS-ModisTerraSnowCover.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/NASAGIBS-ModisTerraTrueColorCR.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/NASAGIBS-ViirsEarthAtNight2012.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/NASAGIBS-ViirsTrueColorCR.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/OPNVKarte.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/OpenRailwayMap.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/OpenStreetMap-BZH.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/OpenStreetMap-CAT.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/OpenStreetMap-CH.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/OpenStreetMap-DE.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/OpenStreetMap-France.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/OpenStreetMap-HOT.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/OpenStreetMap-Mapnik.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/OpenTopoMap.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/Strava-All.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/Strava-Ride.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/Strava-Run.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/Strava-Water.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/Strava-Winter.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/USGS-USImagery.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/USGS-USImageryTopo.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/USGS-USTopo.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/WaymarkedTrails-cycling.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/WaymarkedTrails-hiking.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/WaymarkedTrails-mtb.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/WaymarkedTrails-riding.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/WaymarkedTrails-skating.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/WaymarkedTrails-slopes.png (100%) rename packages/base/{rasterlayer_gallery => layer_gallery}/custom_raster.png (100%) create mode 100644 packages/base/layer_gallery/layer_gallery.json create mode 100644 packages/base/layer_gallery_generator.py delete mode 100644 packages/base/rasterlayer_gallery/raster_layer_gallery.json delete mode 100644 packages/base/rasterlayer_gallery_generator.py diff --git a/packages/base/rasterlayer_gallery/Esri-NatGeoWorldMap.png b/packages/base/layer_gallery/Esri-NatGeoWorldMap.png similarity index 100% rename from packages/base/rasterlayer_gallery/Esri-NatGeoWorldMap.png rename to packages/base/layer_gallery/Esri-NatGeoWorldMap.png diff --git a/packages/base/rasterlayer_gallery/Esri-OceanBasemap.png b/packages/base/layer_gallery/Esri-OceanBasemap.png similarity index 100% rename from packages/base/rasterlayer_gallery/Esri-OceanBasemap.png rename to packages/base/layer_gallery/Esri-OceanBasemap.png diff --git a/packages/base/rasterlayer_gallery/Esri-WorldGrayCanvas.png b/packages/base/layer_gallery/Esri-WorldGrayCanvas.png similarity index 100% rename from packages/base/rasterlayer_gallery/Esri-WorldGrayCanvas.png rename to packages/base/layer_gallery/Esri-WorldGrayCanvas.png diff --git a/packages/base/rasterlayer_gallery/Esri-WorldImagery.png b/packages/base/layer_gallery/Esri-WorldImagery.png similarity index 100% rename from packages/base/rasterlayer_gallery/Esri-WorldImagery.png rename to packages/base/layer_gallery/Esri-WorldImagery.png diff --git a/packages/base/rasterlayer_gallery/Esri-WorldPhysical.png b/packages/base/layer_gallery/Esri-WorldPhysical.png similarity index 100% rename from packages/base/rasterlayer_gallery/Esri-WorldPhysical.png rename to packages/base/layer_gallery/Esri-WorldPhysical.png diff --git a/packages/base/rasterlayer_gallery/Esri-WorldShadedRelief.png b/packages/base/layer_gallery/Esri-WorldShadedRelief.png similarity index 100% rename from packages/base/rasterlayer_gallery/Esri-WorldShadedRelief.png rename to packages/base/layer_gallery/Esri-WorldShadedRelief.png diff --git a/packages/base/rasterlayer_gallery/Esri-WorldStreetMap.png b/packages/base/layer_gallery/Esri-WorldStreetMap.png similarity index 100% rename from packages/base/rasterlayer_gallery/Esri-WorldStreetMap.png rename to packages/base/layer_gallery/Esri-WorldStreetMap.png diff --git a/packages/base/rasterlayer_gallery/Esri-WorldTerrain.png b/packages/base/layer_gallery/Esri-WorldTerrain.png similarity index 100% rename from packages/base/rasterlayer_gallery/Esri-WorldTerrain.png rename to packages/base/layer_gallery/Esri-WorldTerrain.png diff --git a/packages/base/rasterlayer_gallery/Esri-WorldTopoMap.png b/packages/base/layer_gallery/Esri-WorldTopoMap.png similarity index 100% rename from packages/base/rasterlayer_gallery/Esri-WorldTopoMap.png rename to packages/base/layer_gallery/Esri-WorldTopoMap.png diff --git a/packages/base/rasterlayer_gallery/Gaode-Normal.png b/packages/base/layer_gallery/Gaode-Normal.png similarity index 100% rename from packages/base/rasterlayer_gallery/Gaode-Normal.png rename to packages/base/layer_gallery/Gaode-Normal.png diff --git a/packages/base/rasterlayer_gallery/Gaode-Satellite.png b/packages/base/layer_gallery/Gaode-Satellite.png similarity index 100% rename from packages/base/rasterlayer_gallery/Gaode-Satellite.png rename to packages/base/layer_gallery/Gaode-Satellite.png diff --git a/packages/base/rasterlayer_gallery/MacroStrat-CartoRaster.png b/packages/base/layer_gallery/MacroStrat-CartoRaster.png similarity index 100% rename from packages/base/rasterlayer_gallery/MacroStrat-CartoRaster.png rename to packages/base/layer_gallery/MacroStrat-CartoRaster.png diff --git a/packages/base/layer_gallery/MacroStrat-CartoVector.png b/packages/base/layer_gallery/MacroStrat-CartoVector.png new file mode 100644 index 0000000000000000000000000000000000000000..0e63decd34cd83e5829c19ea5797b41902c72e7a GIT binary patch literal 89683 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�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

7a@gXS&wM$@<#q4 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 z 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 S%j5U}0000Gaode.com", "maxZoom": 19}, "layerParameters": {"opacity": 1}}, "Satellite": {"thumbnailPath": "layer_gallery/Gaode-Satellite.png", "name": "Gaode.Satellite", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "http://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}", "attribution": "© Gaode.com", "maxZoom": 19}, "layerParameters": {"opacity": 1}}}, "Strava": {"All": {"thumbnailPath": "layer_gallery/Strava-All.png", "name": "Strava.All", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://heatmap-external-a.strava.com/tiles/all/hot/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15}, "layerParameters": {"opacity": 1}}, "Ride": {"thumbnailPath": "layer_gallery/Strava-Ride.png", "name": "Strava.Ride", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://heatmap-external-a.strava.com/tiles/ride/hot/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15}, "layerParameters": {"opacity": 1}}, "Run": {"thumbnailPath": "layer_gallery/Strava-Run.png", "name": "Strava.Run", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://heatmap-external-a.strava.com/tiles/run/bluered/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15}, "layerParameters": {"opacity": 1}}, "Water": {"thumbnailPath": "layer_gallery/Strava-Water.png", "name": "Strava.Water", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://heatmap-external-a.strava.com/tiles/water/blue/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15}, "layerParameters": {"opacity": 1}}, "Winter": {"thumbnailPath": "layer_gallery/Strava-Winter.png", "name": "Strava.Winter", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://heatmap-external-a.strava.com/tiles/winter/hot/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15}, "layerParameters": {"opacity": 1}}}, "OPNVKarte": {"thumbnailPath": "layer_gallery/OPNVKarte.png", "name": "OPNVKarte", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png", "attribution": "Map memomaps.de CC-BY-SA, map data (C) OpenStreetMap contributors", "maxZoom": 18, "minZoom": null}, "layerParameters": {"opacity": 1}}, "OpenTopoMap": {"thumbnailPath": "layer_gallery/OpenTopoMap.png", "name": "OpenTopoMap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", "attribution": "Map data: (C) OpenStreetMap contributors, SRTM | Map style: (C) OpenTopoMap (CC-BY-SA)", "maxZoom": 17, "minZoom": null}, "layerParameters": {"opacity": 1}}, "OpenRailwayMap": {"thumbnailPath": "layer_gallery/OpenRailwayMap.png", "name": "OpenRailwayMap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png", "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) OpenRailwayMap (CC-BY-SA)", "maxZoom": 19, "minZoom": null}, "layerParameters": {"opacity": 1}}, "Esri": {"WorldStreetMap": {"thumbnailPath": "layer_gallery/Esri-WorldStreetMap.png", "name": "Esri.WorldStreetMap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012", "maxZoom": null, "minZoom": null}, "layerParameters": {"opacity": 1}}, "WorldTopoMap": {"thumbnailPath": "layer_gallery/Esri-WorldTopoMap.png", "name": "Esri.WorldTopoMap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community", "maxZoom": null, "minZoom": null}, "layerParameters": {"opacity": 1}}, "WorldImagery": {"thumbnailPath": "layer_gallery/Esri-WorldImagery.png", "name": "Esri.WorldImagery", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community", "maxZoom": null, "minZoom": null}, "layerParameters": {"opacity": 1}}, "WorldTerrain": {"thumbnailPath": "layer_gallery/Esri-WorldTerrain.png", "name": "Esri.WorldTerrain", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: USGS, Esri, TANA, DeLorme, and NPS", "maxZoom": 13, "minZoom": null}, "layerParameters": {"opacity": 1}}, "WorldShadedRelief": {"thumbnailPath": "layer_gallery/Esri-WorldShadedRelief.png", "name": "Esri.WorldShadedRelief", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: Esri", "maxZoom": 13, "minZoom": null}, "layerParameters": {"opacity": 1}}, "WorldPhysical": {"thumbnailPath": "layer_gallery/Esri-WorldPhysical.png", "name": "Esri.WorldPhysical", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: US National Park Service", "maxZoom": 8, "minZoom": null}, "layerParameters": {"opacity": 1}}, "OceanBasemap": {"thumbnailPath": "layer_gallery/Esri-OceanBasemap.png", "name": "Esri.OceanBasemap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri", "maxZoom": 13, "minZoom": null}, "layerParameters": {"opacity": 1}}, "NatGeoWorldMap": {"thumbnailPath": "layer_gallery/Esri-NatGeoWorldMap.png", "name": "Esri.NatGeoWorldMap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC", "maxZoom": 16, "minZoom": null}, "layerParameters": {"opacity": 1}}, "WorldGrayCanvas": {"thumbnailPath": "layer_gallery/Esri-WorldGrayCanvas.png", "name": "Esri.WorldGrayCanvas", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ", "maxZoom": 16, "minZoom": null}, "layerParameters": {"opacity": 1}}}, "MacroStrat": {"CartoRaster": {"thumbnailPath": "layer_gallery/MacroStrat-CartoRaster.png", "name": "MacroStrat.CartoRaster", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tiles.macrostrat.org/carto/{z}/{x}/{y}.png", "attribution": "\u00a9 Geologic data \u00a9 Macrostrat raster layer (CC\u2011BY 4.0)", "maxZoom": 18, "minZoom": null}, "layerParameters": {"opacity": 1}}, "CartoVector": {"thumbnailPath": "layer_gallery/MacroStrat-CartoVector.png", "name": "MacroStrat.CartoVector", "layerType": "VectorTileLayer", "sourceType": "VectorTileSource", "sourceParameters": {"url": "https://tiles.macrostrat.org/carto/{z}/{x}/{y}.mvt", "attribution": "\u00a9 Geologic data \u00a9 Macrostrat vector layer (CC\u2011BY 4.0)", "maxZoom": 18, "minZoom": null}, "layerParameters": {"opacity": 1}}}} \ No newline at end of file diff --git a/packages/base/layer_gallery_generator.py b/packages/base/layer_gallery_generator.py new file mode 100644 index 000000000..d919da30d --- /dev/null +++ b/packages/base/layer_gallery_generator.py @@ -0,0 +1,618 @@ +from datetime import date, timedelta +import json +from io import BytesIO +import os +import subprocess + +import requests +from PIL import Image +import mercantile +from xyzservices import providers, TileProvider +import string +from requests.exceptions import RequestException + +THUMBNAILS_LOCATION = "layer_gallery" + +def placeholder_tile(size): + return Image.new("RGB", size, (220, 220, 220)) + +def extract_placeholders(url_template): + """ + Extract the placeholders from an url_template + """ + formatter = string.Formatter() + return { + field_name + for _, field_name, _, _ in formatter.parse(url_template) + if field_name + } + +def build_url_parameters(tile_provider): + """ + Build the url parameters that are needed in fetch_tile, create_thumbnail and download_thumbnail + """ + url_template = tile_provider["url"] + placeholders = extract_placeholders(url_template) + + # Placeholders handled explicitly by fetch_tile + reserved = {"x", "y", "z", "s"} + + kwargs = {} + + for name in placeholders - reserved: + if name in tile_provider and tile_provider[name] is not None: + kwargs[name] = tile_provider[name] + if (name == "time" and tile_provider['time']==''): + kwargs['time']= yesterday + else: + raise KeyError( + f"Placeholder '{name}' not found in TileProvider '{tile_provider.get('name')}'" + ) + return kwargs + +def fetch_tile(url_template, x, y, z, s="a", **kwargs): + """ + Fetch a tile from the given URL template. + """ + try: + url = url_template.format(x=x, y=y, z=z, s=s, **kwargs) + response = requests.get( + url, + headers={"User-Agent": "JupyterGIS"}, + timeout=10, + ) + response.raise_for_status() + return Image.open(BytesIO(response.content)) + except RequestException as e: + print(f"⚠️ Tile fetch failed: {e}") + return None + +def latlng_to_tile(lat, lng, zoom): + """ + Convert latitude/longitude to tile coordinates. + """ + tile = mercantile.tile(lng, lat, zoom, True) + return tile.x, tile.y + + + +def create_thumbnail( + url_template, + lat, + lng, + zoom, + thumbnail_path, + tile_size=256, + thumbnail_size=(512, 512), + **url_parameters, +): + """ + Create a thumbnail for the specified location and zoom level. + """ + # Skip if thumbnail already exists + x, y = latlng_to_tile(lat, lng, zoom) + + # Fetch the tiles (2x2 grid for the thumbnail) + tiles = [] + for dy in range(2): + row = [] + for dx in range(2): + tile_x, tile_y = x + dx, y + dy + tile = fetch_tile(url_template, tile_x, tile_y, zoom, **url_parameters) + if tile is None: + tile = placeholder_tile((tile_size, tile_size)) + row.append(tile) + tiles.append(row) + + # Create a blank image for the thumbnail + thumbnail = Image.new("RGB", (2 * tile_size, 2 * tile_size)) + + # Paste the tiles into the thumbnail image + for dy, row in enumerate(tiles): + for dx, tile in enumerate(row): + thumbnail.paste(tile, (dx * tile_size, dy * tile_size)) + + # Resize to the desired thumbnail size + thumbnail = thumbnail.resize(thumbnail_size, Image.LANCZOS) + return thumbnail + + +yesterday = (date.today() - timedelta(days=1)).strftime("%Y-%m-%d") + +# San Francisco +san_francisco = {"lat": 37.7749, "lng": -122.4194, "zoom": 5} + +middle_europe = {"lat": 48.63290858589535, "lng": -350.068359375, "zoom": 4} + +# Default +france = {"lat": 47.040182144806664, "lng": 1.2963867187500002, "zoom": 5} + +provider_config = { + "OpenStreetMap": { + "Mapnik": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail":{ + "Special Rules": {}, + "Default": france + }, + }, + "BZH": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail":{ + "Special Rules": {"lat": 47.76702233051035, "lng": -3.4675598144531254, "zoom": 8}, + "Default": france + }, + }, + "CAT": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail":{ + "Special Rules": {}, + "Default": france + }, + }, + "CH": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail":{ + "Special Rules": {"lat": 46.8182, "lng": 8.2275, "zoom": 8}, + "Default": france + }, + }, + "DE": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail":{ + "Special Rules": {"lat": 51.1657, "lng": 10.4515, "zoom": 8}, + "Default": france + } + }, + "France": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail":{ + "Special Rules": {}, + "Default": france + } + }, + "HOT": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail":{ + "Special Rules": {}, + "Default": france + }, + } + }, + "NASAGIBS": { + "ModisTerraTrueColorCR" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": france, + } + }, + "ModisTerraBands367CR" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": france, + } + }, + "ViirsEarthAtNight2012" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": france, + } + }, + "ModisTerraLSTDay" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": france, + } + }, + "ModisTerraSnowCover" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": france, + } + }, + "ModisTerraAOD" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": france, + } + }, + "ModisTerraChlorophyll" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": france, + } + }, + + "ModisTerraBands721CR" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": france, + } + }, + "ModisAquaTrueColorCR" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": france, + } + }, + "ModisAquaBands721CR" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": france, + } + }, + "ViirsTrueColorCR" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": france, + } + }, + "ASTER_GDEM_Greyscale_Shaded_Relief" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": france, + } + }, + + }, + "USGS": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": san_francisco, + }, + }, + #"WaymarkedTrails": { + #"layerType": "RasterLayer", + #"sourceType": "RasterSource", + # "thumbnail": { + #"Special Rules": {}, + #"Default": france, + #}, + # }, + "Gaode": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": san_francisco, + }, + }, + "Strava": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": france, + "TileSize": 512, + }, + }, + + "OPNVKarte": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": san_francisco, + }, + }, + "OpenTopoMap": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": san_francisco, + }, + }, + "OpenRailwayMap": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": san_francisco, + "TileSize": 512, + }, + }, + "Esri": { + "WorldStreetMap" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": san_francisco, + } + }, + "WorldTopoMap" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": san_francisco, + } + }, + "WorldImagery" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": san_francisco, + } + }, + "WorldTerrain" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": san_francisco, + } + }, + "WorldShadedRelief" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": san_francisco, + } + }, + "WorldPhysical" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": san_francisco, + } + }, + "OceanBasemap" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": san_francisco, + } + }, + "NatGeoWorldMap" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": san_francisco, + } + }, + "WorldGrayCanvas" :{ + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": san_francisco, + } + }, + }, + "MacroStrat": { + "CartoRaster": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": france, + }, + }, + "CartoVector": { + "layerType": "VectorTileLayer", + "sourceType": "VectorTileSource", + "thumbnail": { + "Special Rules": {}, + "Default": france, + }, + }, + }, +} + + + +def download_thumbnail(url_template, name, position, tile_size, **url_parameters): + file_path = f"{THUMBNAILS_LOCATION}/{name}.png" + if os.path.exists(file_path): + return file_path + thumbnail = create_thumbnail( + url_template, + position["lat"], + position["lng"], + position["zoom"], + file_path, + tile_size, + **url_parameters, + ) + thumbnail.save(file_path) + return file_path + + +# Create thumbnail dir if needed +if not os.path.exists(THUMBNAILS_LOCATION): + os.makedirs(THUMBNAILS_LOCATION) + +# This is the JSON we'll generate for the gallery +provider_gallery = {} + +custom_providers = providers.copy() + +custom_providers["MacroStrat"] = { + "CartoRaster": TileProvider( + name="MacroStrat.CartoRaster", + url="https://tiles.macrostrat.org/carto/{z}/{x}/{y}.png", + attribution="© Geologic data © Macrostrat raster layer (CC‑BY 4.0)", + max_zoom=18, + ), + "CartoVector": TileProvider( + name="MacroStrat.CartoVector", + url="https://tiles.macrostrat.org/carto/{z}/{x}/{y}.mvt", + attribution="© Geologic data © Macrostrat vector layer (CC‑BY 4.0)", + max_zoom=18, + ), +} + +# Fetch thumbnails and populate the dictionary +for provider_key, provider_value in provider_config.items(): + + xyzprovider = custom_providers[provider_key] + config_is_flat = "layerType" in provider_value + xyz_is_flat = "url" in xyzprovider + + if config_is_flat and xyz_is_flat: + tile_provider = xyzprovider + + url_template = tile_provider["url"] + url_parameters = build_url_parameters(tile_provider) + + thumbnail_config = provider_value["thumbnail"] + position = thumbnail_config["Special Rules"].get(provider_key, thumbnail_config["Default"]) + tile_size = thumbnail_config.get("TileSize", 256) + file_path = download_thumbnail(url_template, provider_key, position, tile_size, **url_parameters) + + + provider_gallery[provider_key] = { + "thumbnailPath": file_path, + "name": provider_key, + "layerType": provider_value["layerType"], + "sourceType": provider_value["sourceType"], + "sourceParameters": { + "url": url_template, + "attribution": xyzprovider.get("attribution"), + "maxZoom": xyzprovider.get("max_zoom"), + "minZoom": xyzprovider.get("min_zoom"), + }, + "layerParameters": {"opacity": 1} + + } + + elif config_is_flat and not xyz_is_flat: + providers_maps = {} + + for map_name, tile_provider in xyzprovider.items(): + url_template = tile_provider["url"] + url_parameters = build_url_parameters(tile_provider) + + + thumbnail_config = provider_value["thumbnail"] + position = thumbnail_config["Special Rules"].get( + map_name, thumbnail_config["Default"] + ) + tile_size = thumbnail_config.get("TileSize", 256) + + name = tile_provider["name"].replace(".", "-") + + file_path = download_thumbnail( + url_template, name, position, tile_size, **url_parameters + ) + + providers_maps[map_name] = { + "thumbnailPath": file_path, + "name": provider_key +"."+ map_name, + "layerType": provider_value["layerType"], + "sourceType": provider_value["sourceType"], + "sourceParameters": { + "url": url_template, + "attribution": tile_provider.get("attribution"), + "maxZoom": tile_provider.get("max_zoom") + }, + "layerParameters": {"opacity": 1} + } + + provider_gallery[provider_key] = providers_maps + + elif not config_is_flat and not xyz_is_flat: + providers_maps = {} + for map_name, map_config in provider_value.items(): + tile_provider = xyzprovider[map_name] + url_template = tile_provider["url"] + url_parameters = build_url_parameters(tile_provider) + + thumbnail_config = map_config["thumbnail"] + position = thumbnail_config["Special Rules"].get( + map_name, thumbnail_config["Default"] + ) + tile_size = thumbnail_config.get("TileSize", 256) + + name = tile_provider["name"].replace(".", "-") + + file_path = download_thumbnail( + url_template, name, position, tile_size, **url_parameters + ) + + providers_maps[map_name] = { + "thumbnailPath": file_path, + "name": provider_key +"."+ map_name, + "layerType": map_config["layerType"], + "sourceType": map_config["sourceType"], + "sourceParameters": { + "url": url_template, + "attribution": tile_provider.get("attribution"), + "maxZoom": tile_provider.get("max_zoom"), + "minZoom": tile_provider.get("min_zoom") + }, + "layerParameters": {"opacity": 1}, + } + + provider_gallery[provider_key] = providers_maps + + else: + raise ValueError( + f"Inconsistent config for provider '{provider_key}'" + ) + +""" +# compress each images of THUMBNAILS_LOCATION +cmd = f"shopt -s globstar && mogrify -resize 50% {THUMBNAILS_LOCATION}/*.png && optipng {THUMBNAILS_LOCATION}/*.png" +subprocess.run(["bash", "-lc", cmd], check=True) + +try: + subprocess.run(["bash", "-lc", cmd], check=True) +except subprocess.CalledProcessError as e: + print("⚠️ Image compression skipped:", e) +""" + + +""" +# compress a single image of THUMBNAILS_LOCATION +image_name = "MacroStrat-CartoVector.png" +cmd = ( + f'mogrify -resize 50% "{THUMBNAILS_LOCATION}/{image_name}" && ' + f'optipng "{THUMBNAILS_LOCATION}/{image_name}"' +) +subprocess.run(["bash", "-lc", cmd], check=True) +""" + +with open(f"{THUMBNAILS_LOCATION}/layer_gallery.json", "w") as f: + json.dump(provider_gallery, f) + diff --git a/packages/base/rasterlayer_gallery/raster_layer_gallery.json b/packages/base/rasterlayer_gallery/raster_layer_gallery.json deleted file mode 100644 index 39ebac023..000000000 --- a/packages/base/rasterlayer_gallery/raster_layer_gallery.json +++ /dev/null @@ -1 +0,0 @@ -{"OpenStreetMap": {"Mapnik": {"thumbnailPath": "rasterlayer_gallery/OpenStreetMap-Mapnik.png", "url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png", "max_zoom": 19, "html_attribution": "© OpenStreetMap contributors", "attribution": "(C) OpenStreetMap contributors", "name": "OpenStreetMap.Mapnik"}, "DE": {"thumbnailPath": "rasterlayer_gallery/OpenStreetMap-DE.png", "url": "https://tile.openstreetmap.de/{z}/{x}/{y}.png", "max_zoom": 18, "html_attribution": "© OpenStreetMap contributors", "attribution": "(C) OpenStreetMap contributors", "name": "OpenStreetMap.DE"}, "CH": {"thumbnailPath": "rasterlayer_gallery/OpenStreetMap-CH.png", "url": "https://tile.osm.ch/switzerland/{z}/{x}/{y}.png", "max_zoom": 18, "html_attribution": "© OpenStreetMap contributors", "attribution": "(C) OpenStreetMap contributors", "bounds": [[45, 5], [48, 11]], "name": "OpenStreetMap.CH"}, "France": {"thumbnailPath": "rasterlayer_gallery/OpenStreetMap-France.png", "url": "https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png", "max_zoom": 20, "html_attribution": "© OpenStreetMap France | © OpenStreetMap contributors", "attribution": "(C) OpenStreetMap France | (C) OpenStreetMap contributors", "name": "OpenStreetMap.France"}, "HOT": {"thumbnailPath": "rasterlayer_gallery/OpenStreetMap-HOT.png", "url": "https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png", "max_zoom": 19, "html_attribution": "© OpenStreetMap contributors, Tiles style by Humanitarian OpenStreetMap Team hosted by OpenStreetMap France", "attribution": "(C) OpenStreetMap contributors, Tiles style by Humanitarian OpenStreetMap Team hosted by OpenStreetMap France", "name": "OpenStreetMap.HOT"}, "BZH": {"thumbnailPath": "rasterlayer_gallery/OpenStreetMap-BZH.png", "url": "https://tile.openstreetmap.bzh/br/{z}/{x}/{y}.png", "max_zoom": 19, "html_attribution": "© OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team", "attribution": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team", "bounds": [[46.2, -5.5], [50, 0.7]], "name": "OpenStreetMap.BZH"}, "CAT": {"thumbnailPath": "rasterlayer_gallery/OpenStreetMap-CAT.png", "url": "https://tile.openstreetmap.bzh/ca/{z}/{x}/{y}.png", "max_zoom": 19, "html_attribution": "© OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team", "attribution": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team", "name": "OpenStreetMap.CAT"}}, "NASAGIBS": {"ModisTerraTrueColorCR": {"thumbnailPath": "rasterlayer_gallery/NASAGIBS-ModisTerraTrueColorCR.png", "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "html_attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "bounds": [[-85.0511287776, -179.999999975], [85.0511287776, 179.999999975]], "min_zoom": 1, "max_zoom": 9, "format": "jpg", "time": "2026-01-11", "tilematrixset": "GoogleMapsCompatible_Level", "variant": "MODIS_Terra_CorrectedReflectance_TrueColor", "name": "NASAGIBS.ModisTerraTrueColorCR"}, "ModisTerraBands367CR": {"thumbnailPath": "rasterlayer_gallery/NASAGIBS-ModisTerraBands367CR.png", "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "html_attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "bounds": [[-85.0511287776, -179.999999975], [85.0511287776, 179.999999975]], "min_zoom": 1, "max_zoom": 9, "format": "jpg", "time": "2026-01-11", "tilematrixset": "GoogleMapsCompatible_Level", "variant": "MODIS_Terra_CorrectedReflectance_Bands367", "name": "NASAGIBS.ModisTerraBands367CR"}, "ViirsEarthAtNight2012": {"thumbnailPath": "rasterlayer_gallery/NASAGIBS-ViirsEarthAtNight2012.png", "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "html_attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "bounds": [[-85.0511287776, -179.999999975], [85.0511287776, 179.999999975]], "min_zoom": 1, "max_zoom": 8, "format": "jpg", "time": "2026-01-11", "tilematrixset": "GoogleMapsCompatible_Level", "variant": "VIIRS_CityLights_2012", "name": "NASAGIBS.ViirsEarthAtNight2012"}, "ModisTerraLSTDay": {"thumbnailPath": "rasterlayer_gallery/NASAGIBS-ModisTerraLSTDay.png", "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "html_attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "bounds": [[-85.0511287776, -179.999999975], [85.0511287776, 179.999999975]], "min_zoom": 1, "max_zoom": 7, "format": "png", "time": "2026-01-11", "tilematrixset": "GoogleMapsCompatible_Level", "variant": "MODIS_Terra_Land_Surface_Temp_Day", "opacity": 0.75, "name": "NASAGIBS.ModisTerraLSTDay"}, "ModisTerraSnowCover": {"thumbnailPath": "rasterlayer_gallery/NASAGIBS-ModisTerraSnowCover.png", "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "html_attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "bounds": [[-85.0511287776, -179.999999975], [85.0511287776, 179.999999975]], "min_zoom": 1, "max_zoom": 8, "format": "png", "time": "2026-01-11", "tilematrixset": "GoogleMapsCompatible_Level", "variant": "MODIS_Terra_NDSI_Snow_Cover", "opacity": 0.75, "name": "NASAGIBS.ModisTerraSnowCover"}, "ModisTerraAOD": {"thumbnailPath": "rasterlayer_gallery/NASAGIBS-ModisTerraAOD.png", "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "html_attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "bounds": [[-85.0511287776, -179.999999975], [85.0511287776, 179.999999975]], "min_zoom": 1, "max_zoom": 6, "format": "png", "time": "2026-01-11", "tilematrixset": "GoogleMapsCompatible_Level", "variant": "MODIS_Terra_Aerosol", "opacity": 0.75, "name": "NASAGIBS.ModisTerraAOD"}, "ModisTerraChlorophyll": {"thumbnailPath": "rasterlayer_gallery/NASAGIBS-ModisTerraChlorophyll.png", "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "html_attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "bounds": [[-85.0511287776, -179.999999975], [85.0511287776, 179.999999975]], "min_zoom": 1, "max_zoom": 7, "format": "png", "time": "2026-01-11", "tilematrixset": "GoogleMapsCompatible_Level", "variant": "MODIS_Terra_L2_Chlorophyll_A", "opacity": 0.75, "name": "NASAGIBS.ModisTerraChlorophyll", "status": "broken"}, "ModisTerraBands721CR": {"thumbnailPath": "rasterlayer_gallery/NASAGIBS-ModisTerraBands721CR.png", "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Terra_CorrectedReflectance_Bands721/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "max_zoom": 9, "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "html_attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "name": "NASAGIBS.ModisTerraBands721CR", "time": "2026-01-11"}, "ModisAquaTrueColorCR": {"thumbnailPath": "rasterlayer_gallery/NASAGIBS-ModisAquaTrueColorCR.png", "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Aqua_CorrectedReflectance_TrueColor/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "max_zoom": 9, "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "html_attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "name": "NASAGIBS.ModisAquaTrueColorCR", "time": "2026-01-11"}, "ModisAquaBands721CR": {"thumbnailPath": "rasterlayer_gallery/NASAGIBS-ModisAquaBands721CR.png", "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Aqua_CorrectedReflectance_Bands721/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "max_zoom": 9, "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "html_attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "name": "NASAGIBS.ModisAquaBands721CR", "time": "2026-01-11"}, "ViirsTrueColorCR": {"thumbnailPath": "rasterlayer_gallery/NASAGIBS-ViirsTrueColorCR.png", "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/VIIRS_SNPP_CorrectedReflectance_TrueColor/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "max_zoom": 9, "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "html_attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "name": "NASAGIBS.ViirsTrueColorCR", "time": "2026-01-11"}, "ASTER_GDEM_Greyscale_Shaded_Relief": {"thumbnailPath": "rasterlayer_gallery/NASAGIBS-ASTER_GDEM_Greyscale_Shaded_Relief.png", "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/ASTER_GDEM_Greyscale_Shaded_Relief/default/GoogleMapsCompatible_Level12/{z}/{y}/{x}.jpg", "max_zoom": 12, "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "html_attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "name": "NASAGIBS.ASTER_GDEM_Greyscale_Shaded_Relief"}}, "USGS": {"USTopo": {"thumbnailPath": "rasterlayer_gallery/USGS-USTopo.png", "url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}", "max_zoom": 20, "html_attribution": "Tiles courtesy of the U.S. Geological Survey", "attribution": "Tiles courtesy of the U.S. Geological Survey", "name": "USGS.USTopo"}, "USImagery": {"thumbnailPath": "rasterlayer_gallery/USGS-USImagery.png", "url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}", "max_zoom": 20, "html_attribution": "Tiles courtesy of the U.S. Geological Survey", "attribution": "Tiles courtesy of the U.S. Geological Survey", "name": "USGS.USImagery"}, "USImageryTopo": {"thumbnailPath": "rasterlayer_gallery/USGS-USImageryTopo.png", "url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryTopo/MapServer/tile/{z}/{y}/{x}", "max_zoom": 20, "html_attribution": "Tiles courtesy of the U.S. Geological Survey", "attribution": "Tiles courtesy of the U.S. Geological Survey", "name": "USGS.USImageryTopo"}}, "WaymarkedTrails": {"hiking": {"thumbnailPath": "rasterlayer_gallery/WaymarkedTrails-hiking.png", "url": "https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png", "max_zoom": 18, "html_attribution": "Map data: © OpenStreetMap contributors | Map style: © waymarkedtrails.org (CC-BY-SA)", "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)", "variant": "hiking", "name": "WaymarkedTrails.hiking"}, "cycling": {"thumbnailPath": "rasterlayer_gallery/WaymarkedTrails-cycling.png", "url": "https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png", "max_zoom": 18, "html_attribution": "Map data: © OpenStreetMap contributors | Map style: © waymarkedtrails.org (CC-BY-SA)", "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)", "variant": "cycling", "name": "WaymarkedTrails.cycling"}, "mtb": {"thumbnailPath": "rasterlayer_gallery/WaymarkedTrails-mtb.png", "url": "https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png", "max_zoom": 18, "html_attribution": "Map data: © OpenStreetMap contributors | Map style: © waymarkedtrails.org (CC-BY-SA)", "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)", "variant": "mtb", "name": "WaymarkedTrails.mtb"}, "slopes": {"thumbnailPath": "rasterlayer_gallery/WaymarkedTrails-slopes.png", "url": "https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png", "max_zoom": 18, "html_attribution": "Map data: © OpenStreetMap contributors | Map style: © waymarkedtrails.org (CC-BY-SA)", "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)", "variant": "slopes", "name": "WaymarkedTrails.slopes"}, "riding": {"thumbnailPath": "rasterlayer_gallery/WaymarkedTrails-riding.png", "url": "https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png", "max_zoom": 18, "html_attribution": "Map data: © OpenStreetMap contributors | Map style: © waymarkedtrails.org (CC-BY-SA)", "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)", "variant": "riding", "name": "WaymarkedTrails.riding"}, "skating": {"thumbnailPath": "rasterlayer_gallery/WaymarkedTrails-skating.png", "url": "https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png", "max_zoom": 18, "html_attribution": "Map data: © OpenStreetMap contributors | Map style: © waymarkedtrails.org (CC-BY-SA)", "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)", "variant": "skating", "name": "WaymarkedTrails.skating"}}, "Gaode": {"Normal": {"thumbnailPath": "rasterlayer_gallery/Gaode-Normal.png", "url": "http://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}", "max_zoom": 19, "attribution": "© Gaode.com", "html_attribution": "© Gaode.com", "name": "Gaode.Normal"}, "Satellite": {"thumbnailPath": "rasterlayer_gallery/Gaode-Satellite.png", "url": "http://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}", "max_zoom": 19, "attribution": "© Gaode.com", "html_attribution": "© Gaode.com", "name": "Gaode.Satellite"}}, "Strava": {"All": {"thumbnailPath": "rasterlayer_gallery/Strava-All.png", "url": "https://heatmap-external-a.strava.com/tiles/all/hot/{z}/{x}/{y}.png", "max_zoom": 15, "attribution": "Map tiles by Strava 2021", "html_attribution": "Map tiles by Strava 2021", "name": "Strava.All"}, "Ride": {"thumbnailPath": "rasterlayer_gallery/Strava-Ride.png", "url": "https://heatmap-external-a.strava.com/tiles/ride/hot/{z}/{x}/{y}.png", "max_zoom": 15, "attribution": "Map tiles by Strava 2021", "html_attribution": "Map tiles by Strava 2021", "name": "Strava.Ride"}, "Run": {"thumbnailPath": "rasterlayer_gallery/Strava-Run.png", "url": "https://heatmap-external-a.strava.com/tiles/run/bluered/{z}/{x}/{y}.png", "max_zoom": 15, "attribution": "Map tiles by Strava 2021", "html_attribution": "Map tiles by Strava 2021", "name": "Strava.Run"}, "Water": {"thumbnailPath": "rasterlayer_gallery/Strava-Water.png", "url": "https://heatmap-external-a.strava.com/tiles/water/blue/{z}/{x}/{y}.png", "max_zoom": 15, "attribution": "Map tiles by Strava 2021", "html_attribution": "Map tiles by Strava 2021", "name": "Strava.Water"}, "Winter": {"thumbnailPath": "rasterlayer_gallery/Strava-Winter.png", "url": "https://heatmap-external-a.strava.com/tiles/winter/hot/{z}/{x}/{y}.png", "max_zoom": 15, "attribution": "Map tiles by Strava 2021", "html_attribution": "Map tiles by Strava 2021", "name": "Strava.Winter"}}, "OPNVKarte": {"thumbnailPath": "rasterlayer_gallery/OPNVKarte.png", "url": "https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png", "max_zoom": 18, "html_attribution": "Map memomaps.de CC-BY-SA, map data © OpenStreetMap contributors", "attribution": "Map memomaps.de CC-BY-SA, map data (C) OpenStreetMap contributors", "name": "OPNVKarte"}, "OpenTopoMap": {"thumbnailPath": "rasterlayer_gallery/OpenTopoMap.png", "url": "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", "max_zoom": 17, "html_attribution": "Map data: © OpenStreetMap contributors, SRTM | Map style: © OpenTopoMap (CC-BY-SA)", "attribution": "Map data: (C) OpenStreetMap contributors, SRTM | Map style: (C) OpenTopoMap (CC-BY-SA)", "name": "OpenTopoMap"}, "OpenRailwayMap": {"thumbnailPath": "rasterlayer_gallery/OpenRailwayMap.png", "url": "https://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png", "max_zoom": 19, "html_attribution": "Map data: © OpenStreetMap contributors | Map style: © OpenRailwayMap (CC-BY-SA)", "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) OpenRailwayMap (CC-BY-SA)", "name": "OpenRailwayMap"}, "Esri": {"WorldStreetMap": {"thumbnailPath": "rasterlayer_gallery/Esri-WorldStreetMap.png", "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "variant": "World_Street_Map", "html_attribution": "Tiles © Esri — Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012", "attribution": "Tiles (C) Esri -- Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012", "name": "Esri.WorldStreetMap"}, "WorldTopoMap": {"thumbnailPath": "rasterlayer_gallery/Esri-WorldTopoMap.png", "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "variant": "World_Topo_Map", "html_attribution": "Tiles © Esri — Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community", "attribution": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community", "name": "Esri.WorldTopoMap"}, "WorldImagery": {"thumbnailPath": "rasterlayer_gallery/Esri-WorldImagery.png", "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "variant": "World_Imagery", "html_attribution": "Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community", "attribution": "Tiles (C) Esri -- Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community", "name": "Esri.WorldImagery"}, "WorldTerrain": {"thumbnailPath": "rasterlayer_gallery/Esri-WorldTerrain.png", "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "variant": "World_Terrain_Base", "html_attribution": "Tiles © Esri — Source: USGS, Esri, TANA, DeLorme, and NPS", "attribution": "Tiles (C) Esri -- Source: USGS, Esri, TANA, DeLorme, and NPS", "max_zoom": 13, "name": "Esri.WorldTerrain"}, "WorldShadedRelief": {"thumbnailPath": "rasterlayer_gallery/Esri-WorldShadedRelief.png", "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "variant": "World_Shaded_Relief", "html_attribution": "Tiles © Esri — Source: Esri", "attribution": "Tiles (C) Esri -- Source: Esri", "max_zoom": 13, "name": "Esri.WorldShadedRelief"}, "WorldPhysical": {"thumbnailPath": "rasterlayer_gallery/Esri-WorldPhysical.png", "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "variant": "World_Physical_Map", "html_attribution": "Tiles © Esri — Source: US National Park Service", "attribution": "Tiles (C) Esri -- Source: US National Park Service", "max_zoom": 8, "name": "Esri.WorldPhysical"}, "OceanBasemap": {"thumbnailPath": "rasterlayer_gallery/Esri-OceanBasemap.png", "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "variant": "Ocean/World_Ocean_Base", "html_attribution": "Tiles © Esri — Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri", "attribution": "Tiles (C) Esri -- Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri", "max_zoom": 13, "name": "Esri.OceanBasemap"}, "NatGeoWorldMap": {"thumbnailPath": "rasterlayer_gallery/Esri-NatGeoWorldMap.png", "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "variant": "NatGeo_World_Map", "html_attribution": "Tiles © Esri — National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC", "attribution": "Tiles (C) Esri -- National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC", "max_zoom": 16, "name": "Esri.NatGeoWorldMap"}, "WorldGrayCanvas": {"thumbnailPath": "rasterlayer_gallery/Esri-WorldGrayCanvas.png", "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "variant": "Canvas/World_Light_Gray_Base", "html_attribution": "Tiles © Esri — Esri, DeLorme, NAVTEQ", "attribution": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ", "max_zoom": 16, "name": "Esri.WorldGrayCanvas"}}, "MacroStrat": {"CartoRaster": {"thumbnailPath": "rasterlayer_gallery/MacroStrat-CartoRaster.png", "name": "MacroStrat.CartoRaster", "url": "https://tiles.macrostrat.org/carto/{z}/{x}/{y}.png", "attribution": "\u00a9 Geologic data \u00a9 Macrostrat raster layer (CC\u2011BY 4.0)", "max_zoom": 18}}} diff --git a/packages/base/rasterlayer_gallery_generator.py b/packages/base/rasterlayer_gallery_generator.py deleted file mode 100644 index 33d75e888..000000000 --- a/packages/base/rasterlayer_gallery_generator.py +++ /dev/null @@ -1,259 +0,0 @@ -from datetime import date, timedelta -import json -from io import BytesIO -import os -import subprocess - -import requests -from PIL import Image -import mercantile -from xyzservices import providers, TileProvider - -THUMBNAILS_LOCATION = "rasterlayer_gallery" - - -def fetch_tile(url_template, x, y, z, s="a"): - """ - Fetch a tile from the given URL template. - """ - url = url_template.format(x=x, y=y, z=z, s=s) - print(f" Fetch {url}") - response = requests.get( - url, headers={"Content-Type": "application/json", "User-Agent": "JupyterGIS"} - ) - response.raise_for_status() - return Image.open(BytesIO(response.content)) - - -def latlng_to_tile(lat, lng, zoom): - """ - Convert latitude/longitude to tile coordinates. - """ - tile = mercantile.tile(lng, lat, zoom, True) - return tile.x, tile.y - - -def create_thumbnail( - url_template, - lat, - lng, - zoom, - thumbnail_path, - tile_size=256, - thumbnail_size=(512, 512), -): - """ - Create a thumbnail for the specified location and zoom level. - """ - # Skip if thumbnail already exists - if os.path.exists(thumbnail_path): - return Image.open(thumbnail_path) - x, y = latlng_to_tile(lat, lng, zoom) - - # Fetch the tiles (2x2 grid for the thumbnail) - tiles = [] - for dy in range(2): - row = [] - for dx in range(2): - tile_x, tile_y = x + dx, y + dy - tile = fetch_tile(url_template, tile_x, tile_y, zoom) - row.append(tile) - tiles.append(row) - - # Create a blank image for the thumbnail - thumbnail = Image.new("RGB", (2 * tile_size, 2 * tile_size)) - - # Paste the tiles into the thumbnail image - for dy, row in enumerate(tiles): - for dx, tile in enumerate(row): - thumbnail.paste(tile, (dx * tile_size, dy * tile_size)) - - # Resize to the desired thumbnail size - thumbnail = thumbnail.resize(thumbnail_size, Image.LANCZOS) - return thumbnail - - -yesterday = (date.today() - timedelta(days=1)).strftime("%Y-%m-%d") - -# San Francisco -san_francisco = {"lat": 37.7749, "lng": -122.4194, "zoom": 5} - -middle_europe = {"lat": 48.63290858589535, "lng": -350.068359375, "zoom": 4} - -# Default -france = {"lat": 47.040182144806664, "lng": 1.2963867187500002, "zoom": 5} - -thumbnails_providers_positions = { - "OpenStreetMap": { - "Special Rules": { - "BZH": {"lat": 47.76702233051035, "lng": -3.4675598144531254, "zoom": 8}, - "CH": {"lat": 46.8182, "lng": 8.2275, "zoom": 8}, - "DE": {"lat": 51.1657, "lng": 10.4515, "zoom": 8}, - "France": france, - "HOT": france, - }, - "Default": france, - }, - "NASAGIBS": {"Special Rules": {}, "Default": france}, - # 'JusticeMap': { - # 'Special Rules': {}, - # 'Default': san_francisco, - # }, - "USGS": { - "Special Rules": {}, - "Default": san_francisco, - }, - "WaymarkedTrails": { - "Special Rules": {}, - "Default": france, - }, - "Gaode": { - "Special Rules": {}, - "Default": san_francisco, - }, - "Strava": {"Special Rules": {}, "Default": france, "TileSize": 512}, - "OPNVKarte": { - "Special Rules": {}, - "Default": san_francisco, - }, - "OpenTopoMap": { - "Special Rules": {}, - "Default": san_francisco, - }, - "OpenRailwayMap": {"Special Rules": {}, "Default": san_francisco, "TileSize": 512}, - # 'OpenFireMap': { - # 'Special Rules': {}, - # 'Default': san_francisco, - # }, - # 'SafeCast': { - # 'Special Rules': {}, - # 'Default': san_francisco, - # }, - "Esri": { - "Special Rules": {}, - "Default": san_francisco, - }, - "MacroStrat": { - "Special Rules": { - "CartoRaster": france, - }, - "Default": france, - }, -} - - -def download_thumbnail(url_template, name, position, tile_size): - file_path = f"{THUMBNAILS_LOCATION}/{name}.png" - thumbnail = create_thumbnail( - url_template, - position["lat"], - position["lng"], - position["zoom"], - file_path, - tile_size, - ) - thumbnail.save(file_path) - return file_path - - -# This is the JSON we'll generate for the raster gallery -raster_provider_gallery = {} - -# Create thumbnail dir if needed -if not os.path.exists(THUMBNAILS_LOCATION): - os.makedirs(THUMBNAILS_LOCATION) - -custom_providers = providers.copy() - -custom_providers["MacroStrat"] = { - "CartoRaster": TileProvider( - name="MacroStrat.CartoRaster", - url="https://tiles.macrostrat.org/carto/{z}/{x}/{y}.png", - attribution="© Geologic data © Macrostrat raster layer (CC‑BY 4.0)", - max_zoom=18, - ) -} - -# Fetch thumbnails and populate the dictionary -for provider in thumbnails_providers_positions.keys(): - xyzprovider = custom_providers[provider] - - if "url" in xyzprovider.keys(): - print(f"Process {provider}") - - try: - name = provider - url_template = xyzprovider["url"] - - if name in thumbnails_providers_positions[provider]["Special Rules"].keys(): - position = thumbnails_providers_positions[provider]["Special Rules"][ - name - ] - else: - position = thumbnails_providers_positions[provider]["Default"] - - tile_size = thumbnails_providers_positions[provider].get("TileSize", 256) - - file_path = download_thumbnail(url_template, name, position, tile_size) - raster_provider_gallery[name] = dict( - # jgisname=name, - thumbnailPath=file_path, - **xyzprovider, - ) - if "time" in raster_provider_gallery[name]: - raster_provider_gallery[name]["time"] = yesterday - - except Exception as e: - print("Failed...", e) - - continue - - providers_maps = {} - for map_name in xyzprovider.keys(): - print(f"Process {provider} {map_name}") - - try: - if ( - map_name - in thumbnails_providers_positions[provider]["Special Rules"].keys() - ): - position = thumbnails_providers_positions[provider]["Special Rules"][ - map_name - ] - else: - position = thumbnails_providers_positions[provider]["Default"] - - tile_provider = xyzprovider[map_name] - - if "crs" in tile_provider or "apikey" in tile_provider: - # TODO Support other projections once we have another viewer than maplibre - # TODO Support api keys - continue - - name = tile_provider["name"].replace(".", "-") - url_template = tile_provider.build_url(time=yesterday) - tile_size = thumbnails_providers_positions[provider].get("TileSize", 256) - - file_path = download_thumbnail(url_template, name, position, tile_size) - providers_maps[map_name] = dict( - # jgisname=name, - thumbnailPath=file_path, - **tile_provider, - ) - if "time" in providers_maps[map_name]: - providers_maps[map_name]["time"] = yesterday - - raster_provider_gallery[provider] = providers_maps - - except Exception as e: - print("Failed...", e) - -# Save JSON repr -with open(f"{THUMBNAILS_LOCATION}/raster_layer_gallery.json", "w") as f: - json.dump(raster_provider_gallery, f) - - -# compress images -cmd = f"shopt -s globstar && mogrify -resize 50% {THUMBNAILS_LOCATION}/*.png && optipng {THUMBNAILS_LOCATION}/*.png" - -subprocess.run(["bash", "-lc", cmd], check=True) diff --git a/packages/base/src/dialogs/layerBrowserDialog.tsx b/packages/base/src/dialogs/layerBrowserDialog.tsx index e8af9cc3f..386f9972f 100644 --- a/packages/base/src/dialogs/layerBrowserDialog.tsx +++ b/packages/base/src/dialogs/layerBrowserDialog.tsx @@ -7,7 +7,7 @@ import { IJGISLayerDocChange, IJGISSource, IJupyterGISModel, - IRasterLayerGalleryEntry, + ILayerGalleryEntry, } from '@jupytergis/schema'; import { Dialog } from '@jupyterlab/apputils'; import { PromiseDelegate, UUID } from '@lumino/coreutils'; @@ -15,11 +15,11 @@ import { Signal } from '@lumino/signaling'; import React, { ChangeEvent, MouseEvent, useEffect, useState } from 'react'; import { CreationFormWrapper } from './layerCreationFormDialog'; -import CUSTOM_RASTER_IMAGE from '../../rasterlayer_gallery/custom_raster.png'; +import CUSTOM_RASTER_IMAGE from '../../layer_gallery/custom_raster.png'; interface ILayerBrowserDialogProps { model: IJupyterGISModel; - registry: IRasterLayerGalleryEntry[]; + registry: ILayerGalleryEntry[]; formSchemaRegistry: IJGISFormSchemaRegistry; okSignalPromise: PromiseDelegate, number>>; cancel: () => void; @@ -39,9 +39,9 @@ export const LayerBrowserComponent: React.FC = ({ const [creatingCustomRaster, setCreatingCustomRaster] = useState(false); const [galleryWithCategory, setGalleryWithCategory] = - useState(registry); + useState(registry); - const providers = [...new Set(registry.map(item => item.source.provider))]; + const providers = [...new Set(registry.map(item => item.provider))]; const filteredGallery = galleryWithCategory.filter(item => item.name.toLowerCase().includes(searchTerm), @@ -80,9 +80,7 @@ export const LayerBrowserComponent: React.FC = ({ const filteredGallery = sameAsOld ? registry - : registry.filter(item => - item.source.provider?.includes(categoryTab.innerText), - ); + : registry.filter(item => item.provider?.includes(categoryTab.innerText)); setGalleryWithCategory(filteredGallery); setSearchTerm(''); @@ -97,20 +95,18 @@ export const LayerBrowserComponent: React.FC = ({ * Add tile layer and source to model * @param tile Tile to add */ - const handleTileClick = (tile: IRasterLayerGalleryEntry) => { + const handleTileClick = (tile: ILayerGalleryEntry) => { const sourceId = UUID.uuid4(); const sourceModel: IJGISSource = { - type: 'RasterSource', + type: tile.sourceType, name: tile.name, - parameters: tile.source, + parameters: tile.sourceParameters, }; const layerModel: IJGISLayer = { - type: 'RasterLayer', - parameters: { - source: sourceId, - }, + type: tile.layerType, + parameters: { ...tile.layerParameters, source: sourceId }, visible: true, name: tile.name + ' Layer', }; @@ -232,7 +228,7 @@ export const LayerBrowserComponent: React.FC = ({

*/}

- {tile.source.attribution} + {tile.sourceParameters.attribution}

@@ -244,7 +240,7 @@ export const LayerBrowserComponent: React.FC = ({ export interface ILayerBrowserOptions { model: IJupyterGISModel; - registry: IRasterLayerGalleryEntry[]; + registry: ILayerGalleryEntry[]; formSchemaRegistry: IJGISFormSchemaRegistry; } diff --git a/packages/base/src/tools.ts b/packages/base/src/tools.ts index eda87e541..d2b808359 100644 --- a/packages/base/src/tools.ts +++ b/packages/base/src/tools.ts @@ -4,7 +4,7 @@ import { IJGISOptions, IJGISSource, IJupyterGISModel, - IRasterLayerGalleryEntry, + ILayerGalleryEntry, SourceType, } from '@jupytergis/schema'; import { showErrorMessage } from '@jupyterlab/apputils'; @@ -15,7 +15,7 @@ import { compressors } from 'hyparquet-compressors'; import Protobuf from 'pbf'; import shp from 'shpjs'; -import RASTER_LAYER_GALLERY from '@/rasterlayer_gallery/raster_layer_gallery.json'; +import LAYER_GALLERY from '@/layer_gallery/layer_gallery.json'; export const debounce = ( func: CallableFunction, @@ -151,7 +151,7 @@ export function deepCopy>(value: T): T { export function createDefaultLayerRegistry( layerBrowserRegistry: IJGISLayerBrowserRegistry, ): void { - const RASTER_THUMBNAILS: { [key: string]: string } = {}; + const LAYER_THUMBNAILS: { [key: string]: string } = {}; /** * Generate object to hold thumbnail URLs @@ -159,28 +159,30 @@ export function createDefaultLayerRegistry( const importAll = (r: __WebpackModuleApi.RequireContext) => { r.keys().forEach(key => { const imageName = key.replace('./', '').replace(/\.\w+$/, ''); - RASTER_THUMBNAILS[imageName] = r(key); + LAYER_THUMBNAILS[imageName] = r(key); }); }; const context = require.context( - '../rasterlayer_gallery', + '../layer_gallery', false, /\.(png|jpe?g|gif|svg)$/, ); importAll(context); - for (const entry of Object.keys(RASTER_LAYER_GALLERY)) { - const xyzprovider: any = (RASTER_LAYER_GALLERY as any)[entry]; + for (const entry of Object.keys(LAYER_GALLERY)) { + const layerProvider: any = (LAYER_GALLERY as any)[entry]; - if ('url' in xyzprovider) { - const tile = convertToRegistryEntry(entry, xyzprovider); + if ('thumbnailPath' in layerProvider) { + /*flat layer provider*/ + const tile = convertToRegistryEntry(entry, layerProvider); layerBrowserRegistry.addRegistryLayer(tile); } else { - Object.keys(xyzprovider).forEach(mapName => { + /* nested layer provider */ + Object.keys(layerProvider).forEach(mapName => { const tile = convertToRegistryEntry( - xyzprovider[mapName]['name'], - xyzprovider[mapName], + layerProvider[mapName]['name'], + layerProvider[mapName], entry, ); @@ -194,40 +196,44 @@ export function createDefaultLayerRegistry( * Parse tile information from providers to be usable in the layer registry * * @param entry - The name of the entry, which may also serve as the default provider name if none is specified. - * @param xyzprovider - An object containing the XYZ provider's details, including name, URL, zoom levels, attribution, and possibly other properties relevant to the provider. + * @param layerProvider - An object containing the provider's details, including name, URL, zoom levels, attribution, and possibly other properties relevant to the provider. * @param provider - Optional. Specifies the provider name. If not provided, the `entry` parameter is used as the default provider name. * @returns - An object representing the registry entry */ function convertToRegistryEntry( entry: string, - xyzprovider: { [x: string]: any }, + layerProvider: { [x: string]: any }, provider?: string | undefined, - ): IRasterLayerGalleryEntry { + ): ILayerGalleryEntry { const urlParameters: any = {}; - if (xyzprovider.time) { - urlParameters.time = xyzprovider.time; + + if (layerProvider.time) { + urlParameters.time = layerProvider.time; + } + if (layerProvider.variant) { + urlParameters.variant = layerProvider.variant; } - if (xyzprovider.variant) { - urlParameters.variant = xyzprovider.variant; + if (layerProvider.tilematrixset) { + urlParameters.tilematrixset = layerProvider.tilematrixset; } - if (xyzprovider.tilematrixset) { - urlParameters.tilematrixset = xyzprovider.tilematrixset; + if (layerProvider.format) { + urlParameters.format = layerProvider.format; } - if (xyzprovider.format) { - urlParameters.format = xyzprovider.format; + + if (!layerProvider.layerType || !layerProvider.sourceType) { + throw new Error( + `Invalid layer_gallery entry "${entry}": missing layerType or sourceType`, + ); } return { name: entry, - thumbnail: RASTER_THUMBNAILS[xyzprovider['name'].replace('.', '-')], - source: { - url: xyzprovider['url'], - minZoom: xyzprovider['min_zoom'] || 0, - maxZoom: xyzprovider['max_zoom'] || 24, - attribution: xyzprovider['attribution'] || '', - provider: provider ?? entry, - urlParameters, - }, + thumbnail: LAYER_THUMBNAILS[layerProvider['name'].replace('.', '-')], + sourceType: layerProvider['sourceType'], + layerType: layerProvider['layerType'], + sourceParameters: layerProvider['sourceParameters'], + layerParameters: layerProvider['layerParameters'], + provider: provider ?? entry.split('.', 1)[0], }; } } diff --git a/packages/schema/src/interfaces.ts b/packages/schema/src/interfaces.ts index f1990e934..2e0ab1b7a 100644 --- a/packages/schema/src/interfaces.ts +++ b/packages/schema/src/interfaces.ts @@ -27,10 +27,17 @@ import { IJGISSource, IJGISSources, IJGISStoryMap, + LayerType, SourceType, } from './_interface/project/jgis'; -import { IRasterSource } from './_interface/project/sources/rasterSource'; -import { Modes } from './types'; + +import { + IRasterLayer, + IRasterSource, + IVectorTileLayer, + IVectorTileSource, + Modes, +} from './types'; export { IGeoJSONSource } from './_interface/project/sources/geoJsonSource'; export interface IJGISStoryMaps { @@ -356,21 +363,24 @@ export interface IJGISExternalCommandRegistry { } /** - * Defines the structure for entries in a raster layer gallery. - * Each entry consists of a name, a thumbnail URL, and source information. - * The source information is expected to conform to the IRasterSource interface. + * Defines the structure for entries in a layer gallery. * - * @interface IRasterLayerGalleryEntry + * @interface ILayerGalleryEntry */ -export interface IRasterLayerGalleryEntry { + +export type ILayerGalleryEntry = { name: string; thumbnail: string; - source: IRasterSource; -} + layerType: LayerType; + layerParameters: IVectorTileLayer | IRasterLayer; + sourceType: SourceType; + sourceParameters: IVectorTileSource | IRasterSource; + provider: string; +}; export interface IJGISLayerBrowserRegistry { - getRegistryLayers(): IRasterLayerGalleryEntry[]; - addRegistryLayer(data: IRasterLayerGalleryEntry): void; + getRegistryLayers(): ILayerGalleryEntry[]; + addRegistryLayer(data: ILayerGalleryEntry): void; removeRegistryLayer(name: string): void; clearRegistry(): void; } diff --git a/python/jupytergis_core/src/layerBrowserRegistry.ts b/python/jupytergis_core/src/layerBrowserRegistry.ts index fdd08e7ac..4151ecdf4 100644 --- a/python/jupytergis_core/src/layerBrowserRegistry.ts +++ b/python/jupytergis_core/src/layerBrowserRegistry.ts @@ -1,6 +1,6 @@ import { IJGISLayerBrowserRegistry, - IRasterLayerGalleryEntry, + ILayerGalleryEntry, } from '@jupytergis/schema'; /** @@ -11,7 +11,7 @@ import { * @implements IJGISLayerBrowserRegistry */ export class JupyterGISLayerBrowserRegistry implements IJGISLayerBrowserRegistry { - private _registry: IRasterLayerGalleryEntry[]; + private _registry: ILayerGalleryEntry[]; constructor() { this._registry = []; @@ -22,7 +22,7 @@ export class JupyterGISLayerBrowserRegistry implements IJGISLayerBrowserRegistry * Returns a copy of the internal registry array to prevent external modifications. * @returns The current state of the registry layers. */ - getRegistryLayers(): IRasterLayerGalleryEntry[] { + getRegistryLayers(): ILayerGalleryEntry[] { return [...this._registry]; } @@ -30,7 +30,7 @@ export class JupyterGISLayerBrowserRegistry implements IJGISLayerBrowserRegistry * Adds a new raster layer gallery entry to the registry. * @param data - The raster layer gallery entry to add. */ - addRegistryLayer(data: IRasterLayerGalleryEntry): void { + addRegistryLayer(data: ILayerGalleryEntry): void { this._registry.push(data); } From 52159764d552b7e3dbf83fa671f5535656f07588 Mon Sep 17 00:00:00 2001 From: Florence Haudin Date: Wed, 28 Jan 2026 20:09:24 +0100 Subject: [PATCH 2/8] Move provider_config to a json file. --- packages/base/layer_gallery/layer_config.json | 395 ++++++++++++ .../base/layer_gallery/layer_gallery.json | 566 +++++++++++++++++- packages/base/layer_gallery_generator.py | 322 +--------- 3 files changed, 968 insertions(+), 315 deletions(-) create mode 100644 packages/base/layer_gallery/layer_config.json diff --git a/packages/base/layer_gallery/layer_config.json b/packages/base/layer_gallery/layer_config.json new file mode 100644 index 000000000..7a437fb1b --- /dev/null +++ b/packages/base/layer_gallery/layer_config.json @@ -0,0 +1,395 @@ +{ + "OpenStreetMap": { + "Mapnik": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "BZH": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": { + "lat": 47.76702233051035, + "lng": -3.4675598144531254, + "zoom": 8 + }, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "CAT": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "CH": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": { "lat": 46.8182, "lng": 8.2275, "zoom": 8 }, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "DE": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": { "lat": 51.1657, "lng": 10.4515, "zoom": 8 }, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "France": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "HOT": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + } + }, + "NASAGIBS": { + "ModisTerraTrueColorCR": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "ModisTerraBands367CR": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "ViirsEarthAtNight2012": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "ModisTerraLSTDay": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "ModisTerraSnowCover": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "ModisTerraAOD": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "ModisTerraChlorophyll": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + + "ModisTerraBands721CR": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "ModisAquaTrueColorCR": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "ModisAquaBands721CR": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "ViirsTrueColorCR": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "ASTER_GDEM_Greyscale_Shaded_Relief": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + } + }, + "USGS": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + } + }, + + "Gaode": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + } + }, + "Strava": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + }, + "TileSize": 512 + } + }, + + "OPNVKarte": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + } + }, + "OpenTopoMap": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + } + }, + "OpenRailwayMap": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 }, + "TileSize": 512 + } + }, + "Esri": { + "WorldStreetMap": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + } + }, + "WorldTopoMap": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + } + }, + "WorldImagery": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + } + }, + "WorldTerrain": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + } + }, + "WorldShadedRelief": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + } + }, + "WorldPhysical": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + } + }, + "OceanBasemap": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + } + }, + "NatGeoWorldMap": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + } + }, + "WorldGrayCanvas": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + } + } + }, + "MacroStrat": { + "CartoRaster": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "CartoVector": { + "layerType": "VectorTileLayer", + "sourceType": "VectorTileSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + } + } +} diff --git a/packages/base/layer_gallery/layer_gallery.json b/packages/base/layer_gallery/layer_gallery.json index c29644abb..daa111acf 100644 --- a/packages/base/layer_gallery/layer_gallery.json +++ b/packages/base/layer_gallery/layer_gallery.json @@ -1 +1,565 @@ -{"OpenStreetMap": {"Mapnik": {"thumbnailPath": "layer_gallery/OpenStreetMap-Mapnik.png", "name": "OpenStreetMap.Mapnik", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors", "maxZoom": 19, "minZoom": null}, "layerParameters": {"opacity": 1}}, "BZH": {"thumbnailPath": "layer_gallery/OpenStreetMap-BZH.png", "name": "OpenStreetMap.BZH", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tile.openstreetmap.bzh/br/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team", "maxZoom": 19, "minZoom": null}, "layerParameters": {"opacity": 1}}, "CAT": {"thumbnailPath": "layer_gallery/OpenStreetMap-CAT.png", "name": "OpenStreetMap.CAT", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tile.openstreetmap.bzh/ca/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team", "maxZoom": 19, "minZoom": null}, "layerParameters": {"opacity": 1}}, "CH": {"thumbnailPath": "layer_gallery/OpenStreetMap-CH.png", "name": "OpenStreetMap.CH", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tile.osm.ch/switzerland/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors", "maxZoom": 18, "minZoom": null}, "layerParameters": {"opacity": 1}}, "DE": {"thumbnailPath": "layer_gallery/OpenStreetMap-DE.png", "name": "OpenStreetMap.DE", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tile.openstreetmap.de/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors", "maxZoom": 18, "minZoom": null}, "layerParameters": {"opacity": 1}}, "France": {"thumbnailPath": "layer_gallery/OpenStreetMap-France.png", "name": "OpenStreetMap.France", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap France | (C) OpenStreetMap contributors", "maxZoom": 20, "minZoom": null}, "layerParameters": {"opacity": 1}}, "HOT": {"thumbnailPath": "layer_gallery/OpenStreetMap-HOT.png", "name": "OpenStreetMap.HOT", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors, Tiles style by Humanitarian OpenStreetMap Team hosted by OpenStreetMap France", "maxZoom": 19, "minZoom": null}, "layerParameters": {"opacity": 1}}}, "NASAGIBS": {"ModisTerraTrueColorCR": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraTrueColorCR.png", "name": "NASAGIBS.ModisTerraTrueColorCR", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, "minZoom": 1}, "layerParameters": {"opacity": 1}}, "ModisTerraBands367CR": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraBands367CR.png", "name": "NASAGIBS.ModisTerraBands367CR", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, "minZoom": 1}, "layerParameters": {"opacity": 1}}, "ViirsEarthAtNight2012": {"thumbnailPath": "layer_gallery/NASAGIBS-ViirsEarthAtNight2012.png", "name": "NASAGIBS.ViirsEarthAtNight2012", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 8, "minZoom": 1}, "layerParameters": {"opacity": 1}}, "ModisTerraLSTDay": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraLSTDay.png", "name": "NASAGIBS.ModisTerraLSTDay", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 7, "minZoom": 1}, "layerParameters": {"opacity": 1}}, "ModisTerraSnowCover": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraSnowCover.png", "name": "NASAGIBS.ModisTerraSnowCover", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 8, "minZoom": 1}, "layerParameters": {"opacity": 1}}, "ModisTerraAOD": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraAOD.png", "name": "NASAGIBS.ModisTerraAOD", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 6, "minZoom": 1}, "layerParameters": {"opacity": 1}}, "ModisTerraChlorophyll": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraChlorophyll.png", "name": "NASAGIBS.ModisTerraChlorophyll", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 7, "minZoom": 1}, "layerParameters": {"opacity": 1}}, "ModisTerraBands721CR": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraBands721CR.png", "name": "NASAGIBS.ModisTerraBands721CR", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Terra_CorrectedReflectance_Bands721/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, "minZoom": null}, "layerParameters": {"opacity": 1}}, "ModisAquaTrueColorCR": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisAquaTrueColorCR.png", "name": "NASAGIBS.ModisAquaTrueColorCR", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Aqua_CorrectedReflectance_TrueColor/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, "minZoom": null}, "layerParameters": {"opacity": 1}}, "ModisAquaBands721CR": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisAquaBands721CR.png", "name": "NASAGIBS.ModisAquaBands721CR", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Aqua_CorrectedReflectance_Bands721/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, "minZoom": null}, "layerParameters": {"opacity": 1}}, "ViirsTrueColorCR": {"thumbnailPath": "layer_gallery/NASAGIBS-ViirsTrueColorCR.png", "name": "NASAGIBS.ViirsTrueColorCR", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/VIIRS_SNPP_CorrectedReflectance_TrueColor/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, "minZoom": null}, "layerParameters": {"opacity": 1}}, "ASTER_GDEM_Greyscale_Shaded_Relief": {"thumbnailPath": "layer_gallery/NASAGIBS-ASTER_GDEM_Greyscale_Shaded_Relief.png", "name": "NASAGIBS.ASTER_GDEM_Greyscale_Shaded_Relief", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/ASTER_GDEM_Greyscale_Shaded_Relief/default/GoogleMapsCompatible_Level12/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 12, "minZoom": null}, "layerParameters": {"opacity": 1}}}, "USGS": {"USTopo": {"thumbnailPath": "layer_gallery/USGS-USTopo.png", "name": "USGS.USTopo", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles courtesy of the U.S. Geological Survey", "maxZoom": 20}, "layerParameters": {"opacity": 1}}, "USImagery": {"thumbnailPath": "layer_gallery/USGS-USImagery.png", "name": "USGS.USImagery", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles courtesy of the U.S. Geological Survey", "maxZoom": 20}, "layerParameters": {"opacity": 1}}, "USImageryTopo": {"thumbnailPath": "layer_gallery/USGS-USImageryTopo.png", "name": "USGS.USImageryTopo", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryTopo/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles courtesy of the U.S. Geological Survey", "maxZoom": 20}, "layerParameters": {"opacity": 1}}}, "Gaode": {"Normal": {"thumbnailPath": "layer_gallery/Gaode-Normal.png", "name": "Gaode.Normal", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "http://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}", "attribution": "© Gaode.com", "maxZoom": 19}, "layerParameters": {"opacity": 1}}, "Satellite": {"thumbnailPath": "layer_gallery/Gaode-Satellite.png", "name": "Gaode.Satellite", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "http://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}", "attribution": "© Gaode.com", "maxZoom": 19}, "layerParameters": {"opacity": 1}}}, "Strava": {"All": {"thumbnailPath": "layer_gallery/Strava-All.png", "name": "Strava.All", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://heatmap-external-a.strava.com/tiles/all/hot/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15}, "layerParameters": {"opacity": 1}}, "Ride": {"thumbnailPath": "layer_gallery/Strava-Ride.png", "name": "Strava.Ride", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://heatmap-external-a.strava.com/tiles/ride/hot/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15}, "layerParameters": {"opacity": 1}}, "Run": {"thumbnailPath": "layer_gallery/Strava-Run.png", "name": "Strava.Run", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://heatmap-external-a.strava.com/tiles/run/bluered/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15}, "layerParameters": {"opacity": 1}}, "Water": {"thumbnailPath": "layer_gallery/Strava-Water.png", "name": "Strava.Water", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://heatmap-external-a.strava.com/tiles/water/blue/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15}, "layerParameters": {"opacity": 1}}, "Winter": {"thumbnailPath": "layer_gallery/Strava-Winter.png", "name": "Strava.Winter", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://heatmap-external-a.strava.com/tiles/winter/hot/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15}, "layerParameters": {"opacity": 1}}}, "OPNVKarte": {"thumbnailPath": "layer_gallery/OPNVKarte.png", "name": "OPNVKarte", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png", "attribution": "Map memomaps.de CC-BY-SA, map data (C) OpenStreetMap contributors", "maxZoom": 18, "minZoom": null}, "layerParameters": {"opacity": 1}}, "OpenTopoMap": {"thumbnailPath": "layer_gallery/OpenTopoMap.png", "name": "OpenTopoMap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", "attribution": "Map data: (C) OpenStreetMap contributors, SRTM | Map style: (C) OpenTopoMap (CC-BY-SA)", "maxZoom": 17, "minZoom": null}, "layerParameters": {"opacity": 1}}, "OpenRailwayMap": {"thumbnailPath": "layer_gallery/OpenRailwayMap.png", "name": "OpenRailwayMap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png", "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) OpenRailwayMap (CC-BY-SA)", "maxZoom": 19, "minZoom": null}, "layerParameters": {"opacity": 1}}, "Esri": {"WorldStreetMap": {"thumbnailPath": "layer_gallery/Esri-WorldStreetMap.png", "name": "Esri.WorldStreetMap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012", "maxZoom": null, "minZoom": null}, "layerParameters": {"opacity": 1}}, "WorldTopoMap": {"thumbnailPath": "layer_gallery/Esri-WorldTopoMap.png", "name": "Esri.WorldTopoMap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community", "maxZoom": null, "minZoom": null}, "layerParameters": {"opacity": 1}}, "WorldImagery": {"thumbnailPath": "layer_gallery/Esri-WorldImagery.png", "name": "Esri.WorldImagery", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community", "maxZoom": null, "minZoom": null}, "layerParameters": {"opacity": 1}}, "WorldTerrain": {"thumbnailPath": "layer_gallery/Esri-WorldTerrain.png", "name": "Esri.WorldTerrain", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: USGS, Esri, TANA, DeLorme, and NPS", "maxZoom": 13, "minZoom": null}, "layerParameters": {"opacity": 1}}, "WorldShadedRelief": {"thumbnailPath": "layer_gallery/Esri-WorldShadedRelief.png", "name": "Esri.WorldShadedRelief", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: Esri", "maxZoom": 13, "minZoom": null}, "layerParameters": {"opacity": 1}}, "WorldPhysical": {"thumbnailPath": "layer_gallery/Esri-WorldPhysical.png", "name": "Esri.WorldPhysical", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: US National Park Service", "maxZoom": 8, "minZoom": null}, "layerParameters": {"opacity": 1}}, "OceanBasemap": {"thumbnailPath": "layer_gallery/Esri-OceanBasemap.png", "name": "Esri.OceanBasemap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri", "maxZoom": 13, "minZoom": null}, "layerParameters": {"opacity": 1}}, "NatGeoWorldMap": {"thumbnailPath": "layer_gallery/Esri-NatGeoWorldMap.png", "name": "Esri.NatGeoWorldMap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC", "maxZoom": 16, "minZoom": null}, "layerParameters": {"opacity": 1}}, "WorldGrayCanvas": {"thumbnailPath": "layer_gallery/Esri-WorldGrayCanvas.png", "name": "Esri.WorldGrayCanvas", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ", "maxZoom": 16, "minZoom": null}, "layerParameters": {"opacity": 1}}}, "MacroStrat": {"CartoRaster": {"thumbnailPath": "layer_gallery/MacroStrat-CartoRaster.png", "name": "MacroStrat.CartoRaster", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tiles.macrostrat.org/carto/{z}/{x}/{y}.png", "attribution": "\u00a9 Geologic data \u00a9 Macrostrat raster layer (CC\u2011BY 4.0)", "maxZoom": 18, "minZoom": null}, "layerParameters": {"opacity": 1}}, "CartoVector": {"thumbnailPath": "layer_gallery/MacroStrat-CartoVector.png", "name": "MacroStrat.CartoVector", "layerType": "VectorTileLayer", "sourceType": "VectorTileSource", "sourceParameters": {"url": "https://tiles.macrostrat.org/carto/{z}/{x}/{y}.mvt", "attribution": "\u00a9 Geologic data \u00a9 Macrostrat vector layer (CC\u2011BY 4.0)", "maxZoom": 18, "minZoom": null}, "layerParameters": {"opacity": 1}}}} \ No newline at end of file +{ + "OpenStreetMap": { + "Mapnik": { + "thumbnailPath": "layer_gallery/OpenStreetMap-Mapnik.png", + "name": "OpenStreetMap.Mapnik", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png", + "attribution": "(C) OpenStreetMap contributors", + "maxZoom": 19, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "BZH": { + "thumbnailPath": "layer_gallery/OpenStreetMap-BZH.png", + "name": "OpenStreetMap.BZH", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://tile.openstreetmap.bzh/br/{z}/{x}/{y}.png", + "attribution": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team", + "maxZoom": 19, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "CAT": { + "thumbnailPath": "layer_gallery/OpenStreetMap-CAT.png", + "name": "OpenStreetMap.CAT", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://tile.openstreetmap.bzh/ca/{z}/{x}/{y}.png", + "attribution": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team", + "maxZoom": 19, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "CH": { + "thumbnailPath": "layer_gallery/OpenStreetMap-CH.png", + "name": "OpenStreetMap.CH", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://tile.osm.ch/switzerland/{z}/{x}/{y}.png", + "attribution": "(C) OpenStreetMap contributors", + "maxZoom": 18, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "DE": { + "thumbnailPath": "layer_gallery/OpenStreetMap-DE.png", + "name": "OpenStreetMap.DE", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://tile.openstreetmap.de/{z}/{x}/{y}.png", + "attribution": "(C) OpenStreetMap contributors", + "maxZoom": 18, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "France": { + "thumbnailPath": "layer_gallery/OpenStreetMap-France.png", + "name": "OpenStreetMap.France", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png", + "attribution": "(C) OpenStreetMap France | (C) OpenStreetMap contributors", + "maxZoom": 20, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "HOT": { + "thumbnailPath": "layer_gallery/OpenStreetMap-HOT.png", + "name": "OpenStreetMap.HOT", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png", + "attribution": "(C) OpenStreetMap contributors, Tiles style by Humanitarian OpenStreetMap Team hosted by OpenStreetMap France", + "maxZoom": 19, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + } + }, + "NASAGIBS": { + "ModisTerraTrueColorCR": { + "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraTrueColorCR.png", + "name": "NASAGIBS.ModisTerraTrueColorCR", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 9, + "minZoom": 1 + }, + "layerParameters": { "opacity": 1 } + }, + "ModisTerraBands367CR": { + "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraBands367CR.png", + "name": "NASAGIBS.ModisTerraBands367CR", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 9, + "minZoom": 1 + }, + "layerParameters": { "opacity": 1 } + }, + "ViirsEarthAtNight2012": { + "thumbnailPath": "layer_gallery/NASAGIBS-ViirsEarthAtNight2012.png", + "name": "NASAGIBS.ViirsEarthAtNight2012", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 8, + "minZoom": 1 + }, + "layerParameters": { "opacity": 1 } + }, + "ModisTerraLSTDay": { + "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraLSTDay.png", + "name": "NASAGIBS.ModisTerraLSTDay", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 7, + "minZoom": 1 + }, + "layerParameters": { "opacity": 1 } + }, + "ModisTerraSnowCover": { + "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraSnowCover.png", + "name": "NASAGIBS.ModisTerraSnowCover", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 8, + "minZoom": 1 + }, + "layerParameters": { "opacity": 1 } + }, + "ModisTerraAOD": { + "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraAOD.png", + "name": "NASAGIBS.ModisTerraAOD", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 6, + "minZoom": 1 + }, + "layerParameters": { "opacity": 1 } + }, + "ModisTerraChlorophyll": { + "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraChlorophyll.png", + "name": "NASAGIBS.ModisTerraChlorophyll", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 7, + "minZoom": 1 + }, + "layerParameters": { "opacity": 1 } + }, + "ModisTerraBands721CR": { + "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraBands721CR.png", + "name": "NASAGIBS.ModisTerraBands721CR", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Terra_CorrectedReflectance_Bands721/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 9, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "ModisAquaTrueColorCR": { + "thumbnailPath": "layer_gallery/NASAGIBS-ModisAquaTrueColorCR.png", + "name": "NASAGIBS.ModisAquaTrueColorCR", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Aqua_CorrectedReflectance_TrueColor/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 9, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "ModisAquaBands721CR": { + "thumbnailPath": "layer_gallery/NASAGIBS-ModisAquaBands721CR.png", + "name": "NASAGIBS.ModisAquaBands721CR", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Aqua_CorrectedReflectance_Bands721/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 9, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "ViirsTrueColorCR": { + "thumbnailPath": "layer_gallery/NASAGIBS-ViirsTrueColorCR.png", + "name": "NASAGIBS.ViirsTrueColorCR", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/VIIRS_SNPP_CorrectedReflectance_TrueColor/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 9, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "ASTER_GDEM_Greyscale_Shaded_Relief": { + "thumbnailPath": "layer_gallery/NASAGIBS-ASTER_GDEM_Greyscale_Shaded_Relief.png", + "name": "NASAGIBS.ASTER_GDEM_Greyscale_Shaded_Relief", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/ASTER_GDEM_Greyscale_Shaded_Relief/default/GoogleMapsCompatible_Level12/{z}/{y}/{x}.jpg", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 12, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + } + }, + "USGS": { + "USTopo": { + "thumbnailPath": "layer_gallery/USGS-USTopo.png", + "name": "USGS.USTopo", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles courtesy of the U.S. Geological Survey", + "maxZoom": 20 + }, + "layerParameters": { "opacity": 1 } + }, + "USImagery": { + "thumbnailPath": "layer_gallery/USGS-USImagery.png", + "name": "USGS.USImagery", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles courtesy of the U.S. Geological Survey", + "maxZoom": 20 + }, + "layerParameters": { "opacity": 1 } + }, + "USImageryTopo": { + "thumbnailPath": "layer_gallery/USGS-USImageryTopo.png", + "name": "USGS.USImageryTopo", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryTopo/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles courtesy of the U.S. Geological Survey", + "maxZoom": 20 + }, + "layerParameters": { "opacity": 1 } + } + }, + "Gaode": { + "Normal": { + "thumbnailPath": "layer_gallery/Gaode-Normal.png", + "name": "Gaode.Normal", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "http://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}", + "attribution": "© Gaode.com", + "maxZoom": 19 + }, + "layerParameters": { "opacity": 1 } + }, + "Satellite": { + "thumbnailPath": "layer_gallery/Gaode-Satellite.png", + "name": "Gaode.Satellite", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "http://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}", + "attribution": "© Gaode.com", + "maxZoom": 19 + }, + "layerParameters": { "opacity": 1 } + } + }, + "Strava": { + "All": { + "thumbnailPath": "layer_gallery/Strava-All.png", + "name": "Strava.All", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://heatmap-external-a.strava.com/tiles/all/hot/{z}/{x}/{y}.png", + "attribution": "Map tiles by Strava 2021", + "maxZoom": 15 + }, + "layerParameters": { "opacity": 1 } + }, + "Ride": { + "thumbnailPath": "layer_gallery/Strava-Ride.png", + "name": "Strava.Ride", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://heatmap-external-a.strava.com/tiles/ride/hot/{z}/{x}/{y}.png", + "attribution": "Map tiles by Strava 2021", + "maxZoom": 15 + }, + "layerParameters": { "opacity": 1 } + }, + "Run": { + "thumbnailPath": "layer_gallery/Strava-Run.png", + "name": "Strava.Run", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://heatmap-external-a.strava.com/tiles/run/bluered/{z}/{x}/{y}.png", + "attribution": "Map tiles by Strava 2021", + "maxZoom": 15 + }, + "layerParameters": { "opacity": 1 } + }, + "Water": { + "thumbnailPath": "layer_gallery/Strava-Water.png", + "name": "Strava.Water", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://heatmap-external-a.strava.com/tiles/water/blue/{z}/{x}/{y}.png", + "attribution": "Map tiles by Strava 2021", + "maxZoom": 15 + }, + "layerParameters": { "opacity": 1 } + }, + "Winter": { + "thumbnailPath": "layer_gallery/Strava-Winter.png", + "name": "Strava.Winter", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://heatmap-external-a.strava.com/tiles/winter/hot/{z}/{x}/{y}.png", + "attribution": "Map tiles by Strava 2021", + "maxZoom": 15 + }, + "layerParameters": { "opacity": 1 } + } + }, + "OPNVKarte": { + "thumbnailPath": "layer_gallery/OPNVKarte.png", + "name": "OPNVKarte", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png", + "attribution": "Map memomaps.de CC-BY-SA, map data (C) OpenStreetMap contributors", + "maxZoom": 18, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "OpenTopoMap": { + "thumbnailPath": "layer_gallery/OpenTopoMap.png", + "name": "OpenTopoMap", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", + "attribution": "Map data: (C) OpenStreetMap contributors, SRTM | Map style: (C) OpenTopoMap (CC-BY-SA)", + "maxZoom": 17, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "OpenRailwayMap": { + "thumbnailPath": "layer_gallery/OpenRailwayMap.png", + "name": "OpenRailwayMap", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png", + "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) OpenRailwayMap (CC-BY-SA)", + "maxZoom": 19, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "Esri": { + "WorldStreetMap": { + "thumbnailPath": "layer_gallery/Esri-WorldStreetMap.png", + "name": "Esri.WorldStreetMap", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles (C) Esri -- Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012", + "maxZoom": null, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "WorldTopoMap": { + "thumbnailPath": "layer_gallery/Esri-WorldTopoMap.png", + "name": "Esri.WorldTopoMap", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community", + "maxZoom": null, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "WorldImagery": { + "thumbnailPath": "layer_gallery/Esri-WorldImagery.png", + "name": "Esri.WorldImagery", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles (C) Esri -- Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community", + "maxZoom": null, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "WorldTerrain": { + "thumbnailPath": "layer_gallery/Esri-WorldTerrain.png", + "name": "Esri.WorldTerrain", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles (C) Esri -- Source: USGS, Esri, TANA, DeLorme, and NPS", + "maxZoom": 13, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "WorldShadedRelief": { + "thumbnailPath": "layer_gallery/Esri-WorldShadedRelief.png", + "name": "Esri.WorldShadedRelief", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles (C) Esri -- Source: Esri", + "maxZoom": 13, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "WorldPhysical": { + "thumbnailPath": "layer_gallery/Esri-WorldPhysical.png", + "name": "Esri.WorldPhysical", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles (C) Esri -- Source: US National Park Service", + "maxZoom": 8, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "OceanBasemap": { + "thumbnailPath": "layer_gallery/Esri-OceanBasemap.png", + "name": "Esri.OceanBasemap", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles (C) Esri -- Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri", + "maxZoom": 13, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "NatGeoWorldMap": { + "thumbnailPath": "layer_gallery/Esri-NatGeoWorldMap.png", + "name": "Esri.NatGeoWorldMap", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles (C) Esri -- National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC", + "maxZoom": 16, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "WorldGrayCanvas": { + "thumbnailPath": "layer_gallery/Esri-WorldGrayCanvas.png", + "name": "Esri.WorldGrayCanvas", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ", + "maxZoom": 16, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + } + }, + "MacroStrat": { + "CartoRaster": { + "thumbnailPath": "layer_gallery/MacroStrat-CartoRaster.png", + "name": "MacroStrat.CartoRaster", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://tiles.macrostrat.org/carto/{z}/{x}/{y}.png", + "attribution": "\u00a9 Geologic data \u00a9 Macrostrat raster layer (CC\u2011BY 4.0)", + "maxZoom": 18, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + }, + "CartoVector": { + "thumbnailPath": "layer_gallery/MacroStrat-CartoVector.png", + "name": "MacroStrat.CartoVector", + "layerType": "VectorTileLayer", + "sourceType": "VectorTileSource", + "sourceParameters": { + "url": "https://tiles.macrostrat.org/carto/{z}/{x}/{y}.mvt", + "attribution": "\u00a9 Geologic data \u00a9 Macrostrat vector layer (CC\u2011BY 4.0)", + "maxZoom": 18, + "minZoom": null + }, + "layerParameters": { "opacity": 1 } + } + } +} diff --git a/packages/base/layer_gallery_generator.py b/packages/base/layer_gallery_generator.py index d919da30d..7d0d587dd 100644 --- a/packages/base/layer_gallery_generator.py +++ b/packages/base/layer_gallery_generator.py @@ -11,6 +11,10 @@ import string from requests.exceptions import RequestException +with open("layer_gallery/layer_config.json", "r", encoding="utf-8") as f: + provider_config = json.load(f) + + THUMBNAILS_LOCATION = "layer_gallery" def placeholder_tile(size): @@ -122,323 +126,13 @@ def create_thumbnail( # San Francisco san_francisco = {"lat": 37.7749, "lng": -122.4194, "zoom": 5} -middle_europe = {"lat": 48.63290858589535, "lng": -350.068359375, "zoom": 4} +##middle_europe = {"lat": 48.63290858589535, "lng": -350.068359375, "zoom": 4} # Default -france = {"lat": 47.040182144806664, "lng": 1.2963867187500002, "zoom": 5} - -provider_config = { - "OpenStreetMap": { - "Mapnik": { - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail":{ - "Special Rules": {}, - "Default": france - }, - }, - "BZH": { - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail":{ - "Special Rules": {"lat": 47.76702233051035, "lng": -3.4675598144531254, "zoom": 8}, - "Default": france - }, - }, - "CAT": { - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail":{ - "Special Rules": {}, - "Default": france - }, - }, - "CH": { - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail":{ - "Special Rules": {"lat": 46.8182, "lng": 8.2275, "zoom": 8}, - "Default": france - }, - }, - "DE": { - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail":{ - "Special Rules": {"lat": 51.1657, "lng": 10.4515, "zoom": 8}, - "Default": france - } - }, - "France": { - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail":{ - "Special Rules": {}, - "Default": france - } - }, - "HOT": { - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail":{ - "Special Rules": {}, - "Default": france - }, - } - }, - "NASAGIBS": { - "ModisTerraTrueColorCR" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": france, - } - }, - "ModisTerraBands367CR" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": france, - } - }, - "ViirsEarthAtNight2012" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": france, - } - }, - "ModisTerraLSTDay" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": france, - } - }, - "ModisTerraSnowCover" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": france, - } - }, - "ModisTerraAOD" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": france, - } - }, - "ModisTerraChlorophyll" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": france, - } - }, - - "ModisTerraBands721CR" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": france, - } - }, - "ModisAquaTrueColorCR" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": france, - } - }, - "ModisAquaBands721CR" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": france, - } - }, - "ViirsTrueColorCR" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": france, - } - }, - "ASTER_GDEM_Greyscale_Shaded_Relief" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": france, - } - }, - - }, - "USGS": { - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": san_francisco, - }, - }, - #"WaymarkedTrails": { - #"layerType": "RasterLayer", - #"sourceType": "RasterSource", - # "thumbnail": { - #"Special Rules": {}, - #"Default": france, - #}, - # }, - "Gaode": { - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": san_francisco, - }, - }, - "Strava": { - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": france, - "TileSize": 512, - }, - }, - - "OPNVKarte": { - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": san_francisco, - }, - }, - "OpenTopoMap": { - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": san_francisco, - }, - }, - "OpenRailwayMap": { - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": san_francisco, - "TileSize": 512, - }, - }, - "Esri": { - "WorldStreetMap" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": san_francisco, - } - }, - "WorldTopoMap" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": san_francisco, - } - }, - "WorldImagery" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": san_francisco, - } - }, - "WorldTerrain" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": san_francisco, - } - }, - "WorldShadedRelief" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": san_francisco, - } - }, - "WorldPhysical" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": san_francisco, - } - }, - "OceanBasemap" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": san_francisco, - } - }, - "NatGeoWorldMap" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": san_francisco, - } - }, - "WorldGrayCanvas" :{ - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": san_francisco, - } - }, - }, - "MacroStrat": { - "CartoRaster": { - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "thumbnail": { - "Special Rules": {}, - "Default": france, - }, - }, - "CartoVector": { - "layerType": "VectorTileLayer", - "sourceType": "VectorTileSource", - "thumbnail": { - "Special Rules": {}, - "Default": france, - }, - }, - }, -} +#france = {"lat": 47.040182144806664, "lng": 1.2963867187500002, "zoom": 5} + +# def download_thumbnail(url_template, name, position, tile_size, **url_parameters): From fe991c2347d17531208deb16fc5f0bb27d9c31af Mon Sep 17 00:00:00 2001 From: Florence Haudin Date: Thu, 29 Jan 2026 11:13:41 +0100 Subject: [PATCH 3/8] Set default minZoom to 0 when no value is provided by xyzservices. --- .../base/layer_gallery/layer_gallery.json | 82 +++++++++++-------- packages/base/layer_gallery_generator.py | 18 ++-- 2 files changed, 54 insertions(+), 46 deletions(-) diff --git a/packages/base/layer_gallery/layer_gallery.json b/packages/base/layer_gallery/layer_gallery.json index daa111acf..e2fa4be98 100644 --- a/packages/base/layer_gallery/layer_gallery.json +++ b/packages/base/layer_gallery/layer_gallery.json @@ -9,7 +9,7 @@ "url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors", "maxZoom": 19, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -22,7 +22,7 @@ "url": "https://tile.openstreetmap.bzh/br/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team", "maxZoom": 19, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -35,7 +35,7 @@ "url": "https://tile.openstreetmap.bzh/ca/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team", "maxZoom": 19, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -48,7 +48,7 @@ "url": "https://tile.osm.ch/switzerland/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors", "maxZoom": 18, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -61,7 +61,7 @@ "url": "https://tile.openstreetmap.de/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors", "maxZoom": 18, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -74,7 +74,7 @@ "url": "https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap France | (C) OpenStreetMap contributors", "maxZoom": 20, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -87,7 +87,7 @@ "url": "https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors, Tiles style by Humanitarian OpenStreetMap Team hosted by OpenStreetMap France", "maxZoom": 19, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } } @@ -193,7 +193,7 @@ "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Terra_CorrectedReflectance_Bands721/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -206,7 +206,7 @@ "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Aqua_CorrectedReflectance_TrueColor/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -219,7 +219,7 @@ "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Aqua_CorrectedReflectance_Bands721/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -232,7 +232,7 @@ "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/VIIRS_SNPP_CorrectedReflectance_TrueColor/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -245,7 +245,7 @@ "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/ASTER_GDEM_Greyscale_Shaded_Relief/default/GoogleMapsCompatible_Level12/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 12, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } } @@ -259,7 +259,8 @@ "sourceParameters": { "url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles courtesy of the U.S. Geological Survey", - "maxZoom": 20 + "maxZoom": 20, + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -271,7 +272,8 @@ "sourceParameters": { "url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles courtesy of the U.S. Geological Survey", - "maxZoom": 20 + "maxZoom": 20, + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -283,7 +285,8 @@ "sourceParameters": { "url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryTopo/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles courtesy of the U.S. Geological Survey", - "maxZoom": 20 + "maxZoom": 20, + "minZoom": 0 }, "layerParameters": { "opacity": 1 } } @@ -297,7 +300,8 @@ "sourceParameters": { "url": "http://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}", "attribution": "© Gaode.com", - "maxZoom": 19 + "maxZoom": 19, + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -309,7 +313,8 @@ "sourceParameters": { "url": "http://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}", "attribution": "© Gaode.com", - "maxZoom": 19 + "maxZoom": 19, + "minZoom": 0 }, "layerParameters": { "opacity": 1 } } @@ -323,7 +328,8 @@ "sourceParameters": { "url": "https://heatmap-external-a.strava.com/tiles/all/hot/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", - "maxZoom": 15 + "maxZoom": 15, + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -335,7 +341,8 @@ "sourceParameters": { "url": "https://heatmap-external-a.strava.com/tiles/ride/hot/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", - "maxZoom": 15 + "maxZoom": 15, + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -347,7 +354,8 @@ "sourceParameters": { "url": "https://heatmap-external-a.strava.com/tiles/run/bluered/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", - "maxZoom": 15 + "maxZoom": 15, + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -359,7 +367,8 @@ "sourceParameters": { "url": "https://heatmap-external-a.strava.com/tiles/water/blue/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", - "maxZoom": 15 + "maxZoom": 15, + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -371,7 +380,8 @@ "sourceParameters": { "url": "https://heatmap-external-a.strava.com/tiles/winter/hot/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", - "maxZoom": 15 + "maxZoom": 15, + "minZoom": 0 }, "layerParameters": { "opacity": 1 } } @@ -385,7 +395,7 @@ "url": "https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png", "attribution": "Map memomaps.de CC-BY-SA, map data (C) OpenStreetMap contributors", "maxZoom": 18, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -398,7 +408,7 @@ "url": "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", "attribution": "Map data: (C) OpenStreetMap contributors, SRTM | Map style: (C) OpenTopoMap (CC-BY-SA)", "maxZoom": 17, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -411,7 +421,7 @@ "url": "https://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png", "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) OpenRailwayMap (CC-BY-SA)", "maxZoom": 19, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -425,7 +435,7 @@ "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012", "maxZoom": null, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -438,7 +448,7 @@ "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community", "maxZoom": null, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -451,7 +461,7 @@ "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community", "maxZoom": null, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -464,7 +474,7 @@ "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: USGS, Esri, TANA, DeLorme, and NPS", "maxZoom": 13, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -477,7 +487,7 @@ "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: Esri", "maxZoom": 13, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -490,7 +500,7 @@ "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: US National Park Service", "maxZoom": 8, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -503,7 +513,7 @@ "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri", "maxZoom": 13, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -516,7 +526,7 @@ "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC", "maxZoom": 16, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -529,7 +539,7 @@ "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ", "maxZoom": 16, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } } @@ -544,7 +554,7 @@ "url": "https://tiles.macrostrat.org/carto/{z}/{x}/{y}.png", "attribution": "\u00a9 Geologic data \u00a9 Macrostrat raster layer (CC\u2011BY 4.0)", "maxZoom": 18, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } }, @@ -557,7 +567,7 @@ "url": "https://tiles.macrostrat.org/carto/{z}/{x}/{y}.mvt", "attribution": "\u00a9 Geologic data \u00a9 Macrostrat vector layer (CC\u2011BY 4.0)", "maxZoom": 18, - "minZoom": null + "minZoom": 0 }, "layerParameters": { "opacity": 1 } } diff --git a/packages/base/layer_gallery_generator.py b/packages/base/layer_gallery_generator.py index 7d0d587dd..8970b581d 100644 --- a/packages/base/layer_gallery_generator.py +++ b/packages/base/layer_gallery_generator.py @@ -124,7 +124,7 @@ def create_thumbnail( yesterday = (date.today() - timedelta(days=1)).strftime("%Y-%m-%d") # San Francisco -san_francisco = {"lat": 37.7749, "lng": -122.4194, "zoom": 5} +#san_francisco = {"lat": 37.7749, "lng": -122.4194, "zoom": 5} ##middle_europe = {"lat": 48.63290858589535, "lng": -350.068359375, "zoom": 4} @@ -132,9 +132,6 @@ def create_thumbnail( #france = {"lat": 47.040182144806664, "lng": 1.2963867187500002, "zoom": 5} -# - - def download_thumbnail(url_template, name, position, tile_size, **url_parameters): file_path = f"{THUMBNAILS_LOCATION}/{name}.png" if os.path.exists(file_path): @@ -204,7 +201,8 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters "url": url_template, "attribution": xyzprovider.get("attribution"), "maxZoom": xyzprovider.get("max_zoom"), - "minZoom": xyzprovider.get("min_zoom"), + "minZoom": xyzprovider.get("min_zoom") or 0 +, }, "layerParameters": {"opacity": 1} @@ -217,7 +215,6 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters url_template = tile_provider["url"] url_parameters = build_url_parameters(tile_provider) - thumbnail_config = provider_value["thumbnail"] position = thumbnail_config["Special Rules"].get( map_name, thumbnail_config["Default"] @@ -229,7 +226,7 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters file_path = download_thumbnail( url_template, name, position, tile_size, **url_parameters ) - + providers_maps[map_name] = { "thumbnailPath": file_path, "name": provider_key +"."+ map_name, @@ -238,7 +235,8 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters "sourceParameters": { "url": url_template, "attribution": tile_provider.get("attribution"), - "maxZoom": tile_provider.get("max_zoom") + "maxZoom": tile_provider.get("max_zoom"), + "minZoom": tile_provider.get("min_zoom") or 0 }, "layerParameters": {"opacity": 1} } @@ -263,7 +261,7 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters file_path = download_thumbnail( url_template, name, position, tile_size, **url_parameters ) - + providers_maps[map_name] = { "thumbnailPath": file_path, "name": provider_key +"."+ map_name, @@ -273,7 +271,7 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters "url": url_template, "attribution": tile_provider.get("attribution"), "maxZoom": tile_provider.get("max_zoom"), - "minZoom": tile_provider.get("min_zoom") + "minZoom": tile_provider.get("min_zoom") or 0 }, "layerParameters": {"opacity": 1}, } From daf759d2165d9ec8ab9c06e278cdaa84c7ae49eb Mon Sep 17 00:00:00 2001 From: Florence Haudin Date: Fri, 30 Jan 2026 14:55:52 +0100 Subject: [PATCH 4/8] Take review comments into account. Add a vector layer with geoJSONSource in the layer_gallery.json file. --- packages/base/layer_gallery.json | 1 + .../layer_gallery/NaturalEarth-Countries.png | Bin 0 -> 31884 bytes .../base/layer_gallery/layer_gallery.json | 575 ------------------ ...ayer_config.json => thumbnail_config.json} | 101 ++- packages/base/layer_gallery_generator.py | 55 +- .../base/src/dialogs/layerBrowserDialog.tsx | 2 +- packages/base/src/tools.ts | 24 +- packages/schema/src/interfaces.ts | 21 +- 8 files changed, 146 insertions(+), 633 deletions(-) create mode 100644 packages/base/layer_gallery.json create mode 100644 packages/base/layer_gallery/NaturalEarth-Countries.png delete mode 100644 packages/base/layer_gallery/layer_gallery.json rename packages/base/layer_gallery/{layer_config.json => thumbnail_config.json} (83%) diff --git a/packages/base/layer_gallery.json b/packages/base/layer_gallery.json new file mode 100644 index 000000000..3e8541183 --- /dev/null +++ b/packages/base/layer_gallery.json @@ -0,0 +1 @@ +{"OpenStreetMap": {"Mapnik": {"thumbnailPath": "layer_gallery/OpenStreetMap-Mapnik.png", "name": "OpenStreetMap.Mapnik", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors", "maxZoom": 19, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "(C) OpenStreetMap contributors"}, "BZH": {"thumbnailPath": "layer_gallery/OpenStreetMap-BZH.png", "name": "OpenStreetMap.BZH", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tile.openstreetmap.bzh/br/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team", "maxZoom": 19, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team"}, "CAT": {"thumbnailPath": "layer_gallery/OpenStreetMap-CAT.png", "name": "OpenStreetMap.CAT", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tile.openstreetmap.bzh/ca/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team", "maxZoom": 19, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team"}, "CH": {"thumbnailPath": "layer_gallery/OpenStreetMap-CH.png", "name": "OpenStreetMap.CH", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tile.osm.ch/switzerland/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors", "maxZoom": 18, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "(C) OpenStreetMap contributors"}, "DE": {"thumbnailPath": "layer_gallery/OpenStreetMap-DE.png", "name": "OpenStreetMap.DE", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tile.openstreetmap.de/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors", "maxZoom": 18, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "(C) OpenStreetMap contributors"}, "France": {"thumbnailPath": "layer_gallery/OpenStreetMap-France.png", "name": "OpenStreetMap.France", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap France | (C) OpenStreetMap contributors", "maxZoom": 20, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "(C) OpenStreetMap France | (C) OpenStreetMap contributors"}, "HOT": {"thumbnailPath": "layer_gallery/OpenStreetMap-HOT.png", "name": "OpenStreetMap.HOT", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors, Tiles style by Humanitarian OpenStreetMap Team hosted by OpenStreetMap France", "maxZoom": 19, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "(C) OpenStreetMap contributors, Tiles style by Humanitarian OpenStreetMap Team hosted by OpenStreetMap France"}}, "NASAGIBS": {"ModisTerraTrueColorCR": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraTrueColorCR.png", "name": "NASAGIBS.ModisTerraTrueColorCR", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, "minZoom": 1}, "layerParameters": {"opacity": 1}, "urlParameters": {"tilematrixset": "GoogleMapsCompatible_Level", "format": "jpg", "time": "2026-01-29", "maxZoom": 9, "variant": "MODIS_Terra_CorrectedReflectance_TrueColor"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ModisTerraBands367CR": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraBands367CR.png", "name": "NASAGIBS.ModisTerraBands367CR", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, "minZoom": 1}, "layerParameters": {"opacity": 1}, "urlParameters": {"tilematrixset": "GoogleMapsCompatible_Level", "format": "jpg", "time": "2026-01-29", "maxZoom": 9, "variant": "MODIS_Terra_CorrectedReflectance_Bands367"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ViirsEarthAtNight2012": {"thumbnailPath": "layer_gallery/NASAGIBS-ViirsEarthAtNight2012.png", "name": "NASAGIBS.ViirsEarthAtNight2012", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 8, "minZoom": 1}, "layerParameters": {"opacity": 1}, "urlParameters": {"tilematrixset": "GoogleMapsCompatible_Level", "format": "jpg", "time": "2026-01-29", "maxZoom": 8, "variant": "VIIRS_CityLights_2012"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ModisTerraLSTDay": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraLSTDay.png", "name": "NASAGIBS.ModisTerraLSTDay", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 7, "minZoom": 1}, "layerParameters": {"opacity": 1}, "urlParameters": {"tilematrixset": "GoogleMapsCompatible_Level", "format": "png", "time": "2026-01-29", "maxZoom": 7, "variant": "MODIS_Terra_Land_Surface_Temp_Day"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ModisTerraSnowCover": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraSnowCover.png", "name": "NASAGIBS.ModisTerraSnowCover", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 8, "minZoom": 1}, "layerParameters": {"opacity": 1}, "urlParameters": {"tilematrixset": "GoogleMapsCompatible_Level", "format": "png", "time": "2026-01-29", "maxZoom": 8, "variant": "MODIS_Terra_NDSI_Snow_Cover"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ModisTerraAOD": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraAOD.png", "name": "NASAGIBS.ModisTerraAOD", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 6, "minZoom": 1}, "layerParameters": {"opacity": 1}, "urlParameters": {"tilematrixset": "GoogleMapsCompatible_Level", "format": "png", "time": "2026-01-29", "maxZoom": 6, "variant": "MODIS_Terra_Aerosol"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ModisTerraChlorophyll": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraChlorophyll.png", "name": "NASAGIBS.ModisTerraChlorophyll", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 7, "minZoom": 1}, "layerParameters": {"opacity": 1}, "urlParameters": {"tilematrixset": "GoogleMapsCompatible_Level", "format": "png", "time": "2026-01-29", "maxZoom": 7, "variant": "MODIS_Terra_L2_Chlorophyll_A"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ModisTerraBands721CR": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraBands721CR.png", "name": "NASAGIBS.ModisTerraBands721CR", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Terra_CorrectedReflectance_Bands721/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"time": "2026-01-29"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ModisAquaTrueColorCR": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisAquaTrueColorCR.png", "name": "NASAGIBS.ModisAquaTrueColorCR", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Aqua_CorrectedReflectance_TrueColor/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"time": "2026-01-29"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ModisAquaBands721CR": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisAquaBands721CR.png", "name": "NASAGIBS.ModisAquaBands721CR", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Aqua_CorrectedReflectance_Bands721/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"time": "2026-01-29"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ViirsTrueColorCR": {"thumbnailPath": "layer_gallery/NASAGIBS-ViirsTrueColorCR.png", "name": "NASAGIBS.ViirsTrueColorCR", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/VIIRS_SNPP_CorrectedReflectance_TrueColor/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"time": "2026-01-29"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ASTER_GDEM_Greyscale_Shaded_Relief": {"thumbnailPath": "layer_gallery/NASAGIBS-ASTER_GDEM_Greyscale_Shaded_Relief.png", "name": "NASAGIBS.ASTER_GDEM_Greyscale_Shaded_Relief", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/ASTER_GDEM_Greyscale_Shaded_Relief/default/GoogleMapsCompatible_Level12/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 12, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}}, "USGS": {"USTopo": {"thumbnailPath": "layer_gallery/USGS-USTopo.png", "name": "USGS.USTopo", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles courtesy of the U.S. Geological Survey", "maxZoom": 20, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "Tiles courtesy of the U.S. Geological Survey"}, "USImagery": {"thumbnailPath": "layer_gallery/USGS-USImagery.png", "name": "USGS.USImagery", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles courtesy of the U.S. Geological Survey", "maxZoom": 20, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "Tiles courtesy of the U.S. Geological Survey"}, "USImageryTopo": {"thumbnailPath": "layer_gallery/USGS-USImageryTopo.png", "name": "USGS.USImageryTopo", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryTopo/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles courtesy of the U.S. Geological Survey", "maxZoom": 20, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "Tiles courtesy of the U.S. Geological Survey"}}, "Gaode": {"Normal": {"thumbnailPath": "layer_gallery/Gaode-Normal.png", "name": "Gaode.Normal", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "http://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}", "attribution": "© Gaode.com", "maxZoom": 19, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "© Gaode.com"}, "Satellite": {"thumbnailPath": "layer_gallery/Gaode-Satellite.png", "name": "Gaode.Satellite", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "http://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}", "attribution": "© Gaode.com", "maxZoom": 19, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "© Gaode.com"}}, "Strava": {"All": {"thumbnailPath": "layer_gallery/Strava-All.png", "name": "Strava.All", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://heatmap-external-a.strava.com/tiles/all/hot/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "Map tiles by Strava 2021"}, "Ride": {"thumbnailPath": "layer_gallery/Strava-Ride.png", "name": "Strava.Ride", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://heatmap-external-a.strava.com/tiles/ride/hot/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "Map tiles by Strava 2021"}, "Run": {"thumbnailPath": "layer_gallery/Strava-Run.png", "name": "Strava.Run", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://heatmap-external-a.strava.com/tiles/run/bluered/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "Map tiles by Strava 2021"}, "Water": {"thumbnailPath": "layer_gallery/Strava-Water.png", "name": "Strava.Water", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://heatmap-external-a.strava.com/tiles/water/blue/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "Map tiles by Strava 2021"}, "Winter": {"thumbnailPath": "layer_gallery/Strava-Winter.png", "name": "Strava.Winter", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://heatmap-external-a.strava.com/tiles/winter/hot/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "Map tiles by Strava 2021"}}, "OPNVKarte": {"thumbnailPath": "layer_gallery/OPNVKarte.png", "name": "OPNVKarte", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png", "attribution": "Map memomaps.de CC-BY-SA, map data (C) OpenStreetMap contributors", "maxZoom": 18, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": "https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png"}, "OpenTopoMap": {"thumbnailPath": "layer_gallery/OpenTopoMap.png", "name": "OpenTopoMap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", "attribution": "Map data: (C) OpenStreetMap contributors, SRTM | Map style: (C) OpenTopoMap (CC-BY-SA)", "maxZoom": 17, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png"}, "OpenRailwayMap": {"thumbnailPath": "layer_gallery/OpenRailwayMap.png", "name": "OpenRailwayMap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png", "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) OpenRailwayMap (CC-BY-SA)", "maxZoom": 19, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": "https://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png"}, "Esri": {"WorldStreetMap": {"thumbnailPath": "layer_gallery/Esri-WorldStreetMap.png", "name": "Esri.WorldStreetMap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012", "maxZoom": null, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"variant": "World_Street_Map"}, "description": "Tiles (C) Esri -- Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012"}, "WorldTopoMap": {"thumbnailPath": "layer_gallery/Esri-WorldTopoMap.png", "name": "Esri.WorldTopoMap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community", "maxZoom": null, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"variant": "World_Topo_Map"}, "description": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community"}, "WorldImagery": {"thumbnailPath": "layer_gallery/Esri-WorldImagery.png", "name": "Esri.WorldImagery", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community", "maxZoom": null, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"variant": "World_Imagery"}, "description": "Tiles (C) Esri -- Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community"}, "WorldTerrain": {"thumbnailPath": "layer_gallery/Esri-WorldTerrain.png", "name": "Esri.WorldTerrain", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: USGS, Esri, TANA, DeLorme, and NPS", "maxZoom": 13, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"variant": "World_Terrain_Base"}, "description": "Tiles (C) Esri -- Source: USGS, Esri, TANA, DeLorme, and NPS"}, "WorldShadedRelief": {"thumbnailPath": "layer_gallery/Esri-WorldShadedRelief.png", "name": "Esri.WorldShadedRelief", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: Esri", "maxZoom": 13, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"variant": "World_Shaded_Relief"}, "description": "Tiles (C) Esri -- Source: Esri"}, "WorldPhysical": {"thumbnailPath": "layer_gallery/Esri-WorldPhysical.png", "name": "Esri.WorldPhysical", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: US National Park Service", "maxZoom": 8, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"variant": "World_Physical_Map"}, "description": "Tiles (C) Esri -- Source: US National Park Service"}, "OceanBasemap": {"thumbnailPath": "layer_gallery/Esri-OceanBasemap.png", "name": "Esri.OceanBasemap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri", "maxZoom": 13, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"variant": "Ocean/World_Ocean_Base"}, "description": "Tiles (C) Esri -- Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri"}, "NatGeoWorldMap": {"thumbnailPath": "layer_gallery/Esri-NatGeoWorldMap.png", "name": "Esri.NatGeoWorldMap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC", "maxZoom": 16, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"variant": "NatGeo_World_Map"}, "description": "Tiles (C) Esri -- National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC"}, "WorldGrayCanvas": {"thumbnailPath": "layer_gallery/Esri-WorldGrayCanvas.png", "name": "Esri.WorldGrayCanvas", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ", "maxZoom": 16, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"variant": "Canvas/World_Light_Gray_Base"}, "description": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ"}}, "MacroStrat": {"CartoRaster": {"thumbnailPath": "layer_gallery/MacroStrat-CartoRaster.png", "name": "MacroStrat.CartoRaster", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tiles.macrostrat.org/carto/{z}/{x}/{y}.png", "attribution": "\u00a9 Geologic data \u00a9 Macrostrat raster layer (CC\u2011BY 4.0)", "maxZoom": 18, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "\u00a9 Geologic data \u00a9 Macrostrat raster layer (CC\u2011BY 4.0)"}, "CartoVector": {"thumbnailPath": "layer_gallery/MacroStrat-CartoVector.png", "name": "MacroStrat.CartoVector", "layerType": "VectorTileLayer", "sourceType": "VectorTileSource", "sourceParameters": {"url": "https://tiles.macrostrat.org/carto/{z}/{x}/{y}.mvt", "attribution": "\u00a9 Geologic data \u00a9 Macrostrat vector layer (CC\u2011BY 4.0)", "maxZoom": 18, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "\u00a9 Geologic data \u00a9 Macrostrat vector layer (CC\u2011BY 4.0)"}}, "NaturalEarth": {"Countries": {"name": "NaturalEarth.Countries", "thumbnailPath": "/layer_gallery/NaturalEarth-Countries.png", "layerType": "VectorLayer", "layerParameters": {"opacity": 1, "symbologyState": {}}, "sourceType": "GeoJSONSource", "sourceParameters": {"path": "https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_110m_admin_0_countries.geojson"}, "provider": "NaturalEarth", "urlParameters": {}, "description": "Vector country boundaries from the Natural Earth dataset (1:110m scale). Public-domain geographic data intended for small-scale cartography, theming, and global map display."}}} \ No newline at end of file diff --git a/packages/base/layer_gallery/NaturalEarth-Countries.png b/packages/base/layer_gallery/NaturalEarth-Countries.png new file mode 100644 index 0000000000000000000000000000000000000000..4150f368e2f8b5b0d0901845bc3c4ea5af0977a0 GIT binary patch literal 31884 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 literal 0 HcmV?d00001 diff --git a/packages/base/layer_gallery/layer_gallery.json b/packages/base/layer_gallery/layer_gallery.json deleted file mode 100644 index e2fa4be98..000000000 --- a/packages/base/layer_gallery/layer_gallery.json +++ /dev/null @@ -1,575 +0,0 @@ -{ - "OpenStreetMap": { - "Mapnik": { - "thumbnailPath": "layer_gallery/OpenStreetMap-Mapnik.png", - "name": "OpenStreetMap.Mapnik", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png", - "attribution": "(C) OpenStreetMap contributors", - "maxZoom": 19, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "BZH": { - "thumbnailPath": "layer_gallery/OpenStreetMap-BZH.png", - "name": "OpenStreetMap.BZH", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://tile.openstreetmap.bzh/br/{z}/{x}/{y}.png", - "attribution": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team", - "maxZoom": 19, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "CAT": { - "thumbnailPath": "layer_gallery/OpenStreetMap-CAT.png", - "name": "OpenStreetMap.CAT", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://tile.openstreetmap.bzh/ca/{z}/{x}/{y}.png", - "attribution": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team", - "maxZoom": 19, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "CH": { - "thumbnailPath": "layer_gallery/OpenStreetMap-CH.png", - "name": "OpenStreetMap.CH", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://tile.osm.ch/switzerland/{z}/{x}/{y}.png", - "attribution": "(C) OpenStreetMap contributors", - "maxZoom": 18, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "DE": { - "thumbnailPath": "layer_gallery/OpenStreetMap-DE.png", - "name": "OpenStreetMap.DE", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://tile.openstreetmap.de/{z}/{x}/{y}.png", - "attribution": "(C) OpenStreetMap contributors", - "maxZoom": 18, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "France": { - "thumbnailPath": "layer_gallery/OpenStreetMap-France.png", - "name": "OpenStreetMap.France", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png", - "attribution": "(C) OpenStreetMap France | (C) OpenStreetMap contributors", - "maxZoom": 20, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "HOT": { - "thumbnailPath": "layer_gallery/OpenStreetMap-HOT.png", - "name": "OpenStreetMap.HOT", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png", - "attribution": "(C) OpenStreetMap contributors, Tiles style by Humanitarian OpenStreetMap Team hosted by OpenStreetMap France", - "maxZoom": 19, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - } - }, - "NASAGIBS": { - "ModisTerraTrueColorCR": { - "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraTrueColorCR.png", - "name": "NASAGIBS.ModisTerraTrueColorCR", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", - "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", - "maxZoom": 9, - "minZoom": 1 - }, - "layerParameters": { "opacity": 1 } - }, - "ModisTerraBands367CR": { - "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraBands367CR.png", - "name": "NASAGIBS.ModisTerraBands367CR", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", - "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", - "maxZoom": 9, - "minZoom": 1 - }, - "layerParameters": { "opacity": 1 } - }, - "ViirsEarthAtNight2012": { - "thumbnailPath": "layer_gallery/NASAGIBS-ViirsEarthAtNight2012.png", - "name": "NASAGIBS.ViirsEarthAtNight2012", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", - "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", - "maxZoom": 8, - "minZoom": 1 - }, - "layerParameters": { "opacity": 1 } - }, - "ModisTerraLSTDay": { - "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraLSTDay.png", - "name": "NASAGIBS.ModisTerraLSTDay", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", - "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", - "maxZoom": 7, - "minZoom": 1 - }, - "layerParameters": { "opacity": 1 } - }, - "ModisTerraSnowCover": { - "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraSnowCover.png", - "name": "NASAGIBS.ModisTerraSnowCover", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", - "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", - "maxZoom": 8, - "minZoom": 1 - }, - "layerParameters": { "opacity": 1 } - }, - "ModisTerraAOD": { - "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraAOD.png", - "name": "NASAGIBS.ModisTerraAOD", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", - "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", - "maxZoom": 6, - "minZoom": 1 - }, - "layerParameters": { "opacity": 1 } - }, - "ModisTerraChlorophyll": { - "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraChlorophyll.png", - "name": "NASAGIBS.ModisTerraChlorophyll", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", - "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", - "maxZoom": 7, - "minZoom": 1 - }, - "layerParameters": { "opacity": 1 } - }, - "ModisTerraBands721CR": { - "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraBands721CR.png", - "name": "NASAGIBS.ModisTerraBands721CR", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Terra_CorrectedReflectance_Bands721/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", - "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", - "maxZoom": 9, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "ModisAquaTrueColorCR": { - "thumbnailPath": "layer_gallery/NASAGIBS-ModisAquaTrueColorCR.png", - "name": "NASAGIBS.ModisAquaTrueColorCR", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Aqua_CorrectedReflectance_TrueColor/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", - "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", - "maxZoom": 9, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "ModisAquaBands721CR": { - "thumbnailPath": "layer_gallery/NASAGIBS-ModisAquaBands721CR.png", - "name": "NASAGIBS.ModisAquaBands721CR", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Aqua_CorrectedReflectance_Bands721/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", - "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", - "maxZoom": 9, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "ViirsTrueColorCR": { - "thumbnailPath": "layer_gallery/NASAGIBS-ViirsTrueColorCR.png", - "name": "NASAGIBS.ViirsTrueColorCR", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/VIIRS_SNPP_CorrectedReflectance_TrueColor/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", - "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", - "maxZoom": 9, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "ASTER_GDEM_Greyscale_Shaded_Relief": { - "thumbnailPath": "layer_gallery/NASAGIBS-ASTER_GDEM_Greyscale_Shaded_Relief.png", - "name": "NASAGIBS.ASTER_GDEM_Greyscale_Shaded_Relief", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/ASTER_GDEM_Greyscale_Shaded_Relief/default/GoogleMapsCompatible_Level12/{z}/{y}/{x}.jpg", - "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", - "maxZoom": 12, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - } - }, - "USGS": { - "USTopo": { - "thumbnailPath": "layer_gallery/USGS-USTopo.png", - "name": "USGS.USTopo", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}", - "attribution": "Tiles courtesy of the U.S. Geological Survey", - "maxZoom": 20, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "USImagery": { - "thumbnailPath": "layer_gallery/USGS-USImagery.png", - "name": "USGS.USImagery", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}", - "attribution": "Tiles courtesy of the U.S. Geological Survey", - "maxZoom": 20, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "USImageryTopo": { - "thumbnailPath": "layer_gallery/USGS-USImageryTopo.png", - "name": "USGS.USImageryTopo", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryTopo/MapServer/tile/{z}/{y}/{x}", - "attribution": "Tiles courtesy of the U.S. Geological Survey", - "maxZoom": 20, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - } - }, - "Gaode": { - "Normal": { - "thumbnailPath": "layer_gallery/Gaode-Normal.png", - "name": "Gaode.Normal", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "http://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}", - "attribution": "© Gaode.com", - "maxZoom": 19, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "Satellite": { - "thumbnailPath": "layer_gallery/Gaode-Satellite.png", - "name": "Gaode.Satellite", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "http://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}", - "attribution": "© Gaode.com", - "maxZoom": 19, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - } - }, - "Strava": { - "All": { - "thumbnailPath": "layer_gallery/Strava-All.png", - "name": "Strava.All", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://heatmap-external-a.strava.com/tiles/all/hot/{z}/{x}/{y}.png", - "attribution": "Map tiles by Strava 2021", - "maxZoom": 15, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "Ride": { - "thumbnailPath": "layer_gallery/Strava-Ride.png", - "name": "Strava.Ride", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://heatmap-external-a.strava.com/tiles/ride/hot/{z}/{x}/{y}.png", - "attribution": "Map tiles by Strava 2021", - "maxZoom": 15, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "Run": { - "thumbnailPath": "layer_gallery/Strava-Run.png", - "name": "Strava.Run", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://heatmap-external-a.strava.com/tiles/run/bluered/{z}/{x}/{y}.png", - "attribution": "Map tiles by Strava 2021", - "maxZoom": 15, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "Water": { - "thumbnailPath": "layer_gallery/Strava-Water.png", - "name": "Strava.Water", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://heatmap-external-a.strava.com/tiles/water/blue/{z}/{x}/{y}.png", - "attribution": "Map tiles by Strava 2021", - "maxZoom": 15, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "Winter": { - "thumbnailPath": "layer_gallery/Strava-Winter.png", - "name": "Strava.Winter", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://heatmap-external-a.strava.com/tiles/winter/hot/{z}/{x}/{y}.png", - "attribution": "Map tiles by Strava 2021", - "maxZoom": 15, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - } - }, - "OPNVKarte": { - "thumbnailPath": "layer_gallery/OPNVKarte.png", - "name": "OPNVKarte", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png", - "attribution": "Map memomaps.de CC-BY-SA, map data (C) OpenStreetMap contributors", - "maxZoom": 18, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "OpenTopoMap": { - "thumbnailPath": "layer_gallery/OpenTopoMap.png", - "name": "OpenTopoMap", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", - "attribution": "Map data: (C) OpenStreetMap contributors, SRTM | Map style: (C) OpenTopoMap (CC-BY-SA)", - "maxZoom": 17, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "OpenRailwayMap": { - "thumbnailPath": "layer_gallery/OpenRailwayMap.png", - "name": "OpenRailwayMap", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png", - "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) OpenRailwayMap (CC-BY-SA)", - "maxZoom": 19, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "Esri": { - "WorldStreetMap": { - "thumbnailPath": "layer_gallery/Esri-WorldStreetMap.png", - "name": "Esri.WorldStreetMap", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", - "attribution": "Tiles (C) Esri -- Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012", - "maxZoom": null, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "WorldTopoMap": { - "thumbnailPath": "layer_gallery/Esri-WorldTopoMap.png", - "name": "Esri.WorldTopoMap", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", - "attribution": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community", - "maxZoom": null, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "WorldImagery": { - "thumbnailPath": "layer_gallery/Esri-WorldImagery.png", - "name": "Esri.WorldImagery", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", - "attribution": "Tiles (C) Esri -- Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community", - "maxZoom": null, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "WorldTerrain": { - "thumbnailPath": "layer_gallery/Esri-WorldTerrain.png", - "name": "Esri.WorldTerrain", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", - "attribution": "Tiles (C) Esri -- Source: USGS, Esri, TANA, DeLorme, and NPS", - "maxZoom": 13, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "WorldShadedRelief": { - "thumbnailPath": "layer_gallery/Esri-WorldShadedRelief.png", - "name": "Esri.WorldShadedRelief", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", - "attribution": "Tiles (C) Esri -- Source: Esri", - "maxZoom": 13, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "WorldPhysical": { - "thumbnailPath": "layer_gallery/Esri-WorldPhysical.png", - "name": "Esri.WorldPhysical", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", - "attribution": "Tiles (C) Esri -- Source: US National Park Service", - "maxZoom": 8, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "OceanBasemap": { - "thumbnailPath": "layer_gallery/Esri-OceanBasemap.png", - "name": "Esri.OceanBasemap", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", - "attribution": "Tiles (C) Esri -- Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri", - "maxZoom": 13, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "NatGeoWorldMap": { - "thumbnailPath": "layer_gallery/Esri-NatGeoWorldMap.png", - "name": "Esri.NatGeoWorldMap", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", - "attribution": "Tiles (C) Esri -- National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC", - "maxZoom": 16, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "WorldGrayCanvas": { - "thumbnailPath": "layer_gallery/Esri-WorldGrayCanvas.png", - "name": "Esri.WorldGrayCanvas", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", - "attribution": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ", - "maxZoom": 16, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - } - }, - "MacroStrat": { - "CartoRaster": { - "thumbnailPath": "layer_gallery/MacroStrat-CartoRaster.png", - "name": "MacroStrat.CartoRaster", - "layerType": "RasterLayer", - "sourceType": "RasterSource", - "sourceParameters": { - "url": "https://tiles.macrostrat.org/carto/{z}/{x}/{y}.png", - "attribution": "\u00a9 Geologic data \u00a9 Macrostrat raster layer (CC\u2011BY 4.0)", - "maxZoom": 18, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - }, - "CartoVector": { - "thumbnailPath": "layer_gallery/MacroStrat-CartoVector.png", - "name": "MacroStrat.CartoVector", - "layerType": "VectorTileLayer", - "sourceType": "VectorTileSource", - "sourceParameters": { - "url": "https://tiles.macrostrat.org/carto/{z}/{x}/{y}.mvt", - "attribution": "\u00a9 Geologic data \u00a9 Macrostrat vector layer (CC\u2011BY 4.0)", - "maxZoom": 18, - "minZoom": 0 - }, - "layerParameters": { "opacity": 1 } - } - } -} diff --git a/packages/base/layer_gallery/layer_config.json b/packages/base/layer_gallery/thumbnail_config.json similarity index 83% rename from packages/base/layer_gallery/layer_config.json rename to packages/base/layer_gallery/thumbnail_config.json index 7a437fb1b..996a8f220 100644 --- a/packages/base/layer_gallery/layer_config.json +++ b/packages/base/layer_gallery/thumbnail_config.json @@ -44,7 +44,11 @@ "layerType": "RasterLayer", "sourceType": "RasterSource", "thumbnail": { - "Special Rules": { "lat": 46.8182, "lng": 8.2275, "zoom": 8 }, + "Special Rules": { + "lat": 46.8182, + "lng": 8.2275, + "zoom": 8 + }, "Default": { "lat": 47.040182144806664, "lng": 1.2963867187500002, @@ -56,7 +60,11 @@ "layerType": "RasterLayer", "sourceType": "RasterSource", "thumbnail": { - "Special Rules": { "lat": 51.1657, "lng": 10.4515, "zoom": 8 }, + "Special Rules": { + "lat": 51.1657, + "lng": 10.4515, + "zoom": 8 + }, "Default": { "lat": 47.040182144806664, "lng": 1.2963867187500002, @@ -174,7 +182,6 @@ } } }, - "ModisTerraBands721CR": { "layerType": "RasterLayer", "sourceType": "RasterSource", @@ -241,16 +248,23 @@ "sourceType": "RasterSource", "thumbnail": { "Special Rules": {}, - "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + "Default": { + "lat": 37.7749, + "lng": -122.4194, + "zoom": 5 + } } }, - "Gaode": { "layerType": "RasterLayer", "sourceType": "RasterSource", "thumbnail": { "Special Rules": {}, - "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + "Default": { + "lat": 37.7749, + "lng": -122.4194, + "zoom": 5 + } } }, "Strava": { @@ -266,13 +280,16 @@ "TileSize": 512 } }, - "OPNVKarte": { "layerType": "RasterLayer", "sourceType": "RasterSource", "thumbnail": { "Special Rules": {}, - "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + "Default": { + "lat": 37.7749, + "lng": -122.4194, + "zoom": 5 + } } }, "OpenTopoMap": { @@ -280,7 +297,11 @@ "sourceType": "RasterSource", "thumbnail": { "Special Rules": {}, - "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + "Default": { + "lat": 37.7749, + "lng": -122.4194, + "zoom": 5 + } } }, "OpenRailwayMap": { @@ -288,7 +309,11 @@ "sourceType": "RasterSource", "thumbnail": { "Special Rules": {}, - "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 }, + "Default": { + "lat": 37.7749, + "lng": -122.4194, + "zoom": 5 + }, "TileSize": 512 } }, @@ -298,7 +323,11 @@ "sourceType": "RasterSource", "thumbnail": { "Special Rules": {}, - "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + "Default": { + "lat": 37.7749, + "lng": -122.4194, + "zoom": 5 + } } }, "WorldTopoMap": { @@ -306,7 +335,11 @@ "sourceType": "RasterSource", "thumbnail": { "Special Rules": {}, - "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + "Default": { + "lat": 37.7749, + "lng": -122.4194, + "zoom": 5 + } } }, "WorldImagery": { @@ -314,7 +347,11 @@ "sourceType": "RasterSource", "thumbnail": { "Special Rules": {}, - "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + "Default": { + "lat": 37.7749, + "lng": -122.4194, + "zoom": 5 + } } }, "WorldTerrain": { @@ -322,7 +359,11 @@ "sourceType": "RasterSource", "thumbnail": { "Special Rules": {}, - "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + "Default": { + "lat": 37.7749, + "lng": -122.4194, + "zoom": 5 + } } }, "WorldShadedRelief": { @@ -330,7 +371,11 @@ "sourceType": "RasterSource", "thumbnail": { "Special Rules": {}, - "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + "Default": { + "lat": 37.7749, + "lng": -122.4194, + "zoom": 5 + } } }, "WorldPhysical": { @@ -338,7 +383,11 @@ "sourceType": "RasterSource", "thumbnail": { "Special Rules": {}, - "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + "Default": { + "lat": 37.7749, + "lng": -122.4194, + "zoom": 5 + } } }, "OceanBasemap": { @@ -346,7 +395,11 @@ "sourceType": "RasterSource", "thumbnail": { "Special Rules": {}, - "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + "Default": { + "lat": 37.7749, + "lng": -122.4194, + "zoom": 5 + } } }, "NatGeoWorldMap": { @@ -354,7 +407,11 @@ "sourceType": "RasterSource", "thumbnail": { "Special Rules": {}, - "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + "Default": { + "lat": 37.7749, + "lng": -122.4194, + "zoom": 5 + } } }, "WorldGrayCanvas": { @@ -362,7 +419,11 @@ "sourceType": "RasterSource", "thumbnail": { "Special Rules": {}, - "Default": { "lat": 37.7749, "lng": -122.4194, "zoom": 5 } + "Default": { + "lat": 37.7749, + "lng": -122.4194, + "zoom": 5 + } } } }, @@ -392,4 +453,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/base/layer_gallery_generator.py b/packages/base/layer_gallery_generator.py index 8970b581d..7f7bff7af 100644 --- a/packages/base/layer_gallery_generator.py +++ b/packages/base/layer_gallery_generator.py @@ -11,13 +11,36 @@ import string from requests.exceptions import RequestException -with open("layer_gallery/layer_config.json", "r", encoding="utf-8") as f: +with open("layer_gallery/thumbnail_config.json", "r", encoding="utf-8") as f: provider_config = json.load(f) - THUMBNAILS_LOCATION = "layer_gallery" +def snake_to_camel(s): + """ + Convect snake case strings into camel case ones + """ + parts = s.split("_") + return parts[0] + "".join(word.capitalize() for word in parts[1:]) + +def dict_keys_to_camel(obj): + """ + Convect keys of a dict from snake case to camel case + """ + if isinstance(obj, dict): + return { + snake_to_camel(k): dict_keys_to_camel(v) + for k, v in obj.items() + } + elif isinstance(obj, list): + return [dict_keys_to_camel(item) for item in obj] + else: + return obj + def placeholder_tile(size): + """ + Provide a white empty placeholder image + """ return Image.new("RGB", size, (220, 220, 220)) def extract_placeholders(url_template): @@ -78,8 +101,6 @@ def latlng_to_tile(lat, lng, zoom): tile = mercantile.tile(lng, lat, zoom, True) return tile.x, tile.y - - def create_thumbnail( url_template, lat, @@ -148,7 +169,6 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters thumbnail.save(file_path) return file_path - # Create thumbnail dir if needed if not os.path.exists(THUMBNAILS_LOCATION): os.makedirs(THUMBNAILS_LOCATION) @@ -170,12 +190,11 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters url="https://tiles.macrostrat.org/carto/{z}/{x}/{y}.mvt", attribution="© Geologic data © Macrostrat vector layer (CC‑BY 4.0)", max_zoom=18, - ), + ) } # Fetch thumbnails and populate the dictionary for provider_key, provider_value in provider_config.items(): - xyzprovider = custom_providers[provider_key] config_is_flat = "layerType" in provider_value xyz_is_flat = "url" in xyzprovider @@ -191,7 +210,6 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters tile_size = thumbnail_config.get("TileSize", 256) file_path = download_thumbnail(url_template, provider_key, position, tile_size, **url_parameters) - provider_gallery[provider_key] = { "thumbnailPath": file_path, "name": provider_key, @@ -204,8 +222,8 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters "minZoom": xyzprovider.get("min_zoom") or 0 , }, - "layerParameters": {"opacity": 1} - + "layerParameters": {"opacity": 1}, + "urlParameters": dict_keys_to_camel(url_template) } elif config_is_flat and not xyz_is_flat: @@ -226,7 +244,7 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters file_path = download_thumbnail( url_template, name, position, tile_size, **url_parameters ) - + providers_maps[map_name] = { "thumbnailPath": file_path, "name": provider_key +"."+ map_name, @@ -238,7 +256,9 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters "maxZoom": tile_provider.get("max_zoom"), "minZoom": tile_provider.get("min_zoom") or 0 }, - "layerParameters": {"opacity": 1} + "layerParameters": {"opacity": 1}, + "urlParameters": dict_keys_to_camel(url_parameters), + "description": tile_provider.get("attribution") } provider_gallery[provider_key] = providers_maps @@ -261,7 +281,7 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters file_path = download_thumbnail( url_template, name, position, tile_size, **url_parameters ) - + providers_maps[map_name] = { "thumbnailPath": file_path, "name": provider_key +"."+ map_name, @@ -274,10 +294,12 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters "minZoom": tile_provider.get("min_zoom") or 0 }, "layerParameters": {"opacity": 1}, + "urlParameters": dict_keys_to_camel(url_parameters), + "description": tile_provider.get("attribution") } provider_gallery[provider_key] = providers_maps - + else: raise ValueError( f"Inconsistent config for provider '{provider_key}'" @@ -293,11 +315,10 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters except subprocess.CalledProcessError as e: print("⚠️ Image compression skipped:", e) """ - """ # compress a single image of THUMBNAILS_LOCATION -image_name = "MacroStrat-CartoVector.png" +image_name = "NaturalEarth-Countries.png" cmd = ( f'mogrify -resize 50% "{THUMBNAILS_LOCATION}/{image_name}" && ' f'optipng "{THUMBNAILS_LOCATION}/{image_name}"' @@ -305,6 +326,6 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters subprocess.run(["bash", "-lc", cmd], check=True) """ -with open(f"{THUMBNAILS_LOCATION}/layer_gallery.json", "w") as f: +with open(f"layer_gallery.json", "w") as f: json.dump(provider_gallery, f) diff --git a/packages/base/src/dialogs/layerBrowserDialog.tsx b/packages/base/src/dialogs/layerBrowserDialog.tsx index 386f9972f..6b892501e 100644 --- a/packages/base/src/dialogs/layerBrowserDialog.tsx +++ b/packages/base/src/dialogs/layerBrowserDialog.tsx @@ -228,7 +228,7 @@ export const LayerBrowserComponent: React.FC = ({

*/}

- {tile.sourceParameters.attribution} + {tile.description}

diff --git a/packages/base/src/tools.ts b/packages/base/src/tools.ts index d2b808359..a576ff73e 100644 --- a/packages/base/src/tools.ts +++ b/packages/base/src/tools.ts @@ -15,7 +15,7 @@ import { compressors } from 'hyparquet-compressors'; import Protobuf from 'pbf'; import shp from 'shpjs'; -import LAYER_GALLERY from '@/layer_gallery/layer_gallery.json'; +import LAYER_GALLERY from '@/layer_gallery.json'; export const debounce = ( func: CallableFunction, @@ -205,20 +205,6 @@ export function createDefaultLayerRegistry( layerProvider: { [x: string]: any }, provider?: string | undefined, ): ILayerGalleryEntry { - const urlParameters: any = {}; - - if (layerProvider.time) { - urlParameters.time = layerProvider.time; - } - if (layerProvider.variant) { - urlParameters.variant = layerProvider.variant; - } - if (layerProvider.tilematrixset) { - urlParameters.tilematrixset = layerProvider.tilematrixset; - } - if (layerProvider.format) { - urlParameters.format = layerProvider.format; - } if (!layerProvider.layerType || !layerProvider.sourceType) { throw new Error( @@ -234,6 +220,8 @@ export function createDefaultLayerRegistry( sourceParameters: layerProvider['sourceParameters'], layerParameters: layerProvider['layerParameters'], provider: provider ?? entry.split('.', 1)[0], + urlParameters: layerProvider['urlParameters'], + description: layerProvider['description'] }; } } @@ -389,9 +377,9 @@ export const getFromIndexedDB = async (key: string) => { const db = await openDatabase(); return new Promise< | { - file: any; - metadata?: any | undefined; - } + file: any; + metadata?: any | undefined; + } | undefined >((resolve, reject) => { const transaction = db.transaction('files', 'readonly'); diff --git a/packages/schema/src/interfaces.ts b/packages/schema/src/interfaces.ts index 2e0ab1b7a..ccd5640ae 100644 --- a/packages/schema/src/interfaces.ts +++ b/packages/schema/src/interfaces.ts @@ -32,10 +32,25 @@ import { } from './_interface/project/jgis'; import { + IGeoJSONSource, + IGeoParquetSource, + IGeoTiffSource, + IHeatmapLayer, + IHillshadeLayer, + IImageLayer, + IImageSource, + IMarkerSource, + IRasterDemSource, IRasterLayer, IRasterSource, + IShapefileSource, + IStacLayer, + IStorySegmentLayer, + IVectorLayer, IVectorTileLayer, IVectorTileSource, + IVideoSource, + IWebGlLayer, Modes, } from './types'; export { IGeoJSONSource } from './_interface/project/sources/geoJsonSource'; @@ -372,10 +387,12 @@ export type ILayerGalleryEntry = { name: string; thumbnail: string; layerType: LayerType; - layerParameters: IVectorTileLayer | IRasterLayer; + layerParameters: IHeatmapLayer | IHillshadeLayer | IImageLayer | IRasterLayer | IStacLayer | IStorySegmentLayer | IVectorLayer | IVectorTileLayer | IWebGlLayer; sourceType: SourceType; - sourceParameters: IVectorTileSource | IRasterSource; + sourceParameters: IGeoJSONSource | IGeoParquetSource | IGeoTiffSource | IImageSource | IMarkerSource | IRasterDemSource | IRasterSource | IShapefileSource | IVectorTileSource | IVideoSource; provider: string; + urlParameters: IDict; + description: string }; export interface IJGISLayerBrowserRegistry { From 77bd09405839c6a054be6e53a396c999495b856d Mon Sep 17 00:00:00 2001 From: Florence Haudin Date: Fri, 30 Jan 2026 16:03:26 +0100 Subject: [PATCH 5/8] Add the source type in the layer card used in the gallery. --- packages/base/layer_gallery.json | 716 +++++++++++++++++- .../base/layer_gallery/thumbnail_config.json | 2 +- .../base/src/dialogs/layerBrowserDialog.tsx | 1 + packages/base/src/tools.ts | 9 +- packages/schema/src/interfaces.ts | 25 +- 5 files changed, 743 insertions(+), 10 deletions(-) diff --git a/packages/base/layer_gallery.json b/packages/base/layer_gallery.json index 3e8541183..fafe6ca5f 100644 --- a/packages/base/layer_gallery.json +++ b/packages/base/layer_gallery.json @@ -1 +1,715 @@ -{"OpenStreetMap": {"Mapnik": {"thumbnailPath": "layer_gallery/OpenStreetMap-Mapnik.png", "name": "OpenStreetMap.Mapnik", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors", "maxZoom": 19, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "(C) OpenStreetMap contributors"}, "BZH": {"thumbnailPath": "layer_gallery/OpenStreetMap-BZH.png", "name": "OpenStreetMap.BZH", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tile.openstreetmap.bzh/br/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team", "maxZoom": 19, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team"}, "CAT": {"thumbnailPath": "layer_gallery/OpenStreetMap-CAT.png", "name": "OpenStreetMap.CAT", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tile.openstreetmap.bzh/ca/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team", "maxZoom": 19, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team"}, "CH": {"thumbnailPath": "layer_gallery/OpenStreetMap-CH.png", "name": "OpenStreetMap.CH", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tile.osm.ch/switzerland/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors", "maxZoom": 18, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "(C) OpenStreetMap contributors"}, "DE": {"thumbnailPath": "layer_gallery/OpenStreetMap-DE.png", "name": "OpenStreetMap.DE", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tile.openstreetmap.de/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors", "maxZoom": 18, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "(C) OpenStreetMap contributors"}, "France": {"thumbnailPath": "layer_gallery/OpenStreetMap-France.png", "name": "OpenStreetMap.France", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap France | (C) OpenStreetMap contributors", "maxZoom": 20, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "(C) OpenStreetMap France | (C) OpenStreetMap contributors"}, "HOT": {"thumbnailPath": "layer_gallery/OpenStreetMap-HOT.png", "name": "OpenStreetMap.HOT", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors, Tiles style by Humanitarian OpenStreetMap Team hosted by OpenStreetMap France", "maxZoom": 19, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "(C) OpenStreetMap contributors, Tiles style by Humanitarian OpenStreetMap Team hosted by OpenStreetMap France"}}, "NASAGIBS": {"ModisTerraTrueColorCR": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraTrueColorCR.png", "name": "NASAGIBS.ModisTerraTrueColorCR", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, "minZoom": 1}, "layerParameters": {"opacity": 1}, "urlParameters": {"tilematrixset": "GoogleMapsCompatible_Level", "format": "jpg", "time": "2026-01-29", "maxZoom": 9, "variant": "MODIS_Terra_CorrectedReflectance_TrueColor"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ModisTerraBands367CR": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraBands367CR.png", "name": "NASAGIBS.ModisTerraBands367CR", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, "minZoom": 1}, "layerParameters": {"opacity": 1}, "urlParameters": {"tilematrixset": "GoogleMapsCompatible_Level", "format": "jpg", "time": "2026-01-29", "maxZoom": 9, "variant": "MODIS_Terra_CorrectedReflectance_Bands367"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ViirsEarthAtNight2012": {"thumbnailPath": "layer_gallery/NASAGIBS-ViirsEarthAtNight2012.png", "name": "NASAGIBS.ViirsEarthAtNight2012", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 8, "minZoom": 1}, "layerParameters": {"opacity": 1}, "urlParameters": {"tilematrixset": "GoogleMapsCompatible_Level", "format": "jpg", "time": "2026-01-29", "maxZoom": 8, "variant": "VIIRS_CityLights_2012"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ModisTerraLSTDay": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraLSTDay.png", "name": "NASAGIBS.ModisTerraLSTDay", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 7, "minZoom": 1}, "layerParameters": {"opacity": 1}, "urlParameters": {"tilematrixset": "GoogleMapsCompatible_Level", "format": "png", "time": "2026-01-29", "maxZoom": 7, "variant": "MODIS_Terra_Land_Surface_Temp_Day"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ModisTerraSnowCover": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraSnowCover.png", "name": "NASAGIBS.ModisTerraSnowCover", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 8, "minZoom": 1}, "layerParameters": {"opacity": 1}, "urlParameters": {"tilematrixset": "GoogleMapsCompatible_Level", "format": "png", "time": "2026-01-29", "maxZoom": 8, "variant": "MODIS_Terra_NDSI_Snow_Cover"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ModisTerraAOD": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraAOD.png", "name": "NASAGIBS.ModisTerraAOD", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 6, "minZoom": 1}, "layerParameters": {"opacity": 1}, "urlParameters": {"tilematrixset": "GoogleMapsCompatible_Level", "format": "png", "time": "2026-01-29", "maxZoom": 6, "variant": "MODIS_Terra_Aerosol"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ModisTerraChlorophyll": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraChlorophyll.png", "name": "NASAGIBS.ModisTerraChlorophyll", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 7, "minZoom": 1}, "layerParameters": {"opacity": 1}, "urlParameters": {"tilematrixset": "GoogleMapsCompatible_Level", "format": "png", "time": "2026-01-29", "maxZoom": 7, "variant": "MODIS_Terra_L2_Chlorophyll_A"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ModisTerraBands721CR": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraBands721CR.png", "name": "NASAGIBS.ModisTerraBands721CR", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Terra_CorrectedReflectance_Bands721/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"time": "2026-01-29"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ModisAquaTrueColorCR": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisAquaTrueColorCR.png", "name": "NASAGIBS.ModisAquaTrueColorCR", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Aqua_CorrectedReflectance_TrueColor/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"time": "2026-01-29"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ModisAquaBands721CR": {"thumbnailPath": "layer_gallery/NASAGIBS-ModisAquaBands721CR.png", "name": "NASAGIBS.ModisAquaBands721CR", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Aqua_CorrectedReflectance_Bands721/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"time": "2026-01-29"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ViirsTrueColorCR": {"thumbnailPath": "layer_gallery/NASAGIBS-ViirsTrueColorCR.png", "name": "NASAGIBS.ViirsTrueColorCR", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/VIIRS_SNPP_CorrectedReflectance_TrueColor/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"time": "2026-01-29"}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}, "ASTER_GDEM_Greyscale_Shaded_Relief": {"thumbnailPath": "layer_gallery/NASAGIBS-ASTER_GDEM_Greyscale_Shaded_Relief.png", "name": "NASAGIBS.ASTER_GDEM_Greyscale_Shaded_Relief", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/ASTER_GDEM_Greyscale_Shaded_Relief/default/GoogleMapsCompatible_Level12/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 12, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ."}}, "USGS": {"USTopo": {"thumbnailPath": "layer_gallery/USGS-USTopo.png", "name": "USGS.USTopo", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles courtesy of the U.S. Geological Survey", "maxZoom": 20, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "Tiles courtesy of the U.S. Geological Survey"}, "USImagery": {"thumbnailPath": "layer_gallery/USGS-USImagery.png", "name": "USGS.USImagery", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles courtesy of the U.S. Geological Survey", "maxZoom": 20, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "Tiles courtesy of the U.S. Geological Survey"}, "USImageryTopo": {"thumbnailPath": "layer_gallery/USGS-USImageryTopo.png", "name": "USGS.USImageryTopo", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryTopo/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles courtesy of the U.S. Geological Survey", "maxZoom": 20, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "Tiles courtesy of the U.S. Geological Survey"}}, "Gaode": {"Normal": {"thumbnailPath": "layer_gallery/Gaode-Normal.png", "name": "Gaode.Normal", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "http://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}", "attribution": "© Gaode.com", "maxZoom": 19, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "© Gaode.com"}, "Satellite": {"thumbnailPath": "layer_gallery/Gaode-Satellite.png", "name": "Gaode.Satellite", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "http://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}", "attribution": "© Gaode.com", "maxZoom": 19, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "© Gaode.com"}}, "Strava": {"All": {"thumbnailPath": "layer_gallery/Strava-All.png", "name": "Strava.All", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://heatmap-external-a.strava.com/tiles/all/hot/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "Map tiles by Strava 2021"}, "Ride": {"thumbnailPath": "layer_gallery/Strava-Ride.png", "name": "Strava.Ride", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://heatmap-external-a.strava.com/tiles/ride/hot/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "Map tiles by Strava 2021"}, "Run": {"thumbnailPath": "layer_gallery/Strava-Run.png", "name": "Strava.Run", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://heatmap-external-a.strava.com/tiles/run/bluered/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "Map tiles by Strava 2021"}, "Water": {"thumbnailPath": "layer_gallery/Strava-Water.png", "name": "Strava.Water", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://heatmap-external-a.strava.com/tiles/water/blue/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "Map tiles by Strava 2021"}, "Winter": {"thumbnailPath": "layer_gallery/Strava-Winter.png", "name": "Strava.Winter", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://heatmap-external-a.strava.com/tiles/winter/hot/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "Map tiles by Strava 2021"}}, "OPNVKarte": {"thumbnailPath": "layer_gallery/OPNVKarte.png", "name": "OPNVKarte", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png", "attribution": "Map memomaps.de CC-BY-SA, map data (C) OpenStreetMap contributors", "maxZoom": 18, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": "https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png"}, "OpenTopoMap": {"thumbnailPath": "layer_gallery/OpenTopoMap.png", "name": "OpenTopoMap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", "attribution": "Map data: (C) OpenStreetMap contributors, SRTM | Map style: (C) OpenTopoMap (CC-BY-SA)", "maxZoom": 17, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png"}, "OpenRailwayMap": {"thumbnailPath": "layer_gallery/OpenRailwayMap.png", "name": "OpenRailwayMap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png", "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) OpenRailwayMap (CC-BY-SA)", "maxZoom": 19, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": "https://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png"}, "Esri": {"WorldStreetMap": {"thumbnailPath": "layer_gallery/Esri-WorldStreetMap.png", "name": "Esri.WorldStreetMap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012", "maxZoom": null, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"variant": "World_Street_Map"}, "description": "Tiles (C) Esri -- Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012"}, "WorldTopoMap": {"thumbnailPath": "layer_gallery/Esri-WorldTopoMap.png", "name": "Esri.WorldTopoMap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community", "maxZoom": null, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"variant": "World_Topo_Map"}, "description": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community"}, "WorldImagery": {"thumbnailPath": "layer_gallery/Esri-WorldImagery.png", "name": "Esri.WorldImagery", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community", "maxZoom": null, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"variant": "World_Imagery"}, "description": "Tiles (C) Esri -- Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community"}, "WorldTerrain": {"thumbnailPath": "layer_gallery/Esri-WorldTerrain.png", "name": "Esri.WorldTerrain", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: USGS, Esri, TANA, DeLorme, and NPS", "maxZoom": 13, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"variant": "World_Terrain_Base"}, "description": "Tiles (C) Esri -- Source: USGS, Esri, TANA, DeLorme, and NPS"}, "WorldShadedRelief": {"thumbnailPath": "layer_gallery/Esri-WorldShadedRelief.png", "name": "Esri.WorldShadedRelief", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: Esri", "maxZoom": 13, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"variant": "World_Shaded_Relief"}, "description": "Tiles (C) Esri -- Source: Esri"}, "WorldPhysical": {"thumbnailPath": "layer_gallery/Esri-WorldPhysical.png", "name": "Esri.WorldPhysical", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: US National Park Service", "maxZoom": 8, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"variant": "World_Physical_Map"}, "description": "Tiles (C) Esri -- Source: US National Park Service"}, "OceanBasemap": {"thumbnailPath": "layer_gallery/Esri-OceanBasemap.png", "name": "Esri.OceanBasemap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri", "maxZoom": 13, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"variant": "Ocean/World_Ocean_Base"}, "description": "Tiles (C) Esri -- Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri"}, "NatGeoWorldMap": {"thumbnailPath": "layer_gallery/Esri-NatGeoWorldMap.png", "name": "Esri.NatGeoWorldMap", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC", "maxZoom": 16, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"variant": "NatGeo_World_Map"}, "description": "Tiles (C) Esri -- National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC"}, "WorldGrayCanvas": {"thumbnailPath": "layer_gallery/Esri-WorldGrayCanvas.png", "name": "Esri.WorldGrayCanvas", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ", "maxZoom": 16, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {"variant": "Canvas/World_Light_Gray_Base"}, "description": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ"}}, "MacroStrat": {"CartoRaster": {"thumbnailPath": "layer_gallery/MacroStrat-CartoRaster.png", "name": "MacroStrat.CartoRaster", "layerType": "RasterLayer", "sourceType": "RasterSource", "sourceParameters": {"url": "https://tiles.macrostrat.org/carto/{z}/{x}/{y}.png", "attribution": "\u00a9 Geologic data \u00a9 Macrostrat raster layer (CC\u2011BY 4.0)", "maxZoom": 18, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "\u00a9 Geologic data \u00a9 Macrostrat raster layer (CC\u2011BY 4.0)"}, "CartoVector": {"thumbnailPath": "layer_gallery/MacroStrat-CartoVector.png", "name": "MacroStrat.CartoVector", "layerType": "VectorTileLayer", "sourceType": "VectorTileSource", "sourceParameters": {"url": "https://tiles.macrostrat.org/carto/{z}/{x}/{y}.mvt", "attribution": "\u00a9 Geologic data \u00a9 Macrostrat vector layer (CC\u2011BY 4.0)", "maxZoom": 18, "minZoom": 0}, "layerParameters": {"opacity": 1}, "urlParameters": {}, "description": "\u00a9 Geologic data \u00a9 Macrostrat vector layer (CC\u2011BY 4.0)"}}, "NaturalEarth": {"Countries": {"name": "NaturalEarth.Countries", "thumbnailPath": "/layer_gallery/NaturalEarth-Countries.png", "layerType": "VectorLayer", "layerParameters": {"opacity": 1, "symbologyState": {}}, "sourceType": "GeoJSONSource", "sourceParameters": {"path": "https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_110m_admin_0_countries.geojson"}, "provider": "NaturalEarth", "urlParameters": {}, "description": "Vector country boundaries from the Natural Earth dataset (1:110m scale). Public-domain geographic data intended for small-scale cartography, theming, and global map display."}}} \ No newline at end of file +{ + "OpenStreetMap": { + "Mapnik": { + "thumbnailPath": "layer_gallery/OpenStreetMap-Mapnik.png", + "name": "OpenStreetMap.Mapnik", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png", + "attribution": "(C) OpenStreetMap contributors", + "maxZoom": 19, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": {}, + "description": "(C) OpenStreetMap contributors" + }, + "BZH": { + "thumbnailPath": "layer_gallery/OpenStreetMap-BZH.png", + "name": "OpenStreetMap.BZH", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://tile.openstreetmap.bzh/br/{z}/{x}/{y}.png", + "attribution": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team", + "maxZoom": 19, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": {}, + "description": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team" + }, + "CAT": { + "thumbnailPath": "layer_gallery/OpenStreetMap-CAT.png", + "name": "OpenStreetMap.CAT", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://tile.openstreetmap.bzh/ca/{z}/{x}/{y}.png", + "attribution": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team", + "maxZoom": 19, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": {}, + "description": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team" + }, + "CH": { + "thumbnailPath": "layer_gallery/OpenStreetMap-CH.png", + "name": "OpenStreetMap.CH", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://tile.osm.ch/switzerland/{z}/{x}/{y}.png", + "attribution": "(C) OpenStreetMap contributors", + "maxZoom": 18, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": {}, + "description": "(C) OpenStreetMap contributors" + }, + "DE": { + "thumbnailPath": "layer_gallery/OpenStreetMap-DE.png", + "name": "OpenStreetMap.DE", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://tile.openstreetmap.de/{z}/{x}/{y}.png", + "attribution": "(C) OpenStreetMap contributors", + "maxZoom": 18, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": {}, + "description": "(C) OpenStreetMap contributors" + }, + "France": { + "thumbnailPath": "layer_gallery/OpenStreetMap-France.png", + "name": "OpenStreetMap.France", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png", + "attribution": "(C) OpenStreetMap France | (C) OpenStreetMap contributors", + "maxZoom": 20, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": {}, + "description": "(C) OpenStreetMap France | (C) OpenStreetMap contributors" + }, + "HOT": { + "thumbnailPath": "layer_gallery/OpenStreetMap-HOT.png", + "name": "OpenStreetMap.HOT", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png", + "attribution": "(C) OpenStreetMap contributors, Tiles style by Humanitarian OpenStreetMap Team hosted by OpenStreetMap France", + "maxZoom": 19, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": {}, + "description": "(C) OpenStreetMap contributors, Tiles style by Humanitarian OpenStreetMap Team hosted by OpenStreetMap France" + } + }, + "NASAGIBS": { + "ModisTerraTrueColorCR": { + "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraTrueColorCR.png", + "name": "NASAGIBS.ModisTerraTrueColorCR", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 9, + "minZoom": 1 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { + "tilematrixset": "GoogleMapsCompatible_Level", + "format": "jpg", + "time": "2026-01-29", + "maxZoom": 9, + "variant": "MODIS_Terra_CorrectedReflectance_TrueColor" + }, + "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." + }, + "ModisTerraBands367CR": { + "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraBands367CR.png", + "name": "NASAGIBS.ModisTerraBands367CR", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 9, + "minZoom": 1 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { + "tilematrixset": "GoogleMapsCompatible_Level", + "format": "jpg", + "time": "2026-01-29", + "maxZoom": 9, + "variant": "MODIS_Terra_CorrectedReflectance_Bands367" + }, + "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." + }, + "ViirsEarthAtNight2012": { + "thumbnailPath": "layer_gallery/NASAGIBS-ViirsEarthAtNight2012.png", + "name": "NASAGIBS.ViirsEarthAtNight2012", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 8, + "minZoom": 1 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { + "tilematrixset": "GoogleMapsCompatible_Level", + "format": "jpg", + "time": "2026-01-29", + "maxZoom": 8, + "variant": "VIIRS_CityLights_2012" + }, + "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." + }, + "ModisTerraLSTDay": { + "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraLSTDay.png", + "name": "NASAGIBS.ModisTerraLSTDay", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 7, + "minZoom": 1 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { + "tilematrixset": "GoogleMapsCompatible_Level", + "format": "png", + "time": "2026-01-29", + "maxZoom": 7, + "variant": "MODIS_Terra_Land_Surface_Temp_Day" + }, + "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." + }, + "ModisTerraSnowCover": { + "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraSnowCover.png", + "name": "NASAGIBS.ModisTerraSnowCover", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 8, + "minZoom": 1 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { + "tilematrixset": "GoogleMapsCompatible_Level", + "format": "png", + "time": "2026-01-29", + "maxZoom": 8, + "variant": "MODIS_Terra_NDSI_Snow_Cover" + }, + "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." + }, + "ModisTerraAOD": { + "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraAOD.png", + "name": "NASAGIBS.ModisTerraAOD", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 6, + "minZoom": 1 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { + "tilematrixset": "GoogleMapsCompatible_Level", + "format": "png", + "time": "2026-01-29", + "maxZoom": 6, + "variant": "MODIS_Terra_Aerosol" + }, + "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." + }, + "ModisTerraChlorophyll": { + "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraChlorophyll.png", + "name": "NASAGIBS.ModisTerraChlorophyll", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 7, + "minZoom": 1 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { + "tilematrixset": "GoogleMapsCompatible_Level", + "format": "png", + "time": "2026-01-29", + "maxZoom": 7, + "variant": "MODIS_Terra_L2_Chlorophyll_A" + }, + "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." + }, + "ModisTerraBands721CR": { + "thumbnailPath": "layer_gallery/NASAGIBS-ModisTerraBands721CR.png", + "name": "NASAGIBS.ModisTerraBands721CR", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Terra_CorrectedReflectance_Bands721/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 9, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { "time": "2026-01-29" }, + "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." + }, + "ModisAquaTrueColorCR": { + "thumbnailPath": "layer_gallery/NASAGIBS-ModisAquaTrueColorCR.png", + "name": "NASAGIBS.ModisAquaTrueColorCR", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Aqua_CorrectedReflectance_TrueColor/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 9, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { "time": "2026-01-29" }, + "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." + }, + "ModisAquaBands721CR": { + "thumbnailPath": "layer_gallery/NASAGIBS-ModisAquaBands721CR.png", + "name": "NASAGIBS.ModisAquaBands721CR", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Aqua_CorrectedReflectance_Bands721/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 9, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { "time": "2026-01-29" }, + "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." + }, + "ViirsTrueColorCR": { + "thumbnailPath": "layer_gallery/NASAGIBS-ViirsTrueColorCR.png", + "name": "NASAGIBS.ViirsTrueColorCR", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/VIIRS_SNPP_CorrectedReflectance_TrueColor/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 9, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { "time": "2026-01-29" }, + "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." + }, + "ASTER_GDEM_Greyscale_Shaded_Relief": { + "thumbnailPath": "layer_gallery/NASAGIBS-ASTER_GDEM_Greyscale_Shaded_Relief.png", + "name": "NASAGIBS.ASTER_GDEM_Greyscale_Shaded_Relief", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/ASTER_GDEM_Greyscale_Shaded_Relief/default/GoogleMapsCompatible_Level12/{z}/{y}/{x}.jpg", + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 12, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": {}, + "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." + } + }, + "USGS": { + "USTopo": { + "thumbnailPath": "layer_gallery/USGS-USTopo.png", + "name": "USGS.USTopo", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles courtesy of the U.S. Geological Survey", + "maxZoom": 20, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": {}, + "description": "Tiles courtesy of the U.S. Geological Survey" + }, + "USImagery": { + "thumbnailPath": "layer_gallery/USGS-USImagery.png", + "name": "USGS.USImagery", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles courtesy of the U.S. Geological Survey", + "maxZoom": 20, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": {}, + "description": "Tiles courtesy of the U.S. Geological Survey" + }, + "USImageryTopo": { + "thumbnailPath": "layer_gallery/USGS-USImageryTopo.png", + "name": "USGS.USImageryTopo", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryTopo/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles courtesy of the U.S. Geological Survey", + "maxZoom": 20, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": {}, + "description": "Tiles courtesy of the U.S. Geological Survey" + } + }, + "Gaode": { + "Normal": { + "thumbnailPath": "layer_gallery/Gaode-Normal.png", + "name": "Gaode.Normal", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "http://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}", + "attribution": "© Gaode.com", + "maxZoom": 19, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": {}, + "description": "© Gaode.com" + }, + "Satellite": { + "thumbnailPath": "layer_gallery/Gaode-Satellite.png", + "name": "Gaode.Satellite", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "http://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}", + "attribution": "© Gaode.com", + "maxZoom": 19, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": {}, + "description": "© Gaode.com" + } + }, + "Strava": { + "All": { + "thumbnailPath": "layer_gallery/Strava-All.png", + "name": "Strava.All", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://heatmap-external-a.strava.com/tiles/all/hot/{z}/{x}/{y}.png", + "attribution": "Map tiles by Strava 2021", + "maxZoom": 15, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": {}, + "description": "Map tiles by Strava 2021" + }, + "Ride": { + "thumbnailPath": "layer_gallery/Strava-Ride.png", + "name": "Strava.Ride", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://heatmap-external-a.strava.com/tiles/ride/hot/{z}/{x}/{y}.png", + "attribution": "Map tiles by Strava 2021", + "maxZoom": 15, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": {}, + "description": "Map tiles by Strava 2021" + }, + "Run": { + "thumbnailPath": "layer_gallery/Strava-Run.png", + "name": "Strava.Run", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://heatmap-external-a.strava.com/tiles/run/bluered/{z}/{x}/{y}.png", + "attribution": "Map tiles by Strava 2021", + "maxZoom": 15, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": {}, + "description": "Map tiles by Strava 2021" + }, + "Water": { + "thumbnailPath": "layer_gallery/Strava-Water.png", + "name": "Strava.Water", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://heatmap-external-a.strava.com/tiles/water/blue/{z}/{x}/{y}.png", + "attribution": "Map tiles by Strava 2021", + "maxZoom": 15, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": {}, + "description": "Map tiles by Strava 2021" + }, + "Winter": { + "thumbnailPath": "layer_gallery/Strava-Winter.png", + "name": "Strava.Winter", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://heatmap-external-a.strava.com/tiles/winter/hot/{z}/{x}/{y}.png", + "attribution": "Map tiles by Strava 2021", + "maxZoom": 15, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": {}, + "description": "Map tiles by Strava 2021" + } + }, + "OPNVKarte": { + "thumbnailPath": "layer_gallery/OPNVKarte.png", + "name": "OPNVKarte", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png", + "attribution": "Map memomaps.de CC-BY-SA, map data (C) OpenStreetMap contributors", + "maxZoom": 18, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": "https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png" + }, + "OpenTopoMap": { + "thumbnailPath": "layer_gallery/OpenTopoMap.png", + "name": "OpenTopoMap", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", + "attribution": "Map data: (C) OpenStreetMap contributors, SRTM | Map style: (C) OpenTopoMap (CC-BY-SA)", + "maxZoom": 17, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png" + }, + "OpenRailwayMap": { + "thumbnailPath": "layer_gallery/OpenRailwayMap.png", + "name": "OpenRailwayMap", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png", + "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) OpenRailwayMap (CC-BY-SA)", + "maxZoom": 19, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": "https://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png" + }, + "Esri": { + "WorldStreetMap": { + "thumbnailPath": "layer_gallery/Esri-WorldStreetMap.png", + "name": "Esri.WorldStreetMap", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles (C) Esri -- Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012", + "maxZoom": null, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { "variant": "World_Street_Map" }, + "description": "Tiles (C) Esri -- Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012" + }, + "WorldTopoMap": { + "thumbnailPath": "layer_gallery/Esri-WorldTopoMap.png", + "name": "Esri.WorldTopoMap", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community", + "maxZoom": null, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { "variant": "World_Topo_Map" }, + "description": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community" + }, + "WorldImagery": { + "thumbnailPath": "layer_gallery/Esri-WorldImagery.png", + "name": "Esri.WorldImagery", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles (C) Esri -- Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community", + "maxZoom": null, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { "variant": "World_Imagery" }, + "description": "Tiles (C) Esri -- Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community" + }, + "WorldTerrain": { + "thumbnailPath": "layer_gallery/Esri-WorldTerrain.png", + "name": "Esri.WorldTerrain", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles (C) Esri -- Source: USGS, Esri, TANA, DeLorme, and NPS", + "maxZoom": 13, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { "variant": "World_Terrain_Base" }, + "description": "Tiles (C) Esri -- Source: USGS, Esri, TANA, DeLorme, and NPS" + }, + "WorldShadedRelief": { + "thumbnailPath": "layer_gallery/Esri-WorldShadedRelief.png", + "name": "Esri.WorldShadedRelief", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles (C) Esri -- Source: Esri", + "maxZoom": 13, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { "variant": "World_Shaded_Relief" }, + "description": "Tiles (C) Esri -- Source: Esri" + }, + "WorldPhysical": { + "thumbnailPath": "layer_gallery/Esri-WorldPhysical.png", + "name": "Esri.WorldPhysical", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles (C) Esri -- Source: US National Park Service", + "maxZoom": 8, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { "variant": "World_Physical_Map" }, + "description": "Tiles (C) Esri -- Source: US National Park Service" + }, + "OceanBasemap": { + "thumbnailPath": "layer_gallery/Esri-OceanBasemap.png", + "name": "Esri.OceanBasemap", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles (C) Esri -- Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri", + "maxZoom": 13, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { "variant": "Ocean/World_Ocean_Base" }, + "description": "Tiles (C) Esri -- Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri" + }, + "NatGeoWorldMap": { + "thumbnailPath": "layer_gallery/Esri-NatGeoWorldMap.png", + "name": "Esri.NatGeoWorldMap", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles (C) Esri -- National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC", + "maxZoom": 16, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { "variant": "NatGeo_World_Map" }, + "description": "Tiles (C) Esri -- National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC" + }, + "WorldGrayCanvas": { + "thumbnailPath": "layer_gallery/Esri-WorldGrayCanvas.png", + "name": "Esri.WorldGrayCanvas", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", + "attribution": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ", + "maxZoom": 16, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { "variant": "Canvas/World_Light_Gray_Base" }, + "description": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ" + } + }, + "MacroStrat": { + "CartoRaster": { + "thumbnailPath": "layer_gallery/MacroStrat-CartoRaster.png", + "name": "MacroStrat.CartoRaster", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://tiles.macrostrat.org/carto/{z}/{x}/{y}.png", + "attribution": "\u00a9 Geologic data \u00a9 Macrostrat raster layer (CC\u2011BY 4.0)", + "maxZoom": 18, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": {}, + "description": "\u00a9 Geologic data \u00a9 Macrostrat raster layer (CC\u2011BY 4.0)" + }, + "CartoVector": { + "thumbnailPath": "layer_gallery/MacroStrat-CartoVector.png", + "name": "MacroStrat.CartoVector", + "layerType": "VectorTileLayer", + "sourceType": "VectorTileSource", + "sourceParameters": { + "url": "https://tiles.macrostrat.org/carto/{z}/{x}/{y}.mvt", + "attribution": "\u00a9 Geologic data \u00a9 Macrostrat vector layer (CC\u2011BY 4.0)", + "maxZoom": 18, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": {}, + "description": "\u00a9 Geologic data \u00a9 Macrostrat vector layer (CC\u2011BY 4.0)" + } + }, + "NaturalEarth": { + "Countries": { + "name": "NaturalEarth.Countries", + "thumbnailPath": "/layer_gallery/NaturalEarth-Countries.png", + "layerType": "VectorLayer", + "layerParameters": { "opacity": 1, "symbologyState": {} }, + "sourceType": "GeoJSONSource", + "sourceParameters": { + "path": "https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_110m_admin_0_countries.geojson" + }, + "provider": "NaturalEarth", + "urlParameters": {}, + "description": "Vector country boundaries from the Natural Earth dataset (1:110m scale). Public-domain geographic data intended for small-scale cartography, theming, and global map display." + } + } +} diff --git a/packages/base/layer_gallery/thumbnail_config.json b/packages/base/layer_gallery/thumbnail_config.json index 996a8f220..ab94378ba 100644 --- a/packages/base/layer_gallery/thumbnail_config.json +++ b/packages/base/layer_gallery/thumbnail_config.json @@ -453,4 +453,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/base/src/dialogs/layerBrowserDialog.tsx b/packages/base/src/dialogs/layerBrowserDialog.tsx index 6b892501e..09d6117bb 100644 --- a/packages/base/src/dialogs/layerBrowserDialog.tsx +++ b/packages/base/src/dialogs/layerBrowserDialog.tsx @@ -227,6 +227,7 @@ export const LayerBrowserComponent: React.FC = ({ placeholder

*/} +
{tile.sourceType}

{tile.description}

diff --git a/packages/base/src/tools.ts b/packages/base/src/tools.ts index a576ff73e..aa03f8c58 100644 --- a/packages/base/src/tools.ts +++ b/packages/base/src/tools.ts @@ -205,7 +205,6 @@ export function createDefaultLayerRegistry( layerProvider: { [x: string]: any }, provider?: string | undefined, ): ILayerGalleryEntry { - if (!layerProvider.layerType || !layerProvider.sourceType) { throw new Error( `Invalid layer_gallery entry "${entry}": missing layerType or sourceType`, @@ -221,7 +220,7 @@ export function createDefaultLayerRegistry( layerParameters: layerProvider['layerParameters'], provider: provider ?? entry.split('.', 1)[0], urlParameters: layerProvider['urlParameters'], - description: layerProvider['description'] + description: layerProvider['description'], }; } } @@ -377,9 +376,9 @@ export const getFromIndexedDB = async (key: string) => { const db = await openDatabase(); return new Promise< | { - file: any; - metadata?: any | undefined; - } + file: any; + metadata?: any | undefined; + } | undefined >((resolve, reject) => { const transaction = db.transaction('files', 'readonly'); diff --git a/packages/schema/src/interfaces.ts b/packages/schema/src/interfaces.ts index ccd5640ae..5625c0000 100644 --- a/packages/schema/src/interfaces.ts +++ b/packages/schema/src/interfaces.ts @@ -387,12 +387,31 @@ export type ILayerGalleryEntry = { name: string; thumbnail: string; layerType: LayerType; - layerParameters: IHeatmapLayer | IHillshadeLayer | IImageLayer | IRasterLayer | IStacLayer | IStorySegmentLayer | IVectorLayer | IVectorTileLayer | IWebGlLayer; + layerParameters: + | IHeatmapLayer + | IHillshadeLayer + | IImageLayer + | IRasterLayer + | IStacLayer + | IStorySegmentLayer + | IVectorLayer + | IVectorTileLayer + | IWebGlLayer; sourceType: SourceType; - sourceParameters: IGeoJSONSource | IGeoParquetSource | IGeoTiffSource | IImageSource | IMarkerSource | IRasterDemSource | IRasterSource | IShapefileSource | IVectorTileSource | IVideoSource; + sourceParameters: + | IGeoJSONSource + | IGeoParquetSource + | IGeoTiffSource + | IImageSource + | IMarkerSource + | IRasterDemSource + | IRasterSource + | IShapefileSource + | IVectorTileSource + | IVideoSource; provider: string; urlParameters: IDict; - description: string + description: string; }; export interface IJGISLayerBrowserRegistry { From a3dc806b12843ee4da922ce35fe0930839325de7 Mon Sep 17 00:00:00 2001 From: Florence Haudin Date: Fri, 30 Jan 2026 17:36:25 +0100 Subject: [PATCH 6/8] Restore WaymarkedTrails layers. --- packages/base/layer_gallery.json | 149 +++++++++++++----- .../base/layer_gallery/thumbnail_config.json | 74 +++++++++ 2 files changed, 187 insertions(+), 36 deletions(-) diff --git a/packages/base/layer_gallery.json b/packages/base/layer_gallery.json index fafe6ca5f..d7b15d2bd 100644 --- a/packages/base/layer_gallery.json +++ b/packages/base/layer_gallery.json @@ -120,11 +120,11 @@ }, "layerParameters": { "opacity": 1 }, "urlParameters": { - "tilematrixset": "GoogleMapsCompatible_Level", + "maxZoom": 9, "format": "jpg", + "variant": "MODIS_Terra_CorrectedReflectance_TrueColor", "time": "2026-01-29", - "maxZoom": 9, - "variant": "MODIS_Terra_CorrectedReflectance_TrueColor" + "tilematrixset": "GoogleMapsCompatible_Level" }, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." }, @@ -141,11 +141,11 @@ }, "layerParameters": { "opacity": 1 }, "urlParameters": { - "tilematrixset": "GoogleMapsCompatible_Level", + "maxZoom": 9, "format": "jpg", + "variant": "MODIS_Terra_CorrectedReflectance_Bands367", "time": "2026-01-29", - "maxZoom": 9, - "variant": "MODIS_Terra_CorrectedReflectance_Bands367" + "tilematrixset": "GoogleMapsCompatible_Level" }, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." }, @@ -162,11 +162,11 @@ }, "layerParameters": { "opacity": 1 }, "urlParameters": { - "tilematrixset": "GoogleMapsCompatible_Level", + "maxZoom": 8, "format": "jpg", + "variant": "VIIRS_CityLights_2012", "time": "2026-01-29", - "maxZoom": 8, - "variant": "VIIRS_CityLights_2012" + "tilematrixset": "GoogleMapsCompatible_Level" }, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." }, @@ -183,11 +183,11 @@ }, "layerParameters": { "opacity": 1 }, "urlParameters": { - "tilematrixset": "GoogleMapsCompatible_Level", + "maxZoom": 7, "format": "png", + "variant": "MODIS_Terra_Land_Surface_Temp_Day", "time": "2026-01-29", - "maxZoom": 7, - "variant": "MODIS_Terra_Land_Surface_Temp_Day" + "tilematrixset": "GoogleMapsCompatible_Level" }, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." }, @@ -204,11 +204,11 @@ }, "layerParameters": { "opacity": 1 }, "urlParameters": { - "tilematrixset": "GoogleMapsCompatible_Level", + "maxZoom": 8, "format": "png", + "variant": "MODIS_Terra_NDSI_Snow_Cover", "time": "2026-01-29", - "maxZoom": 8, - "variant": "MODIS_Terra_NDSI_Snow_Cover" + "tilematrixset": "GoogleMapsCompatible_Level" }, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." }, @@ -225,11 +225,11 @@ }, "layerParameters": { "opacity": 1 }, "urlParameters": { - "tilematrixset": "GoogleMapsCompatible_Level", + "maxZoom": 6, "format": "png", + "variant": "MODIS_Terra_Aerosol", "time": "2026-01-29", - "maxZoom": 6, - "variant": "MODIS_Terra_Aerosol" + "tilematrixset": "GoogleMapsCompatible_Level" }, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." }, @@ -246,11 +246,11 @@ }, "layerParameters": { "opacity": 1 }, "urlParameters": { - "tilematrixset": "GoogleMapsCompatible_Level", + "maxZoom": 7, "format": "png", + "variant": "MODIS_Terra_L2_Chlorophyll_A", "time": "2026-01-29", - "maxZoom": 7, - "variant": "MODIS_Terra_L2_Chlorophyll_A" + "tilematrixset": "GoogleMapsCompatible_Level" }, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." }, @@ -330,6 +330,98 @@ "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." } }, + "WaymarkedTrails": { + "hiking": { + "thumbnailPath": "layer_gallery/WaymarkedTrails-hiking.png", + "name": "WaymarkedTrails.hiking", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png", + "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)", + "maxZoom": 18, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { "variant": "hiking" }, + "description": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)" + }, + "cycling": { + "thumbnailPath": "layer_gallery/WaymarkedTrails-cycling.png", + "name": "WaymarkedTrails.cycling", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png", + "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)", + "maxZoom": 18, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { "variant": "cycling" }, + "description": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)" + }, + "mtb": { + "thumbnailPath": "layer_gallery/WaymarkedTrails-mtb.png", + "name": "WaymarkedTrails.mtb", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png", + "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)", + "maxZoom": 18, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { "variant": "mtb" }, + "description": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)" + }, + "slopes": { + "thumbnailPath": "layer_gallery/WaymarkedTrails-slopes.png", + "name": "WaymarkedTrails.slopes", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png", + "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)", + "maxZoom": 18, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { "variant": "slopes" }, + "description": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)" + }, + "riding": { + "thumbnailPath": "layer_gallery/WaymarkedTrails-riding.png", + "name": "WaymarkedTrails.riding", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png", + "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)", + "maxZoom": 18, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { "variant": "riding" }, + "description": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)" + }, + "skating": { + "thumbnailPath": "layer_gallery/WaymarkedTrails-skating.png", + "name": "WaymarkedTrails.skating", + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "sourceParameters": { + "url": "https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png", + "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)", + "maxZoom": 18, + "minZoom": 0 + }, + "layerParameters": { "opacity": 1 }, + "urlParameters": { "variant": "skating" }, + "description": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)" + } + }, "USGS": { "USTopo": { "thumbnailPath": "layer_gallery/USGS-USTopo.png", @@ -696,20 +788,5 @@ "urlParameters": {}, "description": "\u00a9 Geologic data \u00a9 Macrostrat vector layer (CC\u2011BY 4.0)" } - }, - "NaturalEarth": { - "Countries": { - "name": "NaturalEarth.Countries", - "thumbnailPath": "/layer_gallery/NaturalEarth-Countries.png", - "layerType": "VectorLayer", - "layerParameters": { "opacity": 1, "symbologyState": {} }, - "sourceType": "GeoJSONSource", - "sourceParameters": { - "path": "https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_110m_admin_0_countries.geojson" - }, - "provider": "NaturalEarth", - "urlParameters": {}, - "description": "Vector country boundaries from the Natural Earth dataset (1:110m scale). Public-domain geographic data intended for small-scale cartography, theming, and global map display." - } } } diff --git a/packages/base/layer_gallery/thumbnail_config.json b/packages/base/layer_gallery/thumbnail_config.json index ab94378ba..54b1b0f1c 100644 --- a/packages/base/layer_gallery/thumbnail_config.json +++ b/packages/base/layer_gallery/thumbnail_config.json @@ -243,6 +243,80 @@ } } }, + "WaymarkedTrails": { + "hiking": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "cycling": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "mtb": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "slopes": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "riding": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + }, + "skating": { + "layerType": "RasterLayer", + "sourceType": "RasterSource", + "thumbnail": { + "Special Rules": {}, + "Default": { + "lat": 47.040182144806664, + "lng": 1.2963867187500002, + "zoom": 5 + } + } + } + }, "USGS": { "layerType": "RasterLayer", "sourceType": "RasterSource", From 61feb6d9b6c75540efe59a3cebc89e51b8578d1d Mon Sep 17 00:00:00 2001 From: Florence Haudin Date: Tue, 3 Feb 2026 14:49:33 +0100 Subject: [PATCH 7/8] Move urlParameters into sourceParameters to properly fit the schema. --- packages/base/layer_gallery.json | 286 +++++++++++------------ packages/base/layer_gallery_generator.py | 13 +- packages/base/src/tools.ts | 7 - packages/schema/src/interfaces.ts | 1 - 4 files changed, 150 insertions(+), 157 deletions(-) diff --git a/packages/base/layer_gallery.json b/packages/base/layer_gallery.json index d7b15d2bd..20f9a62e8 100644 --- a/packages/base/layer_gallery.json +++ b/packages/base/layer_gallery.json @@ -9,10 +9,10 @@ "url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors", "maxZoom": 19, - "minZoom": 0 + "minZoom": 0, + "urlParameters": {} }, "layerParameters": { "opacity": 1 }, - "urlParameters": {}, "description": "(C) OpenStreetMap contributors" }, "BZH": { @@ -24,10 +24,10 @@ "url": "https://tile.openstreetmap.bzh/br/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team", "maxZoom": 19, - "minZoom": 0 + "minZoom": 0, + "urlParameters": {} }, "layerParameters": { "opacity": 1 }, - "urlParameters": {}, "description": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team" }, "CAT": { @@ -39,10 +39,10 @@ "url": "https://tile.openstreetmap.bzh/ca/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team", "maxZoom": 19, - "minZoom": 0 + "minZoom": 0, + "urlParameters": {} }, "layerParameters": { "opacity": 1 }, - "urlParameters": {}, "description": "(C) OpenStreetMap contributors, Tiles courtesy of Breton OpenStreetMap Team" }, "CH": { @@ -54,10 +54,10 @@ "url": "https://tile.osm.ch/switzerland/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors", "maxZoom": 18, - "minZoom": 0 + "minZoom": 0, + "urlParameters": {} }, "layerParameters": { "opacity": 1 }, - "urlParameters": {}, "description": "(C) OpenStreetMap contributors" }, "DE": { @@ -69,10 +69,10 @@ "url": "https://tile.openstreetmap.de/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors", "maxZoom": 18, - "minZoom": 0 + "minZoom": 0, + "urlParameters": {} }, "layerParameters": { "opacity": 1 }, - "urlParameters": {}, "description": "(C) OpenStreetMap contributors" }, "France": { @@ -84,10 +84,10 @@ "url": "https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap France | (C) OpenStreetMap contributors", "maxZoom": 20, - "minZoom": 0 + "minZoom": 0, + "urlParameters": {} }, "layerParameters": { "opacity": 1 }, - "urlParameters": {}, "description": "(C) OpenStreetMap France | (C) OpenStreetMap contributors" }, "HOT": { @@ -99,10 +99,10 @@ "url": "https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png", "attribution": "(C) OpenStreetMap contributors, Tiles style by Humanitarian OpenStreetMap Team hosted by OpenStreetMap France", "maxZoom": 19, - "minZoom": 0 + "minZoom": 0, + "urlParameters": {} }, "layerParameters": { "opacity": 1 }, - "urlParameters": {}, "description": "(C) OpenStreetMap contributors, Tiles style by Humanitarian OpenStreetMap Team hosted by OpenStreetMap France" } }, @@ -116,16 +116,16 @@ "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, - "minZoom": 1 + "minZoom": 1, + "urlParameters": { + "maxZoom": 9, + "variant": "MODIS_Terra_CorrectedReflectance_TrueColor", + "time": "2026-02-02", + "format": "jpg", + "tilematrixset": "GoogleMapsCompatible_Level" + } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { - "maxZoom": 9, - "format": "jpg", - "variant": "MODIS_Terra_CorrectedReflectance_TrueColor", - "time": "2026-01-29", - "tilematrixset": "GoogleMapsCompatible_Level" - }, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." }, "ModisTerraBands367CR": { @@ -137,16 +137,16 @@ "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, - "minZoom": 1 + "minZoom": 1, + "urlParameters": { + "maxZoom": 9, + "variant": "MODIS_Terra_CorrectedReflectance_Bands367", + "time": "2026-02-02", + "format": "jpg", + "tilematrixset": "GoogleMapsCompatible_Level" + } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { - "maxZoom": 9, - "format": "jpg", - "variant": "MODIS_Terra_CorrectedReflectance_Bands367", - "time": "2026-01-29", - "tilematrixset": "GoogleMapsCompatible_Level" - }, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." }, "ViirsEarthAtNight2012": { @@ -158,16 +158,16 @@ "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 8, - "minZoom": 1 + "minZoom": 1, + "urlParameters": { + "maxZoom": 8, + "variant": "VIIRS_CityLights_2012", + "time": "2026-02-02", + "format": "jpg", + "tilematrixset": "GoogleMapsCompatible_Level" + } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { - "maxZoom": 8, - "format": "jpg", - "variant": "VIIRS_CityLights_2012", - "time": "2026-01-29", - "tilematrixset": "GoogleMapsCompatible_Level" - }, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." }, "ModisTerraLSTDay": { @@ -179,16 +179,16 @@ "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 7, - "minZoom": 1 + "minZoom": 1, + "urlParameters": { + "maxZoom": 7, + "variant": "MODIS_Terra_Land_Surface_Temp_Day", + "time": "2026-02-02", + "format": "png", + "tilematrixset": "GoogleMapsCompatible_Level" + } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { - "maxZoom": 7, - "format": "png", - "variant": "MODIS_Terra_Land_Surface_Temp_Day", - "time": "2026-01-29", - "tilematrixset": "GoogleMapsCompatible_Level" - }, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." }, "ModisTerraSnowCover": { @@ -200,16 +200,16 @@ "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 8, - "minZoom": 1 + "minZoom": 1, + "urlParameters": { + "maxZoom": 8, + "variant": "MODIS_Terra_NDSI_Snow_Cover", + "time": "2026-02-02", + "format": "png", + "tilematrixset": "GoogleMapsCompatible_Level" + } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { - "maxZoom": 8, - "format": "png", - "variant": "MODIS_Terra_NDSI_Snow_Cover", - "time": "2026-01-29", - "tilematrixset": "GoogleMapsCompatible_Level" - }, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." }, "ModisTerraAOD": { @@ -221,16 +221,16 @@ "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 6, - "minZoom": 1 + "minZoom": 1, + "urlParameters": { + "maxZoom": 6, + "variant": "MODIS_Terra_Aerosol", + "time": "2026-02-02", + "format": "png", + "tilematrixset": "GoogleMapsCompatible_Level" + } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { - "maxZoom": 6, - "format": "png", - "variant": "MODIS_Terra_Aerosol", - "time": "2026-01-29", - "tilematrixset": "GoogleMapsCompatible_Level" - }, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." }, "ModisTerraChlorophyll": { @@ -242,16 +242,16 @@ "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 7, - "minZoom": 1 + "minZoom": 1, + "urlParameters": { + "maxZoom": 7, + "variant": "MODIS_Terra_L2_Chlorophyll_A", + "time": "2026-02-02", + "format": "png", + "tilematrixset": "GoogleMapsCompatible_Level" + } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { - "maxZoom": 7, - "format": "png", - "variant": "MODIS_Terra_L2_Chlorophyll_A", - "time": "2026-01-29", - "tilematrixset": "GoogleMapsCompatible_Level" - }, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." }, "ModisTerraBands721CR": { @@ -263,10 +263,10 @@ "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Terra_CorrectedReflectance_Bands721/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, - "minZoom": 0 + "minZoom": 0, + "urlParameters": { "time": "2026-02-02" } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { "time": "2026-01-29" }, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." }, "ModisAquaTrueColorCR": { @@ -278,10 +278,10 @@ "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Aqua_CorrectedReflectance_TrueColor/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, - "minZoom": 0 + "minZoom": 0, + "urlParameters": { "time": "2026-02-02" } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { "time": "2026-01-29" }, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." }, "ModisAquaBands721CR": { @@ -293,10 +293,10 @@ "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/MODIS_Aqua_CorrectedReflectance_Bands721/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, - "minZoom": 0 + "minZoom": 0, + "urlParameters": { "time": "2026-02-02" } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { "time": "2026-01-29" }, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." }, "ViirsTrueColorCR": { @@ -308,10 +308,10 @@ "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/VIIRS_SNPP_CorrectedReflectance_TrueColor/default/{time}/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 9, - "minZoom": 0 + "minZoom": 0, + "urlParameters": { "time": "2026-02-02" } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { "time": "2026-01-29" }, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." }, "ASTER_GDEM_Greyscale_Shaded_Relief": { @@ -323,10 +323,10 @@ "url": "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/ASTER_GDEM_Greyscale_Shaded_Relief/default/GoogleMapsCompatible_Level12/{z}/{y}/{x}.jpg", "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", "maxZoom": 12, - "minZoom": 0 + "minZoom": 0, + "urlParameters": {} }, "layerParameters": { "opacity": 1 }, - "urlParameters": {}, "description": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ." } }, @@ -340,10 +340,10 @@ "url": "https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png", "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)", "maxZoom": 18, - "minZoom": 0 + "minZoom": 0, + "urlParameters": { "variant": "hiking" } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { "variant": "hiking" }, "description": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)" }, "cycling": { @@ -355,10 +355,10 @@ "url": "https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png", "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)", "maxZoom": 18, - "minZoom": 0 + "minZoom": 0, + "urlParameters": { "variant": "cycling" } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { "variant": "cycling" }, "description": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)" }, "mtb": { @@ -370,10 +370,10 @@ "url": "https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png", "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)", "maxZoom": 18, - "minZoom": 0 + "minZoom": 0, + "urlParameters": { "variant": "mtb" } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { "variant": "mtb" }, "description": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)" }, "slopes": { @@ -385,10 +385,10 @@ "url": "https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png", "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)", "maxZoom": 18, - "minZoom": 0 + "minZoom": 0, + "urlParameters": { "variant": "slopes" } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { "variant": "slopes" }, "description": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)" }, "riding": { @@ -400,10 +400,10 @@ "url": "https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png", "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)", "maxZoom": 18, - "minZoom": 0 + "minZoom": 0, + "urlParameters": { "variant": "riding" } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { "variant": "riding" }, "description": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)" }, "skating": { @@ -415,10 +415,10 @@ "url": "https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png", "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)", "maxZoom": 18, - "minZoom": 0 + "minZoom": 0, + "urlParameters": { "variant": "skating" } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { "variant": "skating" }, "description": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)" } }, @@ -432,10 +432,10 @@ "url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles courtesy of the U.S. Geological Survey", "maxZoom": 20, - "minZoom": 0 + "minZoom": 0, + "urlParameters": {} }, "layerParameters": { "opacity": 1 }, - "urlParameters": {}, "description": "Tiles courtesy of the U.S. Geological Survey" }, "USImagery": { @@ -447,10 +447,10 @@ "url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles courtesy of the U.S. Geological Survey", "maxZoom": 20, - "minZoom": 0 + "minZoom": 0, + "urlParameters": {} }, "layerParameters": { "opacity": 1 }, - "urlParameters": {}, "description": "Tiles courtesy of the U.S. Geological Survey" }, "USImageryTopo": { @@ -462,10 +462,10 @@ "url": "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryTopo/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles courtesy of the U.S. Geological Survey", "maxZoom": 20, - "minZoom": 0 + "minZoom": 0, + "urlParameters": {} }, "layerParameters": { "opacity": 1 }, - "urlParameters": {}, "description": "Tiles courtesy of the U.S. Geological Survey" } }, @@ -479,10 +479,10 @@ "url": "http://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}", "attribution": "© Gaode.com", "maxZoom": 19, - "minZoom": 0 + "minZoom": 0, + "urlParameters": {} }, "layerParameters": { "opacity": 1 }, - "urlParameters": {}, "description": "© Gaode.com" }, "Satellite": { @@ -494,10 +494,10 @@ "url": "http://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}", "attribution": "© Gaode.com", "maxZoom": 19, - "minZoom": 0 + "minZoom": 0, + "urlParameters": {} }, "layerParameters": { "opacity": 1 }, - "urlParameters": {}, "description": "© Gaode.com" } }, @@ -511,10 +511,10 @@ "url": "https://heatmap-external-a.strava.com/tiles/all/hot/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15, - "minZoom": 0 + "minZoom": 0, + "urlParameters": {} }, "layerParameters": { "opacity": 1 }, - "urlParameters": {}, "description": "Map tiles by Strava 2021" }, "Ride": { @@ -526,10 +526,10 @@ "url": "https://heatmap-external-a.strava.com/tiles/ride/hot/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15, - "minZoom": 0 + "minZoom": 0, + "urlParameters": {} }, "layerParameters": { "opacity": 1 }, - "urlParameters": {}, "description": "Map tiles by Strava 2021" }, "Run": { @@ -541,10 +541,10 @@ "url": "https://heatmap-external-a.strava.com/tiles/run/bluered/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15, - "minZoom": 0 + "minZoom": 0, + "urlParameters": {} }, "layerParameters": { "opacity": 1 }, - "urlParameters": {}, "description": "Map tiles by Strava 2021" }, "Water": { @@ -556,10 +556,10 @@ "url": "https://heatmap-external-a.strava.com/tiles/water/blue/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15, - "minZoom": 0 + "minZoom": 0, + "urlParameters": {} }, "layerParameters": { "opacity": 1 }, - "urlParameters": {}, "description": "Map tiles by Strava 2021" }, "Winter": { @@ -571,10 +571,10 @@ "url": "https://heatmap-external-a.strava.com/tiles/winter/hot/{z}/{x}/{y}.png", "attribution": "Map tiles by Strava 2021", "maxZoom": 15, - "minZoom": 0 + "minZoom": 0, + "urlParameters": {} }, "layerParameters": { "opacity": 1 }, - "urlParameters": {}, "description": "Map tiles by Strava 2021" } }, @@ -587,10 +587,10 @@ "url": "https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png", "attribution": "Map memomaps.de CC-BY-SA, map data (C) OpenStreetMap contributors", "maxZoom": 18, - "minZoom": 0 + "minZoom": 0, + "urlParameters": "https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png" }, - "layerParameters": { "opacity": 1 }, - "urlParameters": "https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png" + "layerParameters": { "opacity": 1 } }, "OpenTopoMap": { "thumbnailPath": "layer_gallery/OpenTopoMap.png", @@ -601,10 +601,10 @@ "url": "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", "attribution": "Map data: (C) OpenStreetMap contributors, SRTM | Map style: (C) OpenTopoMap (CC-BY-SA)", "maxZoom": 17, - "minZoom": 0 + "minZoom": 0, + "urlParameters": "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png" }, - "layerParameters": { "opacity": 1 }, - "urlParameters": "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png" + "layerParameters": { "opacity": 1 } }, "OpenRailwayMap": { "thumbnailPath": "layer_gallery/OpenRailwayMap.png", @@ -615,10 +615,10 @@ "url": "https://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png", "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) OpenRailwayMap (CC-BY-SA)", "maxZoom": 19, - "minZoom": 0 + "minZoom": 0, + "urlParameters": "https://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png" }, - "layerParameters": { "opacity": 1 }, - "urlParameters": "https://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png" + "layerParameters": { "opacity": 1 } }, "Esri": { "WorldStreetMap": { @@ -630,10 +630,10 @@ "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012", "maxZoom": null, - "minZoom": 0 + "minZoom": 0, + "urlParameters": { "variant": "World_Street_Map" } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { "variant": "World_Street_Map" }, "description": "Tiles (C) Esri -- Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012" }, "WorldTopoMap": { @@ -645,10 +645,10 @@ "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community", "maxZoom": null, - "minZoom": 0 + "minZoom": 0, + "urlParameters": { "variant": "World_Topo_Map" } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { "variant": "World_Topo_Map" }, "description": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community" }, "WorldImagery": { @@ -660,10 +660,10 @@ "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community", "maxZoom": null, - "minZoom": 0 + "minZoom": 0, + "urlParameters": { "variant": "World_Imagery" } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { "variant": "World_Imagery" }, "description": "Tiles (C) Esri -- Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community" }, "WorldTerrain": { @@ -675,10 +675,10 @@ "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: USGS, Esri, TANA, DeLorme, and NPS", "maxZoom": 13, - "minZoom": 0 + "minZoom": 0, + "urlParameters": { "variant": "World_Terrain_Base" } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { "variant": "World_Terrain_Base" }, "description": "Tiles (C) Esri -- Source: USGS, Esri, TANA, DeLorme, and NPS" }, "WorldShadedRelief": { @@ -690,10 +690,10 @@ "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: Esri", "maxZoom": 13, - "minZoom": 0 + "minZoom": 0, + "urlParameters": { "variant": "World_Shaded_Relief" } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { "variant": "World_Shaded_Relief" }, "description": "Tiles (C) Esri -- Source: Esri" }, "WorldPhysical": { @@ -705,10 +705,10 @@ "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Source: US National Park Service", "maxZoom": 8, - "minZoom": 0 + "minZoom": 0, + "urlParameters": { "variant": "World_Physical_Map" } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { "variant": "World_Physical_Map" }, "description": "Tiles (C) Esri -- Source: US National Park Service" }, "OceanBasemap": { @@ -720,10 +720,10 @@ "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri", "maxZoom": 13, - "minZoom": 0 + "minZoom": 0, + "urlParameters": { "variant": "Ocean/World_Ocean_Base" } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { "variant": "Ocean/World_Ocean_Base" }, "description": "Tiles (C) Esri -- Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri" }, "NatGeoWorldMap": { @@ -735,10 +735,10 @@ "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC", "maxZoom": 16, - "minZoom": 0 + "minZoom": 0, + "urlParameters": { "variant": "NatGeo_World_Map" } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { "variant": "NatGeo_World_Map" }, "description": "Tiles (C) Esri -- National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC" }, "WorldGrayCanvas": { @@ -750,10 +750,10 @@ "url": "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}", "attribution": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ", "maxZoom": 16, - "minZoom": 0 + "minZoom": 0, + "urlParameters": { "variant": "Canvas/World_Light_Gray_Base" } }, "layerParameters": { "opacity": 1 }, - "urlParameters": { "variant": "Canvas/World_Light_Gray_Base" }, "description": "Tiles (C) Esri -- Esri, DeLorme, NAVTEQ" } }, @@ -767,10 +767,10 @@ "url": "https://tiles.macrostrat.org/carto/{z}/{x}/{y}.png", "attribution": "\u00a9 Geologic data \u00a9 Macrostrat raster layer (CC\u2011BY 4.0)", "maxZoom": 18, - "minZoom": 0 + "minZoom": 0, + "urlParameters": {} }, "layerParameters": { "opacity": 1 }, - "urlParameters": {}, "description": "\u00a9 Geologic data \u00a9 Macrostrat raster layer (CC\u2011BY 4.0)" }, "CartoVector": { @@ -782,10 +782,10 @@ "url": "https://tiles.macrostrat.org/carto/{z}/{x}/{y}.mvt", "attribution": "\u00a9 Geologic data \u00a9 Macrostrat vector layer (CC\u2011BY 4.0)", "maxZoom": 18, - "minZoom": 0 + "minZoom": 0, + "urlParameters": {} }, "layerParameters": { "opacity": 1 }, - "urlParameters": {}, "description": "\u00a9 Geologic data \u00a9 Macrostrat vector layer (CC\u2011BY 4.0)" } } diff --git a/packages/base/layer_gallery_generator.py b/packages/base/layer_gallery_generator.py index 7f7bff7af..a43c80848 100644 --- a/packages/base/layer_gallery_generator.py +++ b/packages/base/layer_gallery_generator.py @@ -219,11 +219,12 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters "url": url_template, "attribution": xyzprovider.get("attribution"), "maxZoom": xyzprovider.get("max_zoom"), - "minZoom": xyzprovider.get("min_zoom") or 0 + "minZoom": xyzprovider.get("min_zoom") or 0, + "urlParameters": dict_keys_to_camel(url_template) , }, "layerParameters": {"opacity": 1}, - "urlParameters": dict_keys_to_camel(url_template) + } elif config_is_flat and not xyz_is_flat: @@ -254,10 +255,10 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters "url": url_template, "attribution": tile_provider.get("attribution"), "maxZoom": tile_provider.get("max_zoom"), - "minZoom": tile_provider.get("min_zoom") or 0 + "minZoom": tile_provider.get("min_zoom") or 0, + "urlParameters": dict_keys_to_camel(url_parameters), }, "layerParameters": {"opacity": 1}, - "urlParameters": dict_keys_to_camel(url_parameters), "description": tile_provider.get("attribution") } @@ -291,10 +292,10 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters "url": url_template, "attribution": tile_provider.get("attribution"), "maxZoom": tile_provider.get("max_zoom"), - "minZoom": tile_provider.get("min_zoom") or 0 + "minZoom": tile_provider.get("min_zoom") or 0, + "urlParameters": dict_keys_to_camel(url_parameters), }, "layerParameters": {"opacity": 1}, - "urlParameters": dict_keys_to_camel(url_parameters), "description": tile_provider.get("attribution") } diff --git a/packages/base/src/tools.ts b/packages/base/src/tools.ts index aa03f8c58..cf19f4fde 100644 --- a/packages/base/src/tools.ts +++ b/packages/base/src/tools.ts @@ -205,12 +205,6 @@ export function createDefaultLayerRegistry( layerProvider: { [x: string]: any }, provider?: string | undefined, ): ILayerGalleryEntry { - if (!layerProvider.layerType || !layerProvider.sourceType) { - throw new Error( - `Invalid layer_gallery entry "${entry}": missing layerType or sourceType`, - ); - } - return { name: entry, thumbnail: LAYER_THUMBNAILS[layerProvider['name'].replace('.', '-')], @@ -219,7 +213,6 @@ export function createDefaultLayerRegistry( sourceParameters: layerProvider['sourceParameters'], layerParameters: layerProvider['layerParameters'], provider: provider ?? entry.split('.', 1)[0], - urlParameters: layerProvider['urlParameters'], description: layerProvider['description'], }; } diff --git a/packages/schema/src/interfaces.ts b/packages/schema/src/interfaces.ts index 5625c0000..d33f81b62 100644 --- a/packages/schema/src/interfaces.ts +++ b/packages/schema/src/interfaces.ts @@ -410,7 +410,6 @@ export type ILayerGalleryEntry = { | IVectorTileSource | IVideoSource; provider: string; - urlParameters: IDict; description: string; }; From f020e1fadcd7c0da5940c5e4f19a0ff713ee627b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 3 Feb 2026 13:51:15 +0000 Subject: [PATCH 8/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- packages/base/layer_gallery_generator.py | 101 ++++++++++++----------- 1 file changed, 53 insertions(+), 48 deletions(-) diff --git a/packages/base/layer_gallery_generator.py b/packages/base/layer_gallery_generator.py index a43c80848..2aaef235d 100644 --- a/packages/base/layer_gallery_generator.py +++ b/packages/base/layer_gallery_generator.py @@ -16,6 +16,7 @@ THUMBNAILS_LOCATION = "layer_gallery" + def snake_to_camel(s): """ Convect snake case strings into camel case ones @@ -23,26 +24,26 @@ def snake_to_camel(s): parts = s.split("_") return parts[0] + "".join(word.capitalize() for word in parts[1:]) + def dict_keys_to_camel(obj): """ Convect keys of a dict from snake case to camel case """ if isinstance(obj, dict): - return { - snake_to_camel(k): dict_keys_to_camel(v) - for k, v in obj.items() - } + return {snake_to_camel(k): dict_keys_to_camel(v) for k, v in obj.items()} elif isinstance(obj, list): return [dict_keys_to_camel(item) for item in obj] else: return obj + def placeholder_tile(size): """ Provide a white empty placeholder image """ return Image.new("RGB", size, (220, 220, 220)) + def extract_placeholders(url_template): """ Extract the placeholders from an url_template @@ -54,6 +55,7 @@ def extract_placeholders(url_template): if field_name } + def build_url_parameters(tile_provider): """ Build the url parameters that are needed in fetch_tile, create_thumbnail and download_thumbnail @@ -69,14 +71,15 @@ def build_url_parameters(tile_provider): for name in placeholders - reserved: if name in tile_provider and tile_provider[name] is not None: kwargs[name] = tile_provider[name] - if (name == "time" and tile_provider['time']==''): - kwargs['time']= yesterday + if name == "time" and tile_provider["time"] == "": + kwargs["time"] = yesterday else: raise KeyError( f"Placeholder '{name}' not found in TileProvider '{tile_provider.get('name')}'" ) return kwargs + def fetch_tile(url_template, x, y, z, s="a", **kwargs): """ Fetch a tile from the given URL template. @@ -94,13 +97,15 @@ def fetch_tile(url_template, x, y, z, s="a", **kwargs): print(f"⚠️ Tile fetch failed: {e}") return None + def latlng_to_tile(lat, lng, zoom): """ Convert latitude/longitude to tile coordinates. """ tile = mercantile.tile(lng, lat, zoom, True) return tile.x, tile.y - + + def create_thumbnail( url_template, lat, @@ -145,12 +150,12 @@ def create_thumbnail( yesterday = (date.today() - timedelta(days=1)).strftime("%Y-%m-%d") # San Francisco -#san_francisco = {"lat": 37.7749, "lng": -122.4194, "zoom": 5} +# san_francisco = {"lat": 37.7749, "lng": -122.4194, "zoom": 5} ##middle_europe = {"lat": 48.63290858589535, "lng": -350.068359375, "zoom": 4} # Default -#france = {"lat": 47.040182144806664, "lng": 1.2963867187500002, "zoom": 5} +# france = {"lat": 47.040182144806664, "lng": 1.2963867187500002, "zoom": 5} def download_thumbnail(url_template, name, position, tile_size, **url_parameters): @@ -169,10 +174,11 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters thumbnail.save(file_path) return file_path + # Create thumbnail dir if needed if not os.path.exists(THUMBNAILS_LOCATION): os.makedirs(THUMBNAILS_LOCATION) - + # This is the JSON we'll generate for the gallery provider_gallery = {} @@ -190,50 +196,52 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters url="https://tiles.macrostrat.org/carto/{z}/{x}/{y}.mvt", attribution="© Geologic data © Macrostrat vector layer (CC‑BY 4.0)", max_zoom=18, - ) + ), } # Fetch thumbnails and populate the dictionary for provider_key, provider_value in provider_config.items(): xyzprovider = custom_providers[provider_key] config_is_flat = "layerType" in provider_value - xyz_is_flat = "url" in xyzprovider - + xyz_is_flat = "url" in xyzprovider + if config_is_flat and xyz_is_flat: tile_provider = xyzprovider - + url_template = tile_provider["url"] url_parameters = build_url_parameters(tile_provider) thumbnail_config = provider_value["thumbnail"] - position = thumbnail_config["Special Rules"].get(provider_key, thumbnail_config["Default"]) + position = thumbnail_config["Special Rules"].get( + provider_key, thumbnail_config["Default"] + ) tile_size = thumbnail_config.get("TileSize", 256) - file_path = download_thumbnail(url_template, provider_key, position, tile_size, **url_parameters) - + file_path = download_thumbnail( + url_template, provider_key, position, tile_size, **url_parameters + ) + provider_gallery[provider_key] = { - "thumbnailPath": file_path, - "name": provider_key, - "layerType": provider_value["layerType"], - "sourceType": provider_value["sourceType"], - "sourceParameters": { - "url": url_template, - "attribution": xyzprovider.get("attribution"), - "maxZoom": xyzprovider.get("max_zoom"), - "minZoom": xyzprovider.get("min_zoom") or 0, - "urlParameters": dict_keys_to_camel(url_template) -, - }, - "layerParameters": {"opacity": 1}, - - } - + "thumbnailPath": file_path, + "name": provider_key, + "layerType": provider_value["layerType"], + "sourceType": provider_value["sourceType"], + "sourceParameters": { + "url": url_template, + "attribution": xyzprovider.get("attribution"), + "maxZoom": xyzprovider.get("max_zoom"), + "minZoom": xyzprovider.get("min_zoom") or 0, + "urlParameters": dict_keys_to_camel(url_template), + }, + "layerParameters": {"opacity": 1}, + } + elif config_is_flat and not xyz_is_flat: providers_maps = {} for map_name, tile_provider in xyzprovider.items(): url_template = tile_provider["url"] url_parameters = build_url_parameters(tile_provider) - + thumbnail_config = provider_value["thumbnail"] position = thumbnail_config["Special Rules"].get( map_name, thumbnail_config["Default"] @@ -245,10 +253,10 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters file_path = download_thumbnail( url_template, name, position, tile_size, **url_parameters ) - + providers_maps[map_name] = { "thumbnailPath": file_path, - "name": provider_key +"."+ map_name, + "name": provider_key + "." + map_name, "layerType": provider_value["layerType"], "sourceType": provider_value["sourceType"], "sourceParameters": { @@ -259,11 +267,11 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters "urlParameters": dict_keys_to_camel(url_parameters), }, "layerParameters": {"opacity": 1}, - "description": tile_provider.get("attribution") + "description": tile_provider.get("attribution"), } - provider_gallery[provider_key] = providers_maps - + provider_gallery[provider_key] = providers_maps + elif not config_is_flat and not xyz_is_flat: providers_maps = {} for map_name, map_config in provider_value.items(): @@ -282,10 +290,10 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters file_path = download_thumbnail( url_template, name, position, tile_size, **url_parameters ) - + providers_maps[map_name] = { "thumbnailPath": file_path, - "name": provider_key +"."+ map_name, + "name": provider_key + "." + map_name, "layerType": map_config["layerType"], "sourceType": map_config["sourceType"], "sourceParameters": { @@ -296,15 +304,13 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters "urlParameters": dict_keys_to_camel(url_parameters), }, "layerParameters": {"opacity": 1}, - "description": tile_provider.get("attribution") + "description": tile_provider.get("attribution"), } provider_gallery[provider_key] = providers_maps - + else: - raise ValueError( - f"Inconsistent config for provider '{provider_key}'" - ) + raise ValueError(f"Inconsistent config for provider '{provider_key}'") """ # compress each images of THUMBNAILS_LOCATION @@ -328,5 +334,4 @@ def download_thumbnail(url_template, name, position, tile_size, **url_parameters """ with open(f"layer_gallery.json", "w") as f: - json.dump(provider_gallery, f) - + json.dump(provider_gallery, f)