Update: remove stamen entries, replace them with stadiamaps alternatives; remove CartoDB (as it has PNG tiles), see #1652

This commit is contained in:
Pieter Vander Vennet 2023-10-11 17:57:16 +02:00
parent 8f5ba2153a
commit 89afe9102f
3 changed files with 325 additions and 382 deletions

View file

@ -32,7 +32,7 @@
51.190748429411705 51.190748429411705
] ]
], ],
"defaultBackgroundId": "CartoDB.DarkMatterNoLabels", "defaultBackgroundId": "alidade.smooth_dark",
"layers": [ "layers": [
{ {
"id": "street_with_width", "id": "street_with_width",
@ -271,4 +271,4 @@
] ]
} }
] ]
} }

View file

@ -1,174 +1,122 @@
import { Feature, Polygon } from "geojson"; import { Feature, Polygon } from "geojson"
import * as editorlayerindex from "../assets/editor-layer-index.json"; import * as editorlayerindex from "../assets/editor-layer-index.json"
import * as globallayers from "../assets/global-raster-layers.json"; import * as globallayers from "../assets/global-raster-layers.json"
import { BBox } from "../Logic/BBox"; import { BBox } from "../Logic/BBox"
import { Store, Stores } from "../Logic/UIEventSource"; import { Store, Stores } from "../Logic/UIEventSource"
import { GeoOperations } from "../Logic/GeoOperations"; import { GeoOperations } from "../Logic/GeoOperations"
import { RasterLayerProperties } from "./RasterLayerProperties"; import { RasterLayerProperties } from "./RasterLayerProperties"
export class AvailableRasterLayers { export class AvailableRasterLayers {
public static EditorLayerIndex: (Feature<Polygon, EditorLayerIndexProperties> & public static EditorLayerIndex: (Feature<Polygon, EditorLayerIndexProperties> &
RasterLayerPolygon)[] = <any>editorlayerindex.features; RasterLayerPolygon)[] = <any>editorlayerindex.features
public static globalLayers: RasterLayerPolygon[] = globallayers.layers.map( public static globalLayers: RasterLayerPolygon[] = globallayers.layers.map(
(properties) => (properties) =>
<RasterLayerPolygon>{ <RasterLayerPolygon>{
type: "Feature",
properties,
geometry: BBox.global.asGeometry(),
}
)
public static readonly osmCartoProperties: RasterLayerProperties = {
id: "osm",
name: "OpenStreetMap",
url: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
attribution: {
text: "OpenStreetMap",
url: "https://openStreetMap.org/copyright",
},
best: true,
max_zoom: 19,
min_zoom: 0,
category: "osmbasedmap",
}
public static readonly osmCarto: RasterLayerPolygon = {
type: "Feature", type: "Feature",
properties, properties: AvailableRasterLayers.osmCartoProperties,
geometry: BBox.global.asGeometry() geometry: BBox.global.asGeometry(),
} }
);
public static readonly osmCartoProperties: RasterLayerProperties = {
id: "osm",
name: "OpenStreetMap",
url: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
attribution: {
text: "OpenStreetMap",
url: "https://openStreetMap.org/copyright"
},
best: true,
max_zoom: 19,
min_zoom: 0,
category: "osmbasedmap"
};
public static readonly osmCarto: RasterLayerPolygon = { public static readonly maptilerDefaultLayer: RasterLayerPolygon = {
type: "Feature", type: "Feature",
properties: AvailableRasterLayers.osmCartoProperties, properties: {
geometry: BBox.global.asGeometry() name: "MapTiler",
}; url: "https://api.maptiler.com/maps/15cc8f61-0353-4be6-b8da-13daea5f7432/style.json?key=GvoVAJgu46I5rZapJuAy",
category: "osmbasedmap",
id: "maptiler",
type: "vector",
attribution: {
text: "Maptiler",
url: "https://www.maptiler.com/copyright/",
},
},
geometry: BBox.global.asGeometry(),
}
public static readonly maptilerDefaultLayer: RasterLayerPolygon = { public static readonly vectorLayers = [
type: "Feature", AvailableRasterLayers.maptilerDefaultLayer,
properties: { AvailableRasterLayers.osmCarto,
name: "MapTiler", ]
url: "https://api.maptiler.com/maps/15cc8f61-0353-4be6-b8da-13daea5f7432/style.json?key=GvoVAJgu46I5rZapJuAy",
category: "osmbasedmap",
id: "maptiler",
type: "vector",
attribution: {
text: "Maptiler",
url: "https://www.maptiler.com/copyright/"
}
},
geometry: BBox.global.asGeometry()
};
public static readonly maptilerCarto: RasterLayerPolygon = { public static layersAvailableAt(
type: "Feature", location: Store<{ lon: number; lat: number }>
properties: { ): Store<RasterLayerPolygon[]> {
name: "MapTiler Carto", const availableLayersBboxes = Stores.ListStabilized(
url: "https://api.maptiler.com/maps/openstreetmap/style.json?key=GvoVAJgu46I5rZapJuAy", location.mapD((loc) => {
category: "osmbasedmap", const lonlat: [number, number] = [loc.lon, loc.lat]
id: "maptiler.carto", return AvailableRasterLayers.EditorLayerIndex.filter((eliPolygon) =>
type: "vector", BBox.get(eliPolygon).contains(lonlat)
attribution: { )
text: "Maptiler", })
url: "https://www.maptiler.com/copyright/" )
} return Stores.ListStabilized(
}, availableLayersBboxes.map((eliPolygons) => {
geometry: BBox.global.asGeometry() const loc = location.data
}; const lonlat: [number, number] = [loc.lon, loc.lat]
const matching: RasterLayerPolygon[] = eliPolygons.filter((eliPolygon) => {
public static readonly maptilerBackdrop: RasterLayerPolygon = { if (eliPolygon.geometry === null) {
type: "Feature", return true // global ELI-layer
properties: { }
name: "MapTiler Backdrop", return GeoOperations.inside(lonlat, eliPolygon)
url: "https://api.maptiler.com/maps/backdrop/style.json?key=GvoVAJgu46I5rZapJuAy", })
category: "osmbasedmap", matching.push(...AvailableRasterLayers.globalLayers)
id: "maptiler.backdrop", return matching
type: "vector", })
attribution: { )
text: "Maptiler", }
url: "https://www.maptiler.com/copyright/"
}
},
geometry: BBox.global.asGeometry()
};
public static readonly americana: RasterLayerPolygon = {
type: "Feature",
properties: {
name: "Americana",
url: "https://zelonewolf.github.io/openstreetmap-americana/style.json",
category: "osmbasedmap",
id: "americana",
type: "vector",
attribution: {
text: "Americana",
url: "https://github.com/ZeLonewolf/openstreetmap-americana/"
}
},
geometry: BBox.global.asGeometry()
};
public static readonly vectorLayers = [
AvailableRasterLayers.maptilerDefaultLayer,
AvailableRasterLayers.osmCarto,
AvailableRasterLayers.maptilerCarto,
AvailableRasterLayers.maptilerBackdrop,
AvailableRasterLayers.americana
];
public static layersAvailableAt(
location: Store<{ lon: number; lat: number }>
): Store<RasterLayerPolygon[]> {
const availableLayersBboxes = Stores.ListStabilized(
location.mapD((loc) => {
const lonlat: [number, number] = [loc.lon, loc.lat];
return AvailableRasterLayers.EditorLayerIndex.filter((eliPolygon) =>
BBox.get(eliPolygon).contains(lonlat)
);
})
);
const available = Stores.ListStabilized(
availableLayersBboxes.map((eliPolygons) => {
const loc = location.data;
const lonlat: [number, number] = [loc.lon, loc.lat];
const matching: RasterLayerPolygon[] = eliPolygons.filter((eliPolygon) => {
if (eliPolygon.geometry === null) {
return true; // global ELI-layer
}
return GeoOperations.inside(lonlat, eliPolygon);
});
matching.push(...AvailableRasterLayers.globalLayers);
matching.unshift(...AvailableRasterLayers.vectorLayers);
return matching;
})
);
return available;
}
} }
export class RasterLayerUtils { export class RasterLayerUtils {
/** /**
* Selects, from the given list of available rasterLayerPolygons, a rasterLayer. * Selects, from the given list of available rasterLayerPolygons, a rasterLayer.
* This rasterlayer will be of type 'preferredCategory' and will be of the 'best'-layer (if available). * This rasterlayer will be of type 'preferredCategory' and will be of the 'best'-layer (if available).
* Returns 'undefined' if no such layer is available * Returns 'undefined' if no such layer is available
* @param available * @param available
* @param preferredCategory * @param preferredCategory
* @param ignoreLayer * @param ignoreLayer
*/ */
public static SelectBestLayerAccordingTo( public static SelectBestLayerAccordingTo(
available: RasterLayerPolygon[], available: RasterLayerPolygon[],
preferredCategory: string, preferredCategory: string,
ignoreLayer?: RasterLayerPolygon ignoreLayer?: RasterLayerPolygon
): RasterLayerPolygon { ): RasterLayerPolygon {
let secondBest: RasterLayerPolygon = undefined; let secondBest: RasterLayerPolygon = undefined
for (const rasterLayer of available) { for (const rasterLayer of available) {
if (rasterLayer === ignoreLayer) { if (rasterLayer === ignoreLayer) {
continue; continue
} }
const p = rasterLayer.properties; const p = rasterLayer.properties
if (p.category === preferredCategory) { if (p.category === preferredCategory) {
if (p.best) { if (p.best) {
return rasterLayer; return rasterLayer
}
if (!secondBest) {
secondBest = rasterLayer
}
}
} }
if (!secondBest) { return secondBest
secondBest = rasterLayer;
}
}
} }
return secondBest;
}
} }
export type RasterLayerPolygon = Feature<Polygon, RasterLayerProperties> export type RasterLayerPolygon = Feature<Polygon, RasterLayerProperties>
@ -180,165 +128,165 @@ export type RasterLayerPolygon = Feature<Polygon, RasterLayerProperties>
* which was then converted with http://borischerny.com/json-schema-to-typescript-browser/ * which was then converted with http://borischerny.com/json-schema-to-typescript-browser/
*/ */
export interface EditorLayerIndexProperties extends RasterLayerProperties { export interface EditorLayerIndexProperties extends RasterLayerProperties {
/**
* The name of the imagery source
*/
readonly name: string;
/**
* Whether the imagery name should be translated
*/
readonly i18n?: boolean;
readonly type:
| "tms"
| "wms"
| "bing"
| "scanex"
| "wms_endpoint"
| "wmts"
| "vector"; /* Vector is not actually part of the ELI-spec, we add it for vector layers */
/**
* A rough categorisation of different types of layers. See https://github.com/osmlab/editor-layer-index/blob/gh-pages/CONTRIBUTING.md#categories for a description of the individual categories.
*/
readonly category?:
| "photo"
| "map"
| "historicmap"
| "osmbasedmap"
| "historicphoto"
| "qa"
| "elevation"
| "other";
/**
* A URL template for imagery tiles
*/
readonly url: string;
readonly min_zoom?: number;
readonly max_zoom?: number;
/**
* explicit/implicit permission by the owner for use in OSM
*/
readonly permission_osm?: "explicit" | "implicit" | "no";
/**
* A URL for the license or permissions for the imagery
*/
readonly license_url?: string;
/**
* A URL for the privacy policy of the operator or false if there is no existing privacy policy for tis imagery.
*/
readonly privacy_policy_url?: string | boolean;
/**
* A unique identifier for the source; used in imagery_used changeset tag
*/
readonly id: string;
/**
* A short English-language description of the source
*/
readonly description?: string;
/**
* The ISO 3166-1 alpha-2 two letter country code in upper case. Use ZZ for unknown or multiple.
*/
readonly country_code?: string;
/**
* Whether this imagery should be shown in the default world-wide menu
*/
readonly default?: boolean;
/**
* Whether this imagery is the best source for the region
*/
readonly best?: boolean;
/**
* The age of the oldest imagery or data in the source, as an RFC3339 date or leading portion of one
*/
readonly start_date?: string;
/**
* The age of the newest imagery or data in the source, as an RFC3339 date or leading portion of one
*/
readonly end_date?: string;
/**
* HTTP header to check for information if the tile is invalid
*/
readonly no_tile_header?: {
/** /**
* This interface was referenced by `undefined`'s JSON-Schema definition * The name of the imagery source
* via the `patternProperty` "^.*$".
*/ */
[k: string]: string[] | null readonly name: string
}; /**
/** * Whether the imagery name should be translated
* 'true' if tiles are transparent and can be overlaid on another source */
*/ readonly i18n?: boolean
readonly overlay?: boolean & string; readonly type:
readonly available_projections?: string[]; | "tms"
readonly attribution?: { | "wms"
readonly url?: string | "bing"
readonly text?: string | "scanex"
readonly html?: string | "wms_endpoint"
readonly required?: boolean | "wmts"
}; | "vector" /* Vector is not actually part of the ELI-spec, we add it for vector layers */
/** /**
* A URL for an image, that can be displayed in the list of imagery layers next to the name * A rough categorisation of different types of layers. See https://github.com/osmlab/editor-layer-index/blob/gh-pages/CONTRIBUTING.md#categories for a description of the individual categories.
*/ */
readonly icon?: string; readonly category?:
/** | "photo"
* A link to an EULA text that has to be accepted by the user, before the imagery source is added. Can contain {lang} to be replaced by a current user language wiki code (like FR:) or an empty string for the default English text. | "map"
*/ | "historicmap"
readonly eula?: string; | "osmbasedmap"
/** | "historicphoto"
* A URL for an image, that is displayed in the mapview for attribution | "qa"
*/ | "elevation"
readonly "logo-image"?: string; | "other"
/** /**
* Customized text for the terms of use link (default is "Background Terms of Use") * A URL template for imagery tiles
*/ */
readonly "terms-of-use-text"?: string; readonly url: string
/** readonly min_zoom?: number
* Specify a checksum for tiles, which aren't real tiles. `type` is the digest type and can be MD5, SHA-1, SHA-256, SHA-384 and SHA-512, value is the hex encoded checksum in lower case. To create a checksum save the tile as file and upload it to e.g. https://defuse.ca/checksums.htm. readonly max_zoom?: number
*/ /**
readonly "no-tile-checksum"?: string; * explicit/implicit permission by the owner for use in OSM
/** */
* header-name attribute specifies a header returned by tile server, that will be shown as `metadata-key` attribute in Show Tile Info dialog readonly permission_osm?: "explicit" | "implicit" | "no"
*/ /**
readonly "metadata-header"?: string; * A URL for the license or permissions for the imagery
/** */
* Set to `true` if imagery source is properly aligned and does not need imagery offset adjustments. This is used for OSM based sources too. readonly license_url?: string
*/ /**
readonly "valid-georeference"?: boolean; * A URL for the privacy policy of the operator or false if there is no existing privacy policy for tis imagery.
/** */
* Size of individual tiles delivered by a TMS service readonly privacy_policy_url?: string | boolean
*/ /**
readonly "tile-size"?: number; * A unique identifier for the source; used in imagery_used changeset tag
/** */
* Whether tiles status can be accessed by appending /status to the tile URL and can be submitted for re-rendering by appending /dirty. readonly id: string
*/ /**
readonly "mod-tile-features"?: string; * A short English-language description of the source
/** */
* HTTP headers to be sent to server. It has two attributes header-name and header-value. May be specified multiple times. readonly description?: string
*/ /**
readonly "custom-http-headers"?: { * The ISO 3166-1 alpha-2 two letter country code in upper case. Use ZZ for unknown or multiple.
readonly "header-name"?: string */
readonly "header-value"?: string readonly country_code?: string
}; /**
/** * Whether this imagery should be shown in the default world-wide menu
* Default layer to open (when using WMS_ENDPOINT type). Contains list of layer tag with two attributes - name and style, e.g. `"default-layers": ["layer": { name="Basisdata_NP_Basiskart_JanMayen_WMTS_25829" "style":"default" } ]` (not allowed in `mirror` attribute) */
*/ readonly default?: boolean
readonly "default-layers"?: { /**
layer?: { * Whether this imagery is the best source for the region
"layer-name"?: string */
"layer-style"?: string readonly best?: boolean
[k: string]: unknown /**
* The age of the oldest imagery or data in the source, as an RFC3339 date or leading portion of one
*/
readonly start_date?: string
/**
* The age of the newest imagery or data in the source, as an RFC3339 date or leading portion of one
*/
readonly end_date?: string
/**
* HTTP header to check for information if the tile is invalid
*/
readonly no_tile_header?: {
/**
* This interface was referenced by `undefined`'s JSON-Schema definition
* via the `patternProperty` "^.*$".
*/
[k: string]: string[] | null
} }
[k: string]: unknown /**
}[]; * 'true' if tiles are transparent and can be overlaid on another source
/** */
* format to use when connecting tile server (when using WMS_ENDPOINT type) readonly overlay?: boolean & string
*/ readonly available_projections?: string[]
readonly format?: string; readonly attribution?: {
/** readonly url?: string
* If `true` transparent tiles will be requested from WMS server readonly text?: string
*/ readonly html?: string
readonly transparent?: boolean & string; readonly required?: boolean
/** }
* minimum expiry time for tiles in seconds. The larger the value, the longer entry in cache will be considered valid /**
*/ * A URL for an image, that can be displayed in the list of imagery layers next to the name
readonly "minimum-tile-expire"?: number; */
readonly icon?: string
/**
* A link to an EULA text that has to be accepted by the user, before the imagery source is added. Can contain {lang} to be replaced by a current user language wiki code (like FR:) or an empty string for the default English text.
*/
readonly eula?: string
/**
* A URL for an image, that is displayed in the mapview for attribution
*/
readonly "logo-image"?: string
/**
* Customized text for the terms of use link (default is "Background Terms of Use")
*/
readonly "terms-of-use-text"?: string
/**
* Specify a checksum for tiles, which aren't real tiles. `type` is the digest type and can be MD5, SHA-1, SHA-256, SHA-384 and SHA-512, value is the hex encoded checksum in lower case. To create a checksum save the tile as file and upload it to e.g. https://defuse.ca/checksums.htm.
*/
readonly "no-tile-checksum"?: string
/**
* header-name attribute specifies a header returned by tile server, that will be shown as `metadata-key` attribute in Show Tile Info dialog
*/
readonly "metadata-header"?: string
/**
* Set to `true` if imagery source is properly aligned and does not need imagery offset adjustments. This is used for OSM based sources too.
*/
readonly "valid-georeference"?: boolean
/**
* Size of individual tiles delivered by a TMS service
*/
readonly "tile-size"?: number
/**
* Whether tiles status can be accessed by appending /status to the tile URL and can be submitted for re-rendering by appending /dirty.
*/
readonly "mod-tile-features"?: string
/**
* HTTP headers to be sent to server. It has two attributes header-name and header-value. May be specified multiple times.
*/
readonly "custom-http-headers"?: {
readonly "header-name"?: string
readonly "header-value"?: string
}
/**
* Default layer to open (when using WMS_ENDPOINT type). Contains list of layer tag with two attributes - name and style, e.g. `"default-layers": ["layer": { name="Basisdata_NP_Basiskart_JanMayen_WMTS_25829" "style":"default" } ]` (not allowed in `mirror` attribute)
*/
readonly "default-layers"?: {
layer?: {
"layer-name"?: string
"layer-style"?: string
[k: string]: unknown
}
[k: string]: unknown
}[]
/**
* format to use when connecting tile server (when using WMS_ENDPOINT type)
*/
readonly format?: string
/**
* If `true` transparent tiles will be requested from WMS server
*/
readonly transparent?: boolean & string
/**
* minimum expiry time for tiles in seconds. The larger the value, the longer entry in cache will be considered valid
*/
readonly "minimum-tile-expire"?: number
} }

View file

@ -1,97 +1,92 @@
{ {
"layers": [ "layers": [
{ {
"id": "Stamen.TonerLite", "name": "Americana",
"name": "Toner Lite (by Stamen)", "url": "https://zelonewolf.github.io/openstreetmap-americana/style.json",
"url": "https://stamen-tiles-{switch:a,b,c,d}.a.ssl.fastly.net/toner-lite/{z}/{x}/{y}.png",
"category": "osmbasedmap", "category": "osmbasedmap",
"id": "americana",
"type": "vector",
"attribution": { "attribution": {
"html": "Map tiles by <a href=\"http://stamen.com\">Stamen Design</a>, <a href=\"http://creativecommons.org/licenses/by/3.0\">CC BY 3.0</a> &mdash; Map data {attribution.OpenStreetMap}" "text": "Americana",
}, "url": "https://github.com/ZeLonewolf/openstreetmap-americana/"
"min_zoom": 0, }
"max_zoom": 20
}, },
{ {
"id": "Stamen.TonerBackground", "name": "MapTiler Backdrop",
"name": "Toner Background - no labels (by Stamen)", "url": "https://api.maptiler.com/maps/backdrop/style.json?key=GvoVAJgu46I5rZapJuAy",
"category": "osmbasedmap", "category": "osmbasedmap",
"url": "https://stamen-tiles-{switch:a,b,c,d}.a.ssl.fastly.net/toner-background/{z}/{x}/{y}.png", "id": "maptiler.backdrop",
"type": "vector",
"attribution": { "attribution": {
"html": "Map tiles by <a href=\"http://stamen.com\">Stamen Design</a>, <a href=\"http://creativecommons.org/licenses/by/3.0\">CC BY 3.0</a> &mdash; Map data {attribution.OpenStreetMap}" "text": "Maptiler",
}, "url": "https://www.maptiler.com/copyright/"
"min_zoom": 0, }
"max_zoom": 20
}, },
{ {
"id": "Stamen.Watercolor", "name": "MapTiler Carto",
"name": "Watercolor (by Stamen)", "url": "https://api.maptiler.com/maps/openstreetmap/style.json?key=GvoVAJgu46I5rZapJuAy",
"category": "osmbasedmap", "category": "osmbasedmap",
"url": "https://stamen-tiles-{switch:a,b,c,d}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.png", "id": "maptiler.carto",
"type": "vector",
"attribution": { "attribution": {
"html": "Map tiles by <a href=\"http://stamen.com\">Stamen Design</a>, <a href=\"http://creativecommons.org/licenses/by/3.0\">CC BY 3.0</a> &mdash; Map data {attribution.OpenStreetMap}" "text": "Maptiler",
}, "url": "https://www.maptiler.com/copyright/"
"min_zoom": 0, }
"max_zoom": 20
}, },
{ {
"id": "CartoDB.Positron", "name": "Alidade Smooth",
"name": "Positron (by CartoDB)", "url": "https://tiles-eu.stadiamaps.com/styles/alidade_smooth.json?key=14c5a900-7137-42f7-9cb9-fff0f4696f75",
"url": "https://{switch:a,b,c,d}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}@2x.png", "category": "osmbasedmap",
"id": "alidade.smooth",
"type": "vector",
"attribution": { "attribution": {
"html": "<a href=\"https://carto.com/attributions\">CARTO</a>" "text": "Alidade",
}, "url": "https://stadiamaps.com/"
"max_zoom": 20, }
"category": "osmbasedmap"
}, },
{ {
"id": "CartoDB.PositronNoLabels", "name": "Alidade Smooth Dark",
"name": "Positron - no labels (by CartoDB)", "url": "https://tiles-eu.stadiamaps.com/styles/alidade_smooth_dark.json?key=14c5a900-7137-42f7-9cb9-fff0f4696f75",
"url": "https://{switch:a,b,c,d}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png",
"category": "osmbasedmap", "category": "osmbasedmap",
"id": "alidade.smooth_dark",
"type": "vector",
"attribution": { "attribution": {
"html": "<a href=\"https://carto.com/attributions\">CARTO</a>" "text": "Alidade/Stadiamaps",
}, "url": "https://stadiamaps.com/"
"max_zoom": 20 }
}, },
{ {
"id": "CartoDB.Voyager", "name": "Stamen Terrain",
"name": "Voyager (by CartoDB)", "url": "https://tiles-eu.stadiamaps.com/styles/stamen_terrain.json?key=14c5a900-7137-42f7-9cb9-fff0f4696f75",
"url": "https://{switch:a,b,c,d}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png",
"category": "osmbasedmap", "category": "osmbasedmap",
"id": "stamen.terrain",
"type": "vector",
"attribution": { "attribution": {
"html": "<a href=\"https://carto.com/attributions\">CARTO</a>" "text": "Stamen/Stadiamaps",
}, "url": "https://stadiamaps.com/"
"max_zoom": 20 }
}, },
{ {
"id": "CartoDB.VoyagerNoLabels", "name": "Stamen Toner",
"name": "Voyager - no labels (by CartoDB)", "url": "https://tiles-eu.stadiamaps.com/styles/stamen_toner.json?key=14c5a900-7137-42f7-9cb9-fff0f4696f75",
"url": "https://{switch:a,b,c,d}.basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}.png",
"category": "osmbasedmap", "category": "osmbasedmap",
"id": "stamen.toner",
"type": "vector",
"attribution": { "attribution": {
"html": "<a href=\"https://carto.com/attributions\">CARTO</a>" "text": "Stamen/Stadiamaps",
}, "url": "https://stadiamaps.com/"
"max_zoom": 20 }
}, },
{ {
"id": "CartoDB.DarkMatter", "url": "https://tiles-eu.stadiamaps.com/styles/osm_bright.json",
"name": "Dark Matter (by CartoDB)", "name": "StadiaMaps OSM Bright",
"url": "https://{switch:a,b,c,d}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png",
"category": "osmbasedmap", "category": "osmbasedmap",
"id": "stadia.bright",
"type": "vector",
"attribution": { "attribution": {
"html": "<a href=\"https://carto.com/attributions\">CARTO</a>" "text": "Stadiamaps",
}, "url": "https://stadiamaps.com/"
"max_zoom": 20 }
},
{
"id": "CartoDB.DarkMatterNoLabels",
"name": "Dark Matter - no labels (by CartoDB)",
"url": "https://{switch:a,b,c,d}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png",
"category": "osmbasedmap",
"attribution": {
"html": "<a href=\"https://carto.com/attributions\">CARTO</a>"
},
"max_zoom": 20
} }
] ]
} }