diff --git a/examples/pmtiles-rasterDem.jGIS b/examples/pmtiles-rasterDem.jGIS new file mode 100644 index 000000000..d0ee5f0de --- /dev/null +++ b/examples/pmtiles-rasterDem.jGIS @@ -0,0 +1,60 @@ +{ + "layerTree": [ + "ee4ddf21-31f1-4a71-befd-c7a456628db6", + "e6a856f0-3883-4d10-9e9f-04132b3eb7f1" + ], + "layers": { + "e6a856f0-3883-4d10-9e9f-04132b3eb7f1": { + "name": "Custom Hillshade Layer Layer", + "parameters": { + "shadowColor": "#473B24", + "source": "7b6789c2-74cd-4a74-ad4d-ceef22d1eab8" + }, + "type": "HillshadeLayer", + "visible": true + }, + "ee4ddf21-31f1-4a71-befd-c7a456628db6": { + "name": "OpenStreetMap.Mapnik Layer", + "parameters": { + "source": "d6afefa4-6dcc-4a24-88ad-f7af0f6d76f3" + }, + "type": "RasterLayer", + "visible": true + } + }, + "options": { + "bearing": 0.0, + "latitude": 2.842170943040401e-14, + "longitude": -14.148151338180469, + "pitch": 0.0, + "zoom": 1.2095796395452967 + }, + "sources": { + "7b6789c2-74cd-4a74-ad4d-ceef22d1eab8": { + "name": "PMTile", + "parameters": { + "encoding": "terrarium", + "tileSize": 512.0, + "url": "pmtiles://https://r2-public.protomaps.com/protomaps-sample-datasets/terrarium_z9.pmtiles", + "urlParameters": {} + }, + "type": "RasterDemSource" + }, + "d6afefa4-6dcc-4a24-88ad-f7af0f6d76f3": { + "name": "OpenStreetMap.Mapnik", + "parameters": { + "attribution": "(C) OpenStreetMap contributors", + "maxZoom": 19.0, + "minZoom": 0.0, + "provider": "OpenStreetMap", + "url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png", + "urlParameters": {} + }, + "type": "RasterSource" + } + }, + "terrain": { + "exaggeration": 0.0, + "source": "" + } +} \ No newline at end of file diff --git a/examples/pmtiles-vector.jGIS b/examples/pmtiles-vector.jGIS new file mode 100644 index 000000000..f6968fd10 --- /dev/null +++ b/examples/pmtiles-vector.jGIS @@ -0,0 +1,87 @@ +{ + "layerTree": [ + "34e3dbaa-f1df-4add-af4b-8f8bec9bc0c8", + { + "layers": [ + "3c9de1bc-8a95-4913-a3a7-c76ea54146e6", + "2171c5a1-b237-4413-a358-07ea8bcc67d3" + ], + "name": "Firenze" + } + ], + "layers": { + "2171c5a1-b237-4413-a358-07ea8bcc67d3": { + "name": "Roads Layer", + "parameters": { + "color": "#241f31", + "opacity": 1.0, + "source": "c09567a9-2c88-4818-ad40-536e005e9496", + "sourceLayer": "roads", + "type": "line" + }, + "type": "VectorLayer", + "visible": true + }, + "34e3dbaa-f1df-4add-af4b-8f8bec9bc0c8": { + "name": "OpenStreetMap.Mapnik Layer", + "parameters": { + "source": "4cc5d853-cb4a-4d05-9560-0c5e7de54fb0" + }, + "type": "RasterLayer", + "visible": true + }, + "3c9de1bc-8a95-4913-a3a7-c76ea54146e6": { + "name": "Land Use", + "parameters": { + "color": "#1a5fb4", + "opacity": 1.0, + "source": "c09567a9-2c88-4818-ad40-536e005e9496", + "sourceLayer": "landuse", + "type": "fill" + }, + "type": "VectorLayer", + "visible": true + } + }, + "options": { + "bearing": 0.0, + "latitude": 43.769833024178524, + "longitude": 11.243657520618626, + "pitch": 9.886721549625761, + "zoom": 11.30647314935016 + }, + "sources": { + "4cc5d853-cb4a-4d05-9560-0c5e7de54fb0": { + "name": "OpenStreetMap.Mapnik", + "parameters": { + "attribution": "(C) OpenStreetMap contributors", + "maxZoom": 19.0, + "minZoom": 0.0, + "provider": "OpenStreetMap", + "url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png", + "urlParameters": {} + }, + "type": "RasterSource" + }, + "c09567a9-2c88-4818-ad40-536e005e9496": { + "name": "PMTiles", + "parameters": { + "bounds": [ + -180.0, + -85.051129, + 180.0, + 85.051129 + ], + "maxZoom": 24.0, + "minZoom": 0.0, + "scheme": "xyz", + "url": "pmtiles://https://pmtiles.io/protomaps(vector)ODbL_firenze.pmtiles" + }, + "type": "VectorTileSource" + } + }, + "terrain": { + "exaggeration": 0.0, + "source": "" + } +} \ No newline at end of file diff --git a/package.json b/package.json index 016522558..e304d562f 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.5.2", "@fortawesome/free-solid-svg-icons": "^6.5.2", - "@fortawesome/react-fontawesome": "latest" + "@fortawesome/react-fontawesome": "latest", + "pmtiles": "^3.0.7" } } diff --git a/packages/base/src/mainview/mainview.tsx b/packages/base/src/mainview/mainview.tsx index ae30a28a6..eddbd3d15 100644 --- a/packages/base/src/mainview/mainview.tsx +++ b/packages/base/src/mainview/mainview.tsx @@ -27,6 +27,7 @@ import * as React from 'react'; import * as MapLibre from 'maplibre-gl'; +import { Protocol } from 'pmtiles'; import { isLightTheme } from '../tools'; import { MainViewModel } from './mainviewmodel'; import { Spinner } from './spinner'; @@ -145,6 +146,10 @@ export class MainView extends React.Component { }); }); + // PM tile stuff + this._protocol = new Protocol(); + MapLibre.addProtocol('pmtiles', this._protocol.tile); + // Workaround for broken intialization of maplibre this._Map._lazyInitEmptyStyle(); @@ -170,27 +175,40 @@ export class MainView extends React.Component { switch (source.type) { case 'RasterSource': { const mapSource = this._Map.getSource(id) as MapLibre.RasterTileSource; + if (!mapSource) { - this._Map.addSource(id, { - type: 'raster', - attribution: source.parameters?.attribution || '', - tiles: [this.computeSourceUrl(source)], - tileSize: 256 - }); + const parameters = source.parameters as IRasterSource; + const sourceSpec = this.configureTileSource( + { + type: 'raster', + minzoom: parameters.minZoom, + maxzoom: parameters.maxZoom, + attribution: parameters.attribution || '', + tileSize: 256 + }, + this.computeSourceUrl(source) + ); + + this._Map.addSource(id, sourceSpec); } break; } case 'VectorTileSource': { const mapSource = this._Map.getSource(id) as MapLibre.VectorTileSource; + if (!mapSource) { const parameters = source.parameters as IVectorTileSource; - this._Map.addSource(id, { - type: 'vector', - minzoom: parameters.minZoom, - maxzoom: parameters.maxZoom, - attribution: parameters.attribution || '', - tiles: [this.computeSourceUrl(source)] - }); + const sourceSpec = this.configureTileSource( + { + type: 'vector', + minzoom: parameters.minZoom, + maxzoom: parameters.maxZoom, + attribution: parameters.attribution || '' + }, + this.computeSourceUrl(source) + ); + + this._Map.addSource(id, sourceSpec); } break; } @@ -211,13 +229,19 @@ export class MainView extends React.Component { const mapSource = this._Map.getSource( id ) as MapLibre.RasterDEMTileSource; + if (!mapSource) { const parameters = source.parameters as IRasterDemSource; - this._Map.addSource(id, { - type: 'raster-dem', - tileSize: parameters.tileSize, - url: parameters.url - }); + const sourceSpec = this.configureTileSource( + { + type: 'raster-dem', + tileSize: parameters.tileSize, + encoding: parameters.encoding + }, + this.computeSourceUrl(source) + ); + + this._Map.addSource(id, sourceSpec); } break; } @@ -294,15 +318,21 @@ export class MainView extends React.Component { switch (source.type) { case 'RasterSource': { - (mapSource as MapLibre.RasterTileSource).setTiles([ - this.computeSourceUrl(source) - ]); + const sourceCast = mapSource as MapLibre.RasterTileSource; + const url = this.computeSourceUrl(source); + this._handleSourceUpdate(sourceCast, url); break; } case 'VectorTileSource': { - (mapSource as MapLibre.RasterTileSource).setTiles([ - this.computeSourceUrl(source) - ]); + const sourceCast = mapSource as MapLibre.VectorTileSource; + const url = this.computeSourceUrl(source); + this._handleSourceUpdate(sourceCast, url); + break; + } + case 'RasterDemSource': { + const sourceCast = mapSource as MapLibre.RasterDEMTileSource; + const url = this.computeSourceUrl(source); + this._handleSourceUpdate(sourceCast, url); break; } case 'GeoJSONSource': { @@ -312,11 +342,6 @@ export class MainView extends React.Component { (mapSource as MapLibre.GeoJSONSource).setData(data); break; } - case 'RasterDemSource': { - const parameters = source.parameters as IRasterDemSource; - (mapSource as MapLibre.RasterDEMTileSource).setTiles([parameters.url]); - break; - } case 'ImageSource': { const parameters = source.parameters as IImageSource; (mapSource as MapLibre.ImageSource).updateImage({ @@ -445,6 +470,7 @@ export class MainView extends React.Component { if (index < currentLayerIds.length && index !== -1) { beforeId = currentLayerIds[index]; } + switch (layer.type) { case 'RasterLayer': { this._Map.addLayer( @@ -665,6 +691,81 @@ export class MainView extends React.Component { } } + /** + * Determines whether to use tiles or a URL for a given source based on the presence of replaceable parameters in the URL. + * + * This method checks if the provided URL contains patterns that indicate it can be broken down into tiles + * (e.g., {z}/{x}/{y}, {ratio}, etc.). + * If such patterns are found, it suggests that tiles should be used. Otherwise, the URL itself should be used. + * + * @param url - The URL to check for replaceable parameters. + * @returns True if the URL contains replaceable parameters indicating the use of tiles, false otherwise. + */ + private _shouldUseTiles(url: string) { + const regexPatterns = [ + /\{z\}/, + /\{x\}/, + /\{y\}/, + /\{ratio\}/, + /\{quadkey\}/, + /\{bbox-epsg-3857\}/ + ]; + + let result = false; + + for (const pattern of regexPatterns) { + if (pattern.test(url)) { + result = true; + break; + } + } + + return result; + } + + /** + * Updates the given source with either a new URL or tiles based on the result of `_shouldUseTiles`. + * + * @param source - The source to update. + * @param url - The URL to set for the source. + */ + private _handleSourceUpdate( + source: + | MapLibre.RasterTileSource + | MapLibre.VectorTileSource + | MapLibre.RasterDEMTileSource, + url: string + ) { + const result = this._shouldUseTiles(url); + + result ? source.setTiles([url]) : source.setUrl(url); + } + + /** + * Configures a source specification, setting either the `tiles` or `url` property based on the provided URL. + * + * This method uses the `_shouldUseTiles` method to determine whether the source should be configured with tiles or a direct URL. It then modifies the `sourceSpec` object accordingly. + * + * @param sourceSpec - The source specification object to configure. This object is modified in place. + * @param url - The URL to check for replaceable parameters and use in configuring the source. + * @returns The modified source specification object. + */ + private configureTileSource( + sourceSpec: + | MapLibre.RasterSourceSpecification + | MapLibre.RasterDEMSourceSpecification + | MapLibre.VectorSourceSpecification, + url: string + ) { + const result = this._shouldUseTiles(url); + + result + ? (sourceSpec = { tiles: [url], ...sourceSpec }) + : (sourceSpec = { url, ...sourceSpec }); + + return sourceSpec; + } + private _onClientSharedStateChanged = ( sender: IJupyterGISModel, clients: Map @@ -825,4 +926,5 @@ export class MainView extends React.Component { private _ready = false; private _terrainControl: MapLibre.TerrainControl | null; private _videoPlaying = false; + private _protocol: Protocol; } diff --git a/packages/schema/src/schema/rasterDemSource.json b/packages/schema/src/schema/rasterDemSource.json index adf916b06..4eca60dc1 100644 --- a/packages/schema/src/schema/rasterDemSource.json +++ b/packages/schema/src/schema/rasterDemSource.json @@ -18,6 +18,11 @@ "type": "string", "description": "The attribution for the raster-dem source" }, + "encoding": { + "type": "string", + "enum": ["terrarium", "mapbox"], + "default": "mapbox" + }, "urlParameters": { "type": "object", "additionalProperties": { diff --git a/ui-tests/tests/contextmenu.spec.ts b/ui-tests/tests/contextmenu.spec.ts index f273fcbcb..ec715d712 100644 --- a/ui-tests/tests/contextmenu.spec.ts +++ b/ui-tests/tests/contextmenu.spec.ts @@ -11,7 +11,7 @@ test.describe('context menu', () => { ); }); test.beforeEach(async ({ page }) => { - await page.filebrowser.open('testDir/test.jGIS'); + await page.filebrowser.open('testDir/context-test.jGIS'); }); test.afterEach(async ({ page }) => { diff --git a/ui-tests/tests/gis-files/context-test.jGIS b/ui-tests/tests/gis-files/context-test.jGIS new file mode 100644 index 000000000..9498dbcc9 --- /dev/null +++ b/ui-tests/tests/gis-files/context-test.jGIS @@ -0,0 +1,81 @@ +{ + "layerTree": [ + "a0044fd7-f167-445f-b3d1-620a8f94b498", + { + "layers": [ + "2467576f-b527-4cb7-998d-fa1d056fb8a1", + { + "layers": [ + "57ef55ef-facb-48a2-ae1d-c9c824be3e8a" + ], + "name": "level 2 group" + } + ], + "name": "level 1 group" + } + ], + "layers": { + "2467576f-b527-4cb7-998d-fa1d056fb8a1": { + "name": "Open Street Map", + "parameters": { + "source": "699facc9-e7c4-4f38-acf1-1fd7f02d9f36" + }, + "type": "RasterLayer", + "visible": true + }, + "57ef55ef-facb-48a2-ae1d-c9c824be3e8a": { + "name": "Regions France", + "parameters": { + "color": "#e66100", + "opacity": 0.6, + "source": "7d082e75-69d5-447a-82d8-b05cca5945ba", + "type": "line" + }, + "type": "VectorLayer", + "visible": true + }, + "a0044fd7-f167-445f-b3d1-620a8f94b498": { + "name": "Open Topo Map", + "parameters": { + "source": "5fd42e3b-4681-4607-b15d-65c3a3e89b32" + }, + "type": "RasterLayer", + "visible": true + } + }, + "options": { + "bearing": 0.0, + "latitude": 45.83799886047626, + "longitude": 1.195999428191726, + "pitch": 0.0, + "zoom": 4.075821017792898 + }, + "sources": { + "5fd42e3b-4681-4607-b15d-65c3a3e89b32": { + "name": "Open Topo Map", + "parameters": { + "maxZoom": 24.0, + "minZoom": 0.0, + "url": "https://tile.opentopomap.org/{z}/{x}/{y}.png " + }, + "type": "RasterSource" + }, + "699facc9-e7c4-4f38-acf1-1fd7f02d9f36": { + "name": "Open Street Map", + "parameters": { + "maxZoom": 24.0, + "minZoom": 0.0, + "url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png" + }, + "type": "RasterSource" + }, + "7d082e75-69d5-447a-82d8-b05cca5945ba": { + "name": "france_regions", + "parameters": { + "path": "france_regions.json", + "valid": true + }, + "type": "GeoJSONSource" + } + } +} \ No newline at end of file diff --git a/ui-tests/tests/gis-files/panel-test.jGIS b/ui-tests/tests/gis-files/panel-test.jGIS new file mode 100644 index 000000000..9498dbcc9 --- /dev/null +++ b/ui-tests/tests/gis-files/panel-test.jGIS @@ -0,0 +1,81 @@ +{ + "layerTree": [ + "a0044fd7-f167-445f-b3d1-620a8f94b498", + { + "layers": [ + "2467576f-b527-4cb7-998d-fa1d056fb8a1", + { + "layers": [ + "57ef55ef-facb-48a2-ae1d-c9c824be3e8a" + ], + "name": "level 2 group" + } + ], + "name": "level 1 group" + } + ], + "layers": { + "2467576f-b527-4cb7-998d-fa1d056fb8a1": { + "name": "Open Street Map", + "parameters": { + "source": "699facc9-e7c4-4f38-acf1-1fd7f02d9f36" + }, + "type": "RasterLayer", + "visible": true + }, + "57ef55ef-facb-48a2-ae1d-c9c824be3e8a": { + "name": "Regions France", + "parameters": { + "color": "#e66100", + "opacity": 0.6, + "source": "7d082e75-69d5-447a-82d8-b05cca5945ba", + "type": "line" + }, + "type": "VectorLayer", + "visible": true + }, + "a0044fd7-f167-445f-b3d1-620a8f94b498": { + "name": "Open Topo Map", + "parameters": { + "source": "5fd42e3b-4681-4607-b15d-65c3a3e89b32" + }, + "type": "RasterLayer", + "visible": true + } + }, + "options": { + "bearing": 0.0, + "latitude": 45.83799886047626, + "longitude": 1.195999428191726, + "pitch": 0.0, + "zoom": 4.075821017792898 + }, + "sources": { + "5fd42e3b-4681-4607-b15d-65c3a3e89b32": { + "name": "Open Topo Map", + "parameters": { + "maxZoom": 24.0, + "minZoom": 0.0, + "url": "https://tile.opentopomap.org/{z}/{x}/{y}.png " + }, + "type": "RasterSource" + }, + "699facc9-e7c4-4f38-acf1-1fd7f02d9f36": { + "name": "Open Street Map", + "parameters": { + "maxZoom": 24.0, + "minZoom": 0.0, + "url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png" + }, + "type": "RasterSource" + }, + "7d082e75-69d5-447a-82d8-b05cca5945ba": { + "name": "france_regions", + "parameters": { + "path": "france_regions.json", + "valid": true + }, + "type": "GeoJSONSource" + } + } +} \ No newline at end of file diff --git a/ui-tests/tests/left-panel.spec.ts b/ui-tests/tests/left-panel.spec.ts index 6b4aa37b7..3c1205598 100644 --- a/ui-tests/tests/left-panel.spec.ts +++ b/ui-tests/tests/left-panel.spec.ts @@ -71,7 +71,7 @@ test.describe('#layerPanel', () => { ); }); test.beforeEach(async ({ page }) => { - await page.filebrowser.open('testDir/test.jGIS'); + await page.filebrowser.open('testDir/panel-test.jGIS'); }); test.afterEach(async ({ page }) => { @@ -289,7 +289,7 @@ test.describe('#sourcePanel', () => { ); }); test.beforeEach(async ({ page }) => { - await page.filebrowser.open('examples/test.jGIS'); + await page.filebrowser.open('testDir/panel-test.jGIS'); }); test.afterEach(async ({ page }) => { diff --git a/ui-tests/tests/notebook.spec.ts-snapshots/dark-Notebook-ipynb-cell-1-linux.png b/ui-tests/tests/notebook.spec.ts-snapshots/dark-Notebook-ipynb-cell-1-linux.png index d8d6fa6ab..fbd83b4d2 100644 Binary files a/ui-tests/tests/notebook.spec.ts-snapshots/dark-Notebook-ipynb-cell-1-linux.png and b/ui-tests/tests/notebook.spec.ts-snapshots/dark-Notebook-ipynb-cell-1-linux.png differ diff --git a/ui-tests/tests/notebook.spec.ts-snapshots/dark-Notebook-ipynb-cell-2-linux.png b/ui-tests/tests/notebook.spec.ts-snapshots/dark-Notebook-ipynb-cell-2-linux.png index 75cde0cbb..080a93b96 100644 Binary files a/ui-tests/tests/notebook.spec.ts-snapshots/dark-Notebook-ipynb-cell-2-linux.png and b/ui-tests/tests/notebook.spec.ts-snapshots/dark-Notebook-ipynb-cell-2-linux.png differ diff --git a/ui-tests/tests/notebook.spec.ts-snapshots/light-Notebook-ipynb-cell-1-linux.png b/ui-tests/tests/notebook.spec.ts-snapshots/light-Notebook-ipynb-cell-1-linux.png index 0a130adfd..f3e11c20d 100644 Binary files a/ui-tests/tests/notebook.spec.ts-snapshots/light-Notebook-ipynb-cell-1-linux.png and b/ui-tests/tests/notebook.spec.ts-snapshots/light-Notebook-ipynb-cell-1-linux.png differ diff --git a/ui-tests/tests/notebook.spec.ts-snapshots/light-Notebook-ipynb-cell-2-linux.png b/ui-tests/tests/notebook.spec.ts-snapshots/light-Notebook-ipynb-cell-2-linux.png index 3d30d6a65..c419e3d4a 100644 Binary files a/ui-tests/tests/notebook.spec.ts-snapshots/light-Notebook-ipynb-cell-2-linux.png and b/ui-tests/tests/notebook.spec.ts-snapshots/light-Notebook-ipynb-cell-2-linux.png differ diff --git a/ui-tests/tests/notebooks/Notebook.ipynb b/ui-tests/tests/notebooks/Notebook.ipynb index 0becef71b..3ae3bae8c 100644 --- a/ui-tests/tests/notebooks/Notebook.ipynb +++ b/ui-tests/tests/notebooks/Notebook.ipynb @@ -18,7 +18,7 @@ " opacity=0.6\n", ")\n", "\n", - "doc" + "doc\n" ] }, { @@ -31,12 +31,11 @@ "doc = GISDocument(latitude=40.775, longitude=-73.973, zoom=13)\n", "\n", "doc.add_raster_layer(\n", - " url=\"https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}\",\n", - " name=\"Google Satellite\",\n", - " attribution=\"Google\",\n", + " url=\"https://tile.openstreetmap.org/{z}/{x}/{y}.png\",\n", + " name=\"Open Street Map\",\n", + " attribution=\"OpenStreetMap\",\n", " opacity=0.6\n", ")\n", - "\n", "doc" ] }, @@ -50,9 +49,9 @@ "doc = GISDocument(latitude=40.775, longitude=-73.973, zoom=13)\n", "\n", "doc.add_raster_layer(\n", - " url=\"https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}\",\n", - " name=\"Google Satellite\",\n", - " attribution=\"Google\",\n", + " url=\"https://tile.openstreetmap.org/{z}/{x}/{y}.png\",\n", + " name=\"Open Street Map\",\n", + " attribution=\"OpenStreetMap\",\n", " opacity=0.6\n", ")\n", "\n", diff --git a/ui-tests/tests/ui.spec.ts-snapshots/Render-test-jGIS-linux.png b/ui-tests/tests/ui.spec.ts-snapshots/Render-test-jGIS-linux.png index 19a28bca8..abfe743ae 100644 Binary files a/ui-tests/tests/ui.spec.ts-snapshots/Render-test-jGIS-linux.png and b/ui-tests/tests/ui.spec.ts-snapshots/Render-test-jGIS-linux.png differ diff --git a/yarn.lock b/yarn.lock index 918138df4..fe24ef1e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1024,6 +1024,7 @@ __metadata: eslint-plugin-prettier: ^5.0.1 lerna: ^7.0.0 npm-run-all: ^4.1.5 + pmtiles: ^3.0.7 prettier: ^3.0.0 rimraf: ^3.0.2 typescript: ^5 @@ -3204,6 +3205,15 @@ __metadata: languageName: node linkType: hard +"@types/leaflet@npm:^1.9.8": + version: 1.9.12 + resolution: "@types/leaflet@npm:1.9.12" + dependencies: + "@types/geojson": "*" + checksum: 45e69572048d6da96840505ab13e1e672f75d1e058c8dd87894e7d4be51c62b650303406e45c9df720722a111d348c27626ed22eb7ac0ac102249964e958a635 + languageName: node + linkType: hard + "@types/lodash@npm:^4.14.134, @types/lodash@npm:^4.14.168": version: 4.17.4 resolution: "@types/lodash@npm:4.17.4" @@ -6018,6 +6028,13 @@ __metadata: languageName: node linkType: hard +"fflate@npm:^0.8.0": + version: 0.8.2 + resolution: "fflate@npm:0.8.2" + checksum: 29470337b85d3831826758e78f370e15cda3169c5cd4477c9b5eea2402261a74b2975bae816afabe1c15d21d98591e0d30a574f7103aa117bff60756fa3035d4 + languageName: node + linkType: hard + "figures@npm:3.2.0, figures@npm:^3.0.0": version: 3.2.0 resolution: "figures@npm:3.2.0" @@ -9685,6 +9702,16 @@ __metadata: languageName: node linkType: hard +"pmtiles@npm:^3.0.7": + version: 3.0.7 + resolution: "pmtiles@npm:3.0.7" + dependencies: + "@types/leaflet": ^1.9.8 + fflate: ^0.8.0 + checksum: 2fafd09cf14f138125ba84d8f5afa2666defde603414cc4bf642134a5187664fd3068ab6b5f780f6be8a4cc23f3c8094023e50d19fdef203dfdcdca7a56542c9 + languageName: node + linkType: hard + "popper.js@npm:^1.14.4, popper.js@npm:^1.16.1": version: 1.16.1 resolution: "popper.js@npm:1.16.1"