diff --git a/app/package-lock.json b/app/package-lock.json index 0d9d6bb..5af3da1 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -28,6 +28,7 @@ "react-use": "^17.4.0" }, "devDependencies": { + "@maplibre/maplibre-gl-style-spec": "^19.3.1", "@types/d3-path": "^3.0.0", "@types/d3-scale-chromatic": "^3.0.0", "@types/leaflet": "^1.7.9", diff --git a/app/package.json b/app/package.json index 1bddbc1..7d4b1df 100644 --- a/app/package.json +++ b/app/package.json @@ -30,6 +30,7 @@ "react-use": "^17.4.0" }, "devDependencies": { + "@maplibre/maplibre-gl-style-spec": "^19.3.1", "@types/d3-path": "^3.0.0", "@types/d3-scale-chromatic": "^3.0.0", "@types/leaflet": "^1.7.9", diff --git a/app/src/MaplibreMap.tsx b/app/src/MaplibreMap.tsx index 76c811b..a4f7b55 100644 --- a/app/src/MaplibreMap.tsx +++ b/app/src/MaplibreMap.tsx @@ -8,10 +8,18 @@ import { MapGeoJSONFeature } from "maplibre-gl"; import "maplibre-gl/dist/maplibre-gl.css"; import { schemeSet3 } from "d3-scale-chromatic"; import base_theme from "protomaps-themes-base"; +import { + StyleSpecification, + LayerSpecification, +} from "@maplibre/maplibre-gl-style-spec"; const INITIAL_ZOOM = 0; const INITIAL_LNG = 0; const INITIAL_LAT = 0; +const BASEMAP_URL = + "https://api.protomaps.com/tiles/v3/{z}/{x}/{y}.mvt?key=1003762824b9687f"; +const BASEMAP_ATTRIBUTION = + 'Basemap Protomaps © OpenStreetMap'; maplibregl.setRTLTextPlugin( "https://unpkg.com/@mapbox/mapbox-gl-rtl-text@0.2.3/mapbox-gl-rtl-text.min.js", @@ -169,14 +177,13 @@ const LayersVisibilityController = (props: { ); }; -const rasterStyle = async (file: PMTiles): Promise => { +const rasterStyle = async (file: PMTiles): Promise => { let header = await file.getHeader(); let metadata = await file.getMetadata(); - let layers: any[] = []; + let layers: LayerSpecification[] = []; if (metadata.type !== "baselayer") { layers = base_theme("basemap", "black"); - layers[0].paint["background-color"] = "black"; } layers.push({ @@ -196,10 +203,9 @@ const rasterStyle = async (file: PMTiles): Promise => { }, basemap: { type: "vector", - tiles: [ - "https://api.protomaps.com/tiles/v3/{z}/{x}/{y}.mvt?key=1003762824b9687f", - ], - maxzoom: 14, + tiles: [BASEMAP_URL], + maxzoom: 15, + attribution: BASEMAP_ATTRIBUTION, }, }, glyphs: "https://cdn.protomaps.com/fonts/pbf/{fontstack}/{range}.pbf", @@ -207,18 +213,24 @@ const rasterStyle = async (file: PMTiles): Promise => { }; }; -const vectorStyle = async (file: PMTiles): Promise => { +const vectorStyle = async ( + file: PMTiles +): Promise<{ + style: StyleSpecification; + layersVisibility: LayerVisibility[]; +}> => { let header = await file.getHeader(); let metadata = await file.getMetadata(); - let layers: any[] = []; + let layers: LayerSpecification[] = []; + let baseOpacity = 0.35; if (metadata.type !== "baselayer") { layers = base_theme("basemap", "black"); - layers[0].paint["background-color"] = "black"; + baseOpacity = 0.9; } var tilestats: any; - var vector_layers: any; + var vector_layers: LayerSpecification[]; if (metadata.json) { let j = JSON.parse(metadata.json); tilestats = j.tilestats; @@ -240,14 +252,14 @@ const vectorStyle = async (file: PMTiles): Promise => { "fill-opacity": [ "case", ["boolean", ["feature-state", "hover"], false], - 0.35, - 0.2, + baseOpacity, + baseOpacity - 0.15, ], "fill-outline-color": [ "case", ["boolean", ["feature-state", "hover"], false], "hsl(0,100%,90%)", - "rgba(0,0,0,0)", + "rgba(0,0,0,0.2)", ], }, filter: ["==", ["geometry-type"], "Polygon"], @@ -287,14 +299,12 @@ const vectorStyle = async (file: PMTiles): Promise => { } } - for (let layer of layers) { - if (layer["source-layer"] === "mask" && layer["type"] === "fill") { - layer.paint["fill-color"] = "black"; - layer.paint["fill-opacity"] = 0.8; - } - } - - const bounds = [header.minLon, header.minLat, header.maxLon, header.maxLat]; + const bounds: [number, number, number, number] = [ + header.minLon, + header.minLat, + header.maxLon, + header.maxLat, + ]; return { style: { @@ -309,17 +319,16 @@ const vectorStyle = async (file: PMTiles): Promise => { }, basemap: { type: "vector", - tiles: [ - "https://api.protomaps.com/tiles/v3/{z}/{x}/{y}.mvt?key=1003762824b9687f", - ], - maxzoom: 14, + tiles: [BASEMAP_URL], + maxzoom: 15, bounds: bounds, + attribution: BASEMAP_ATTRIBUTION, }, }, glyphs: "https://cdn.protomaps.com/fonts/pbf/{fontstack}/{range}.pbf", layers: layers, }, - layersVisibility: vector_layers.map((l: any) => ({ + layersVisibility: vector_layers.map((l: LayerSpecification) => ({ id: l.id, visible: true, })), @@ -455,7 +464,7 @@ function MaplibreMap(props: { file: PMTiles; mapHashPassed: boolean }) { ); } - let style: any; // TODO maplibre types (not any) + let style: StyleSpecification; if ( header.tileType === TileType.Png || header.tileType === TileType.Webp ||