From e6f6be1e4fc1074b0fc4375516c196166966c9db Mon Sep 17 00:00:00 2001 From: Benjamin TD Date: Tue, 25 Apr 2023 14:48:54 +0200 Subject: [PATCH] maplibre with demo tiles --- README.md | 2 +- package.json | 3 +- src/pages/index.tsx | 364 ++++++++++++++++++++++++++++++-------------- yarn.lock | 88 ++++++++++- 4 files changed, 334 insertions(+), 123 deletions(-) diff --git a/README.md b/README.md index df171ca9..37127008 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ The isochrones are stored as GeoJSON and served on hover. This is a Next.js application deployed on Vercel. This allows using the Vercel Edge cache to serve the isochrones fast enough to have a smooth experience. -The mapping library is mapbox-gl. +The mapping library is maplibre-gl. The data is stored on a Postgres database hosted at Supabase. diff --git a/package.json b/package.json index db83aecd..3e8e3cfc 100644 --- a/package.json +++ b/package.json @@ -15,10 +15,9 @@ "@prisma/client": "^4.1.0", "@tailwindcss/typography": "^0.5.4", "@turf/turf": "^6.5.0", - "@types/mapbox-gl": "^2.7.3", "lodash": "^4.17.21", "lru-cache": "^7.13.1", - "mapbox-gl": "^2.9.2", + "maplibre-gl": "^2.4.0", "next": "^12.3.0", "next-i18next": "^12.0.0", "next-usequerystate": "^1.7.2", diff --git a/src/pages/index.tsx b/src/pages/index.tsx index a1b05e16..ce56efa4 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,5 +1,5 @@ import type { GetStaticProps, NextPage } from "next"; -import mapboxgl, { GeoJSONSource, MapMouseEvent } from "mapbox-gl"; +import maplibregl, { GeoJSONSource, MapMouseEvent } from "maplibre-gl"; import { useEffect, useRef, @@ -8,7 +8,7 @@ import { useCallback, useMemo, } from "react"; -import "mapbox-gl/dist/mapbox-gl.css"; +import "maplibre-gl/dist/maplibre-gl.css"; import { IsochronesRes } from "./isochrones/[stationId]"; import { FeatureCollection, MultiPolygon, Polygon } from "@turf/turf"; import { Transition } from "@headlessui/react"; @@ -20,8 +20,6 @@ import { queryTypes, useQueryStates } from "next-usequerystate"; import { useRouter } from "next/router"; import AdBlock from "~/components/adBlock"; -mapboxgl.accessToken = process.env.NEXT_PUBLIC_MAPBOX_TOKEN!; - const Home: NextPage = () => { const [routeParams, setRouteParams] = useQueryStates({ lat: queryTypes.float.withDefault(45), @@ -31,7 +29,7 @@ const Home: NextPage = () => { }); const mapContainer = useRef(null); - const [map, setMap] = useState(null); + const [map, setMap] = useState(null); const [hoveredStation, setHoveredStation] = useState(null); const [selectedStation, setSelectedStation] = useState( routeParams.stationId @@ -51,126 +49,265 @@ const Home: NextPage = () => { useEffect(() => { if (map) return; // initialize map only once - let mapboxMap = new mapboxgl.Map({ + let maplibreMap = new maplibregl.Map({ container: mapContainer.current!, - style: "mapbox://styles/mapbox/light-v9", + style: { + name: "MapLibre", + glyphs: "https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf", + layers: [ + { + id: "background", + type: "background", + paint: { + "background-color": "#dddddd", + }, + layout: { + visibility: "visible", + }, + maxzoom: 24, + }, + { + id: "coastline", + type: "line", + paint: { + "line-blur": 0.5, + "line-color": "#aaaaaa", + "line-width": { + stops: [ + [0, 2], + [6, 6], + [14, 9], + [22, 18], + ], + }, + }, + filter: ["all"], + layout: { + "line-cap": "round", + "line-join": "round", + visibility: "visible", + }, + source: "maplibre", + maxzoom: 24, + minzoom: 0, + "source-layer": "countries", + }, + { + id: "countries-fill", + type: "fill", + paint: { + "fill-color": [ + "match", + ["get", "ADM0_A3"], + [ + "FRA", + "ESP", + "PRT", + "GBR", + "DEU", + "FIN", + "SWE", + "NOR", + "BEL", + "LUX", + "DNK", + "NLD", + "ITA", + "CHE", + "AUT", + "POL", + "CZE", + "SVK", + "SVN", + "HUN", + "HRV", + "UKR", + "MDA", + "ROU", + "BGR", + "IRL", + "GRC", + "LTU", + ], + "#ffffff", + "#eeeeee", + ], + }, + filter: ["all"], + layout: { + visibility: "visible", + }, + source: "maplibre", + maxzoom: 24, + "source-layer": "countries", + }, + { + id: "countries-boundary", + type: "line", + paint: { + "line-color": "rgba(245, 245, 245, 1)", + "line-width": { + stops: [ + [1, 1], + [6, 2], + [14, 6], + [22, 12], + ], + }, + "line-opacity": { + stops: [ + [3, 0.5], + [6, 1], + ], + }, + }, + layout: { + "line-cap": "round", + "line-join": "round", + visibility: "visible", + }, + source: "maplibre", + maxzoom: 24, + "source-layer": "countries", + }, + { + id: "geolines", + type: "line", + paint: { + "line-color": "#aaaaaa", + "line-opacity": 1, + "line-dasharray": [3, 3], + }, + filter: ["all", ["!=", "name", "International Date Line"]], + layout: { + visibility: "visible", + }, + source: "maplibre", + maxzoom: 24, + "source-layer": "geolines", + }, + { + id: "geolines-label", + type: "symbol", + paint: { + "text-color": "#aaaaaa", + "text-halo-blur": 1, + "text-halo-color": "rgba(255, 255, 255, 1)", + "text-halo-width": 1, + }, + filter: ["all", ["!=", "name", "International Date Line"]], + layout: { + "text-font": ["Open Sans Semibold"], + "text-size": { + stops: [ + [2, 12], + [6, 16], + ], + }, + "text-field": "{name}", + visibility: "visible", + "symbol-placement": "line", + }, + source: "maplibre", + maxzoom: 24, + minzoom: 1, + "source-layer": "geolines", + }, + { + id: "countries-label", + type: "symbol", + paint: { + "text-color": "rgba(8, 37, 77, 1)", + "text-halo-blur": { + stops: [ + [2, 0.2], + [6, 0], + ], + }, + "text-halo-color": "rgba(255, 255, 255, 1)", + "text-halo-width": { + stops: [ + [2, 1], + [6, 1.6], + ], + }, + }, + filter: ["all"], + layout: { + "text-font": ["Open Sans Semibold"], + "text-size": { + stops: [ + [2, 10], + [4, 12], + [6, 16], + ], + }, + "text-field": { + stops: [ + [2, "{ABBREV}"], + [4, "{NAME}"], + ], + }, + visibility: "visible", + "text-max-width": 10, + "text-transform": { + stops: [ + [0, "uppercase"], + [2, "none"], + ], + }, + }, + source: "maplibre", + maxzoom: 24, + minzoom: 2, + "source-layer": "centroids", + }, + ], + bearing: 0, + sources: { + maplibre: { + url: "https://demotiles.maplibre.org/tiles/tiles.json", + type: "vector", + }, + }, + version: 8, + } as any, center: [routeParams.lng, routeParams.lat], zoom: routeParams.zoom, }); - mapboxMap.on("load", () => { - setMap(mapboxMap); + maplibreMap.on("load", () => { + setMap(maplibreMap); - mapboxMap.addSource("stations", { + maplibreMap.addSource("stations", { type: "geojson", data: { type: "FeatureCollection", features: [] }, }); - mapboxMap.addSource("countries", { - type: "vector", - url: "mapbox://mapbox.country-boundaries-v1", - }); - - mapboxMap.addSource("isochrones", { + maplibreMap.addSource("isochrones", { type: "geojson", data: { type: "FeatureCollection", features: [] }, }); - mapboxMap.addSource("hoveredStation", { + maplibreMap.addSource("hoveredStation", { type: "geojson", data: { type: "FeatureCollection", features: [] }, }); - mapboxMap.addSource("selectedStation", { + maplibreMap.addSource("selectedStation", { type: "geojson", data: { type: "FeatureCollection", features: [] }, }); - mapboxMap.addLayer( - { - id: "country-boundaries", - type: "fill", - source: "countries", - "source-layer": "country_boundaries", - filter: [ - "match", - ["get", "iso_3166_1_alpha_3"], - ["FRA"], - true, - ["ESP"], - true, - ["PRT"], - true, - ["GBR"], - true, - ["DEU"], - true, - ["FIN"], - true, - ["SWE"], - true, - ["NOR"], - true, - ["BEL"], - true, - ["LUX"], - true, - ["DNK"], - true, - ["NLD"], - true, - ["ITA"], - true, - ["CHE"], - true, - ["AUT"], - true, - ["POL"], - true, - ["CZE"], - true, - ["SVK"], - true, - ["SVN"], - true, - ["HUN"], - true, - ["HRV"], - true, - ["UKR"], - true, - ["MDA"], - true, - ["ROU"], - true, - ["BGR"], - true, - ["IRL"], - true, - ["GRC"], - true, - ["LTU"], - true, - false, - ], - layout: {}, - paint: { "fill-color": "hsl(0, 0%, 100%)" }, - }, - "water shadow" - ); - - mapboxMap.addLayer( - { - id: "stations", - type: "circle", - source: "stations", - paint: { - "circle-radius": 5, - "circle-opacity": 0, - }, + maplibreMap.addLayer({ + id: "stations", + type: "circle", + source: "stations", + paint: { + "circle-radius": 5, + "circle-opacity": 0, }, - "waterway-label" - ); + }); - mapboxMap.addLayer( + maplibreMap.addLayer( { id: "isochrones", type: "fill", @@ -205,10 +342,10 @@ const Home: NextPage = () => { ], }, }, - "waterway-label" + "countries-label" ); - mapboxMap.addLayer( + maplibreMap.addLayer( { id: "isochrones-outline", type: "line", @@ -235,10 +372,10 @@ const Home: NextPage = () => { "line-width": 1.5, }, }, - "waterway-label" + "countries-label" ); - mapboxMap.addLayer({ + maplibreMap.addLayer({ id: "stations-symbol", type: "symbol", source: "stations", @@ -246,23 +383,20 @@ const Home: NextPage = () => { "text-field": ["get", "name"], "text-offset": [0, -1.5], "text-size": 10, - "text-font": ["DIN Pro Medium", "Open Sans Regular"], - "icon-image": "dot-11", }, paint: { "text-color": "#333", }, - minzoom: 7, + minzoom: 6, }); - mapboxMap.addLayer({ + maplibreMap.addLayer({ id: "hoveredStation", type: "symbol", source: "hoveredStation", layout: { "text-field": ["get", "name"], "text-offset": [0, -1.5], - "text-font": ["DIN Pro Bold", "Open Sans Bold"], "icon-image": "dot-11", }, paint: { @@ -270,15 +404,13 @@ const Home: NextPage = () => { }, }); - mapboxMap.addLayer({ + maplibreMap.addLayer({ id: "selectedStation", type: "symbol", source: "selectedStation", layout: { "text-field": ["get", "name"], "text-offset": [0, -1.5], - "text-font": ["DIN Pro Bold", "Open Sans Bold"], - "icon-image": "dot-11", }, paint: { "text-color": "#110", @@ -410,7 +542,7 @@ const Home: NextPage = () => { const setMapIsochronesData = useCallback( ( station: number, - map: mapboxgl.Map, + map: maplibregl.Map, isochronesData: IsochronesRes | undefined ) => { if (isochronesData && isochronesData.stationId === station) { diff --git a/yarn.lock b/yarn.lock index 06d4394b..43bce5af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -83,7 +83,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@mapbox/geojson-rewind@^0.5.1": +"@mapbox/geojson-rewind@^0.5.1", "@mapbox/geojson-rewind@^0.5.2": version "0.5.2" resolved "https://registry.yarnpkg.com/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz#591a5d71a9cd1da1a0bf3420b3bea31b0fc7946a" integrity sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA== @@ -121,6 +121,11 @@ resolved "https://registry.yarnpkg.com/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz#15651bd553a67b8581fb398810c98ad86a34524e" integrity sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA== +"@mapbox/unitbezier@^0.0.1": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz#d32deb66c7177e9e9dfc3bbd697083e2e657ff01" + integrity sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw== + "@mapbox/vector-tile@^1.3.1": version "1.3.1" resolved "https://registry.yarnpkg.com/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz#d3a74c90402d06e89ec66de49ec817ff53409666" @@ -1426,7 +1431,7 @@ "@turf/invariant" "^6.5.0" d3-voronoi "1.1.2" -"@types/geojson@*": +"@types/geojson@*", "@types/geojson@^7946.0.10": version "7946.0.10" resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.10.tgz#6dfbf5ea17142f7f9a043809f1cd4c448cb68249" integrity sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA== @@ -1456,6 +1461,20 @@ dependencies: "@types/geojson" "*" +"@types/mapbox__point-geometry@*", "@types/mapbox__point-geometry@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.2.tgz#488a9b76e8457d6792ea2504cdd4ecdd9860a27e" + integrity sha512-D0lgCq+3VWV85ey1MZVkE8ZveyuvW5VAfuahVTQRpXFQTxw03SuIf1/K4UQ87MMIXVKzpFjXFiFMZzLj2kU+iA== + +"@types/mapbox__vector-tile@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.0.tgz#8fa1379dbaead1e1b639b8d96cfd174404c379d6" + integrity sha512-kDwVreQO5V4c8yAxzZVQLE5tyWF+IPToAanloQaSnwfXmIcJ7cyOrv8z4Ft4y7PsLYmhWXmON8MBV8RX0Rgr8g== + dependencies: + "@types/geojson" "*" + "@types/mapbox__point-geometry" "*" + "@types/pbf" "*" + "@types/node-fetch@2.6": version "2.6.2" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da" @@ -1474,6 +1493,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.35.tgz#635b7586086d51fb40de0a2ec9d1014a5283ba4a" integrity sha512-vu1SrqBjbbZ3J6vwY17jBs8Sr/BKA+/a/WtjRG+whKg1iuLFOosq872EXS0eXWILdO36DHQQeku/ZcL6hz2fpg== +"@types/pbf@*", "@types/pbf@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/pbf/-/pbf-3.0.2.tgz#8d291ad68b4b8c533e96c174a2e3e6399a59ed61" + integrity sha512-EDrLIPaPXOZqDjrkzxxbX7UlJSeQVgah3i0aA4pOSzmK9zq3BIh7/MZIQxED7slJByvKM4Gc6Hypyu2lJzh3SQ== + "@types/prop-types@*": version "15.7.5" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" @@ -2006,7 +2030,7 @@ dotenv@^16.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.1.tgz#8f8f9d94876c35dac989876a5d3a82a267fdce1d" integrity sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ== -earcut@^2.0.0, earcut@^2.2.3: +earcut@^2.0.0, earcut@^2.2.3, earcut@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/earcut/-/earcut-2.2.4.tgz#6d02fd4d68160c114825d06890a92ecaae60343a" integrity sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ== @@ -2484,6 +2508,15 @@ glob@^7.1.3, glob@^7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + globals@^13.15.0: version "13.17.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" @@ -2606,6 +2639,11 @@ inherits@2: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +ini@^1.3.5: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + internal-slot@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" @@ -2781,6 +2819,11 @@ kdbush@^3.0.0: resolved "https://registry.yarnpkg.com/kdbush/-/kdbush-3.0.0.tgz#f8484794d47004cc2d85ed3a79353dbe0abc2bf0" integrity sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew== +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + language-subtag-registry@~0.3.2: version "0.3.22" resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" @@ -2886,6 +2929,36 @@ mapbox-gl@^2.9.2: tinyqueue "^2.0.3" vt-pbf "^3.1.3" +maplibre-gl@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/maplibre-gl/-/maplibre-gl-2.4.0.tgz#2b53dbf526626bf4ee92ad4f33f13ef09e5af182" + integrity sha512-csNFylzntPmHWidczfgCZpvbTSmhaWvLRj9e1ezUDBEPizGgshgm3ea1T5TCNEEBq0roauu7BPuRZjA3wO4KqA== + dependencies: + "@mapbox/geojson-rewind" "^0.5.2" + "@mapbox/jsonlint-lines-primitives" "^2.0.2" + "@mapbox/mapbox-gl-supported" "^2.0.1" + "@mapbox/point-geometry" "^0.1.0" + "@mapbox/tiny-sdf" "^2.0.5" + "@mapbox/unitbezier" "^0.0.1" + "@mapbox/vector-tile" "^1.3.1" + "@mapbox/whoots-js" "^3.1.0" + "@types/geojson" "^7946.0.10" + "@types/mapbox__point-geometry" "^0.1.2" + "@types/mapbox__vector-tile" "^1.3.0" + "@types/pbf" "^3.0.2" + csscolorparser "~1.0.3" + earcut "^2.2.4" + geojson-vt "^3.2.1" + gl-matrix "^3.4.3" + global-prefix "^3.0.0" + murmurhash-js "^1.0.0" + pbf "^3.2.1" + potpack "^1.0.2" + quickselect "^2.0.0" + supercluster "^7.1.5" + tinyqueue "^2.0.3" + vt-pbf "^3.1.3" + merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" @@ -3554,7 +3627,7 @@ styled-jsx@5.0.6: resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.6.tgz#fa684790a9cc3badded14badea163418fe568f77" integrity sha512-xOeROtkK5MGMDimBQ3J6iPId8q0t/BDoG5XN6oKkZClVz9ISF/hihN8OCn2LggMU6N32aXnrXBdn3auSqNS9fA== -supercluster@^7.1.4: +supercluster@^7.1.4, supercluster@^7.1.5: version "7.1.5" resolved "https://registry.yarnpkg.com/supercluster/-/supercluster-7.1.5.tgz#65a6ce4a037a972767740614c19051b64b8be5a3" integrity sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg== @@ -3807,6 +3880,13 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"