diff --git a/package-lock.json b/package-lock.json index 7ea0e3ca..d67d7a6b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,18 @@ { - "name": "io-site", + "name": "explore-site", "lockfileVersion": 3, "requires": true, - "packages": {} + "packages": { + "": { + "dependencies": { + "papaparse": "^5.5.2" + } + }, + "node_modules/papaparse": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.2.tgz", + "integrity": "sha512-PZXg8UuAc4PcVwLosEEDYjPyfWnTEhOrUfdv+3Bx+NuAb+5NhDmXzg5fHWmdCh1mP5p7JAZfFr3IMQfcntNAdA==", + "license": "MIT" + } + } } diff --git a/package.json b/package.json new file mode 100644 index 00000000..0f75c778 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "papaparse": "^5.5.2" + } +} diff --git a/site/package-lock.json b/site/package-lock.json index 584e7a84..a6664b2e 100644 --- a/site/package-lock.json +++ b/site/package-lock.json @@ -15,13 +15,15 @@ "@mui/icons-material": "^5.15.21", "@mui/material": "^5.15.20", "apache-arrow": "^15.0.2", - "maplibre-gl": "^4.4.1", + "maplibre-gl": "^5.0.0", + "papaparse": "^5.5.2", "pmtiles": "^3.0.5", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-dropzone": "^14.3.8", "react-floater": "^0.9.3", "react-joyride": "^2.8.2", - "react-map-gl": "^7.1.7" + "react-map-gl": "^8.0.0" }, "devDependencies": { "@types/react": "^18.2.64", @@ -1177,19 +1179,18 @@ } }, "node_modules/@maplibre/maplibre-gl-style-spec": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.3.0.tgz", - "integrity": "sha512-eSiQ3E5LUSxAOY9ABXGyfNhout2iEa6mUxKeaQ9nJ8NL1NuaQYU7zKqzx/LEYcXe1neT4uYAgM1wYZj3fTSXtA==", + "version": "23.2.1", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-23.2.1.tgz", + "integrity": "sha512-SDeCvKyrPqkW1wsoNKRLuE+urRgnYa9qDE/9bVZnz7alNILPPIFPa5jcq1VgFSivnktbnoVZNNXXqNoOUKZYNg==", "license": "ISC", "dependencies": { "@mapbox/jsonlint-lines-primitives": "~2.0.2", "@mapbox/unitbezier": "^0.0.1", "json-stringify-pretty-compact": "^4.0.0", "minimist": "^1.2.8", - "quickselect": "^2.0.0", + "quickselect": "^3.0.0", "rw": "^1.3.3", - "sort-object": "^3.0.3", - "tinyqueue": "^2.0.3" + "tinyqueue": "^3.0.0" }, "bin": { "gl-style-format": "dist/gl-style-format.mjs", @@ -1973,9 +1974,10 @@ "dev": true }, "node_modules/@types/geojson": { - "version": "7946.0.14", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", - "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==" + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" }, "node_modules/@types/geojson-vt": { "version": "3.2.5", @@ -1985,11 +1987,6 @@ "@types/geojson": "*" } }, - "node_modules/@types/junit-report-builder": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/junit-report-builder/-/junit-report-builder-3.0.2.tgz", - "integrity": "sha512-R5M+SYhMbwBeQcNXYWNCZkl09vkVfAtcPIaCGdzIkkbeaTrVbGQ7HVgi4s+EmM/M1K4ZuWQH0jGcvMvNePfxYA==" - }, "node_modules/@types/leaflet": { "version": "1.9.8", "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.8.tgz", @@ -2013,14 +2010,6 @@ "@types/pbf": "*" } }, - "node_modules/@types/mapbox-gl": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-3.1.0.tgz", - "integrity": "sha512-hI6cQDjw1bkJw7MC/eHMqq5TWUamLwsujnUUeiIX2KDRjxRNSYMjnHz07+LATz9I9XIsKumOtUz4gRYnZOJ/FA==", - "dependencies": { - "@types/geojson": "*" - } - }, "node_modules/@types/node": { "version": "20.12.12", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", @@ -2090,6 +2079,66 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "node_modules/@vis.gl/react-mapbox": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/@vis.gl/react-mapbox/-/react-mapbox-8.0.4.tgz", + "integrity": "sha512-NFk0vsWcNzSs0YCsVdt2100Zli9QWR+pje8DacpLkkGEAXFaJsFtI1oKD0Hatiate4/iAIW39SQHhgfhbeEPfQ==", + "license": "MIT", + "peerDependencies": { + "mapbox-gl": ">=3.5.0", + "react": ">=16.3.0", + "react-dom": ">=16.3.0" + }, + "peerDependenciesMeta": { + "mapbox-gl": { + "optional": true + } + } + }, + "node_modules/@vis.gl/react-maplibre": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/@vis.gl/react-maplibre/-/react-maplibre-8.0.4.tgz", + "integrity": "sha512-HwZyfLjEu+y1mUFvwDAkVxinGm8fEegaWN+O8np/WZ2Sqe5Lv6OXFpV6GWz9LOEvBYMbGuGk1FQdejo+4HCJ5w==", + "license": "MIT", + "dependencies": { + "@maplibre/maplibre-gl-style-spec": "^19.2.1" + }, + "peerDependencies": { + "maplibre-gl": ">=4.0.0", + "react": ">=16.3.0", + "react-dom": ">=16.3.0" + }, + "peerDependenciesMeta": { + "maplibre-gl": { + "optional": true + } + } + }, + "node_modules/@vis.gl/react-maplibre/node_modules/@maplibre/maplibre-gl-style-spec": { + "version": "19.3.3", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-19.3.3.tgz", + "integrity": "sha512-cOZZOVhDSulgK0meTsTkmNXb1ahVvmTmWmfx9gRBwc6hq98wS9JP35ESIoNq3xqEan+UN+gn8187Z6E4NKhLsw==", + "license": "ISC", + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "~2.0.2", + "@mapbox/unitbezier": "^0.0.1", + "json-stringify-pretty-compact": "^3.0.0", + "minimist": "^1.2.8", + "rw": "^1.3.3", + "sort-object": "^3.0.3" + }, + "bin": { + "gl-style-format": "dist/gl-style-format.mjs", + "gl-style-migrate": "dist/gl-style-migrate.mjs", + "gl-style-validate": "dist/gl-style-validate.mjs" + } + }, + "node_modules/@vis.gl/react-maplibre/node_modules/json-stringify-pretty-compact": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-3.0.0.tgz", + "integrity": "sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA==", + "license": "MIT" + }, "node_modules/@vitejs/plugin-react": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz", @@ -2195,6 +2244,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -2348,6 +2398,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -2361,6 +2412,15 @@ "has-symbols": "^1.0.3" } }, + "node_modules/attr-accept": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz", + "integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -2473,6 +2533,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/bytewise/-/bytewise-1.1.0.tgz", "integrity": "sha512-rHuuseJ9iQ0na6UDhnrRVDh8YnWVlU6xM3VH6q/+yHDeUH2zIhUzP+2/h3LIrhLDBtTqzWpE3p3tP/boefskKQ==", + "license": "MIT", "dependencies": { "bytewise-core": "^1.2.2", "typewise": "^1.0.3" @@ -2482,6 +2543,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/bytewise-core/-/bytewise-core-1.2.3.tgz", "integrity": "sha512-nZD//kc78OOxeYtRlVk8/zXqTB4gf/nlguL1ggWA8FuchMyOxcyHR4QPQZMUmA7czC+YnaBrPUCubqAWe50DaA==", + "license": "MIT", "dependencies": { "typewise-core": "^1.2" } @@ -2869,9 +2931,10 @@ } }, "node_modules/earcut": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", - "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.1.tgz", + "integrity": "sha512-0l1/0gOjESMeQyYaK5IDiPNvFeu93Z/cO0TjZh9eZ1vyCtZnA7KMZ8rQggpsJHIbGSdrqYq9OhuveadOVHCshw==", + "license": "ISC" }, "node_modules/electron-to-chromium": { "version": "1.4.705", @@ -3410,6 +3473,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "license": "MIT", "dependencies": { "is-extendable": "^0.1.0" }, @@ -3461,6 +3525,18 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-selector": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz", + "integrity": "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==", + "license": "MIT", + "dependencies": { + "tslib": "^2.7.0" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/find-replace": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", @@ -3592,9 +3668,10 @@ } }, "node_modules/geojson-vt": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz", - "integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-4.0.2.tgz", + "integrity": "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==", + "license": "ISC" }, "node_modules/get-intrinsic": { "version": "1.2.4", @@ -3647,6 +3724,7 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -3689,27 +3767,41 @@ } }, "node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-4.0.0.tgz", + "integrity": "sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA==", + "license": "MIT", "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" + "ini": "^4.1.3", + "kind-of": "^6.0.3", + "which": "^4.0.0" }, "engines": { - "node": ">=6" + "node": ">=16" + } + }, + "node_modules/global-prefix/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "license": "ISC", + "engines": { + "node": ">=16" } }, "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "license": "ISC", "dependencies": { - "isexe": "^2.0.0" + "isexe": "^3.1.1" }, "bin": { - "which": "bin/which" + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" } }, "node_modules/globals": { @@ -3919,9 +4011,13 @@ "dev": true }, "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/internal-slot": { "version": "1.0.7", @@ -4043,6 +4139,7 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -4152,6 +4249,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "license": "MIT", "dependencies": { "isobject": "^3.0.1" }, @@ -4296,12 +4394,14 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "node_modules/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -4430,6 +4530,7 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -4513,9 +4614,9 @@ } }, "node_modules/maplibre-gl": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.4.1.tgz", - "integrity": "sha512-tD+wn8qWSLCGhABKBrbewmgFfyopZDz+fkYXeOM8vdBhnf126DvMPyaYGGoKvoF4QuswCsgikETd2c39wK+OQw==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.4.0.tgz", + "integrity": "sha512-ZVrtdFIhFAqt53H2k5Ssqn7QIKNI19fW+He5tr4loxZxWZffp1aZYY9ImNncAJaALU/NYlV6Eul7UVB56/N7WQ==", "license": "BSD-3-Clause", "dependencies": { "@mapbox/geojson-rewind": "^0.5.2", @@ -4525,25 +4626,24 @@ "@mapbox/unitbezier": "^0.0.1", "@mapbox/vector-tile": "^1.3.1", "@mapbox/whoots-js": "^3.1.0", - "@maplibre/maplibre-gl-style-spec": "^20.3.0", - "@types/geojson": "^7946.0.14", + "@maplibre/maplibre-gl-style-spec": "^23.1.0", + "@types/geojson": "^7946.0.16", "@types/geojson-vt": "3.2.5", - "@types/junit-report-builder": "^3.0.2", "@types/mapbox__point-geometry": "^0.1.4", "@types/mapbox__vector-tile": "^1.3.4", "@types/pbf": "^3.0.5", "@types/supercluster": "^7.1.3", - "earcut": "^2.2.4", - "geojson-vt": "^3.2.1", + "earcut": "^3.0.1", + "geojson-vt": "^4.0.2", "gl-matrix": "^3.4.3", - "global-prefix": "^3.0.0", + "global-prefix": "^4.0.0", "kdbush": "^4.0.2", "murmurhash-js": "^1.0.0", - "pbf": "^3.2.1", + "pbf": "^3.3.0", "potpack": "^2.0.0", - "quickselect": "^2.0.0", + "quickselect": "^3.0.0", "supercluster": "^8.0.1", - "tinyqueue": "^2.0.3", + "tinyqueue": "^3.0.0", "vt-pbf": "^3.1.3" }, "engines": { @@ -4786,6 +4886,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/papaparse": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.2.tgz", + "integrity": "sha512-PZXg8UuAc4PcVwLosEEDYjPyfWnTEhOrUfdv+3Bx+NuAb+5NhDmXzg5fHWmdCh1mP5p7JAZfFr3IMQfcntNAdA==", + "license": "MIT" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4855,9 +4961,10 @@ } }, "node_modules/pbf": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", - "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.3.0.tgz", + "integrity": "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==", + "license": "BSD-3-Clause", "dependencies": { "ieee754": "^1.1.12", "resolve-protobuf-schema": "^2.1.0" @@ -4999,9 +5106,9 @@ ] }, "node_modules/quickselect": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", - "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", + "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==", "license": "ISC" }, "node_modules/react": { @@ -5027,6 +5134,23 @@ "react": "^18.2.0" } }, + "node_modules/react-dropzone": { + "version": "14.3.8", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.3.8.tgz", + "integrity": "sha512-sBgODnq+lcA4P296DY4wacOZz3JFpD99fp+hb//iBO2HHnyeZU3FwWyXJ6salNpqQdsZrgMrotuko/BdJMV8Ug==", + "license": "MIT", + "dependencies": { + "attr-accept": "^2.2.4", + "file-selector": "^2.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "react": ">= 16.8 || 18.0.0" + } + }, "node_modules/react-floater": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/react-floater/-/react-floater-0.9.3.tgz", @@ -5130,12 +5254,13 @@ } }, "node_modules/react-map-gl": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/react-map-gl/-/react-map-gl-7.1.7.tgz", - "integrity": "sha512-mwjc0obkBJOXCcoXQr3VoLqmqwo9vS4bXfbGsdxXzEgVCv/PM0v+1QggL7W0d/ccIy+VCjbXNlGij+PENz6VNg==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/react-map-gl/-/react-map-gl-8.0.4.tgz", + "integrity": "sha512-SHdpvFIvswsZBg6BCPcwY/nbKuCo3sJM1Cj7Sd+gA3gFRFOixD+KtZ2XSuUWq2WySL2emYEXEgrLZoXsV4Ut4Q==", + "license": "MIT", "dependencies": { - "@maplibre/maplibre-gl-style-spec": "^19.2.1", - "@types/mapbox-gl": ">=1.0.0" + "@vis.gl/react-mapbox": "8.0.4", + "@vis.gl/react-maplibre": "8.0.4" }, "peerDependencies": { "mapbox-gl": ">=1.13.0", @@ -5152,29 +5277,6 @@ } } }, - "node_modules/react-map-gl/node_modules/@maplibre/maplibre-gl-style-spec": { - "version": "19.3.3", - "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-19.3.3.tgz", - "integrity": "sha512-cOZZOVhDSulgK0meTsTkmNXb1ahVvmTmWmfx9gRBwc6hq98wS9JP35ESIoNq3xqEan+UN+gn8187Z6E4NKhLsw==", - "dependencies": { - "@mapbox/jsonlint-lines-primitives": "~2.0.2", - "@mapbox/unitbezier": "^0.0.1", - "json-stringify-pretty-compact": "^3.0.0", - "minimist": "^1.2.8", - "rw": "^1.3.3", - "sort-object": "^3.0.3" - }, - "bin": { - "gl-style-format": "dist/gl-style-format.mjs", - "gl-style-migrate": "dist/gl-style-migrate.mjs", - "gl-style-validate": "dist/gl-style-validate.mjs" - } - }, - "node_modules/react-map-gl/node_modules/json-stringify-pretty-compact": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-3.0.0.tgz", - "integrity": "sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA==" - }, "node_modules/react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", @@ -5359,7 +5461,8 @@ "node_modules/rw": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" }, "node_modules/safe-array-concat": { "version": "1.1.2", @@ -5459,6 +5562,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "license": "MIT", "dependencies": { "extend-shallow": "^2.0.1", "is-extendable": "^0.1.1", @@ -5522,6 +5626,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/sort-asc/-/sort-asc-0.2.0.tgz", "integrity": "sha512-umMGhjPeHAI6YjABoSTrFp2zaBtXBej1a0yKkuMUyjjqu6FJsTF+JYwCswWDg+zJfk/5npWUUbd33HH/WLzpaA==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5530,6 +5635,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/sort-desc/-/sort-desc-0.2.0.tgz", "integrity": "sha512-NqZqyvL4VPW+RAxxXnB8gvE1kyikh8+pR+T+CXLksVRN9eiQqkQlPwqWYU0mF9Jm7UnctShlxLyAt1CaBOTL1w==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5538,6 +5644,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/sort-object/-/sort-object-3.0.3.tgz", "integrity": "sha512-nK7WOY8jik6zaG9CRwZTaD5O7ETWDLZYMM12pqY8htll+7dYeqGfEUPcUBHOpSJg2vJOrvFIY2Dl5cX2ih1hAQ==", + "license": "MIT", "dependencies": { "bytewise": "^1.1.0", "get-value": "^2.0.2", @@ -5571,6 +5678,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "license": "MIT", "dependencies": { "extend-shallow": "^3.0.0" }, @@ -5582,6 +5690,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "license": "MIT", "dependencies": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" @@ -5594,6 +5703,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "license": "MIT", "dependencies": { "is-plain-object": "^2.0.4" }, @@ -5782,9 +5892,9 @@ "dev": true }, "node_modules/tinyqueue": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", - "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", + "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==", "license": "ISC" }, "node_modules/to-fast-properties": { @@ -5817,9 +5927,10 @@ } }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, "node_modules/type-check": { "version": "0.4.0", @@ -5922,6 +6033,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/typewise/-/typewise-1.0.3.tgz", "integrity": "sha512-aXofE06xGhaQSPzt8hlTY+/YWQhm9P0jYUp1f2XtmW/3Bk0qzXcyFWAtPoo2uTGQj1ZwbDuSyuxicq+aDo8lCQ==", + "license": "MIT", "dependencies": { "typewise-core": "^1.2.0" } @@ -5929,7 +6041,8 @@ "node_modules/typewise-core": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/typewise-core/-/typewise-core-1.2.0.tgz", - "integrity": "sha512-2SCC/WLzj2SbUwzFOzqMCkz5amXLlxtJqDKTICqg30x+2DZxcfZN2MvQZmGfXWKNWaKK9pBPsvkcwv8bF/gxKg==" + "integrity": "sha512-2SCC/WLzj2SbUwzFOzqMCkz5amXLlxtJqDKTICqg30x+2DZxcfZN2MvQZmGfXWKNWaKK9pBPsvkcwv8bF/gxKg==", + "license": "MIT" }, "node_modules/typical": { "version": "4.0.0", @@ -5963,6 +6076,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "license": "MIT", "dependencies": { "arr-union": "^3.1.0", "get-value": "^2.0.6", diff --git a/site/package.json b/site/package.json index f20c3226..09d4cab5 100644 --- a/site/package.json +++ b/site/package.json @@ -18,13 +18,15 @@ "@mui/icons-material": "^5.15.21", "@mui/material": "^5.15.20", "apache-arrow": "^15.0.2", - "maplibre-gl": "^4.4.1", + "maplibre-gl": "^5.0.0", + "papaparse": "^5.5.2", "pmtiles": "^3.0.5", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-dropzone": "^14.3.8", "react-floater": "^0.9.3", "react-joyride": "^2.8.2", - "react-map-gl": "^7.1.7" + "react-map-gl": "^8.0.0" }, "devDependencies": { "@types/react": "^18.2.64", diff --git a/site/src/App.jsx b/site/src/App.jsx index 98739232..3e4e436e 100644 --- a/site/src/App.jsx +++ b/site/src/App.jsx @@ -1,13 +1,15 @@ import "./App.css"; import Header from "./nav/Header"; +import QaBar from "./qa_info_bar/QaBar"; import Map from "./Map"; import { MapProvider } from "react-map-gl/maplibre"; import { getTheme, keepTheme, darkTheme, lightTheme } from "./themeUtils"; -import { useState, useEffect, useRef } from "react"; +import { useState, useEffect, useRef, useCallback } from "react"; import Tour from "./Tour"; import StartupBox from "./StartupBox"; import { ThemeProvider } from "@mui/material"; import { useNavigatorState } from "./navigator/Navigator"; +import { INITIAL_VIEW_STATE } from "./MapLibreMap"; function App() { const [modeName, setModeName] = useState(getTheme()); @@ -20,6 +22,7 @@ function App() { const [zoom, setZoom] = useState(0); const themeRef = useRef(null); const [activeFeature, setActiveFeature] = useState(null); + const [activeOsmFeature, setActiveOsmFeature] = useState(null); const startTour = () => { setOpen(false); @@ -30,9 +33,10 @@ function App() { localStorage.setItem("tour", event.target.checked); setTour(!tour); }; - - const [visibleTypes, setVisibleTypes] = useState([]); + const [viewState, setViewState] = useState(INITIAL_VIEW_STATE); + const onMove = useCallback((evt) => setViewState(evt.viewState), []); + const [visibleTypes, setVisibleTypes] = useState([]); useEffect(() => { keepTheme(setModeName); @@ -71,6 +75,12 @@ function App() { setZoom={setZoom} visibleTypes={visibleTypes} /> + diff --git a/site/src/CompareToggle.css b/site/src/CompareToggle.css new file mode 100644 index 00000000..1848ee06 --- /dev/null +++ b/site/src/CompareToggle.css @@ -0,0 +1,29 @@ +.compare-toggle { + padding: 6px; + background: white; + border: none; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; +} + +.compare-icon { + width: 20px; + height: 20px; + display: block; +} + +.compare-icon svg { + fill: #666; + width: 100%; + height: 100%; +} + +.compare-toggle:hover { + background: #fafafa; +} + +.compare-toggle:hover .compare-icon svg { + fill: #333; +} diff --git a/site/src/CompareToggle.jsx b/site/src/CompareToggle.jsx new file mode 100644 index 00000000..077f53e3 --- /dev/null +++ b/site/src/CompareToggle.jsx @@ -0,0 +1,20 @@ +import CompareIcon from "@mui/icons-material/Compare"; +import PropTypes from "prop-types"; +import "./CompareToggle.css"; + +export default function CompareToggle({ compareMode, setCompareMode }) { + return ( + + ); +} + +CompareToggle.propTypes = { + compareMode: PropTypes.bool.isRequired, + setCompareMode: PropTypes.func.isRequired, +}; diff --git a/site/src/CustomControls.css b/site/src/CustomControls.css index 56152ddd..4b5f1c49 100644 --- a/site/src/CustomControls.css +++ b/site/src/CustomControls.css @@ -41,3 +41,39 @@ a.bug-nub-link { height: 25px; width: 25px; } + +.compare-toggle { + position: absolute; + top: 10px; + right: 50px; + background: white; + border: none; + border-radius: 4px; + padding: 8px; + cursor: pointer; + box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1); + color: var(--ifm-color-content); +} + +.compare-toggle:hover { + background: #f0f0f0; +} + +.compare-icon { + width: 20px; + height: 20px; +} + +/* Add dark mode styles */ +[data-theme="dark"] .bug-nub { + background: var(--ifm-background-surface-color); +} + +[data-theme="dark"] .compare-toggle { + background: var(--ifm-background-surface-color); + color: var(--ifm-color-content); +} + +[data-theme="dark"] .compare-toggle:hover { + background: var(--ifm-color-emphasis-200); +} diff --git a/site/src/FeatureSelector.css b/site/src/FeatureSelector.css index a23dc237..a53cd410 100644 --- a/site/src/FeatureSelector.css +++ b/site/src/FeatureSelector.css @@ -4,28 +4,13 @@ overflow-y: auto; } -.maplibregl-popup-content { - border-radius: 6px; -} - -.theme-light .maplibregl-popup-content { - background-color: rgba(255, 255, 255, 0.9); - color: black; -} - - .theme-dark .maplibregl-popup-content { - background-color: rgba(0, 0, 0, 0.85); - color: white; -} - -.maplibregl-popup-close-button { - color: black; + background-color: #121212; + color: #fff; } - .theme-dark .maplibregl-popup-close-button { - color: white; + color: #fff; } .feature-selector-title { diff --git a/site/src/Layers.js b/site/src/Layers.js index ae1b9c4f..250c65cf 100644 --- a/site/src/Layers.js +++ b/site/src/Layers.js @@ -15,6 +15,7 @@ export const layers = [ outline: true, color: "hsla(195, 71%, 80%, 1)", activeColor: "hsla(195, 77%, 85%, 1)", + labelColor: "hsla(195, 77%, 60%, 1)", }, { theme: "base", diff --git a/site/src/Map.jsx b/site/src/Map.jsx index 1b99209c..a1933a36 100644 --- a/site/src/Map.jsx +++ b/site/src/Map.jsx @@ -1,43 +1,25 @@ -import { - Map as MapLibreMap, - NavigationControl, - Source, - AttributionControl, -} from "react-map-gl/maplibre"; import "maplibre-gl/dist/maplibre-gl.css"; import * as pmtiles from "pmtiles"; import maplibregl from "maplibre-gl"; import { useState, useEffect, useCallback, useRef } from "react"; -import { Layer, GeolocateControl } from "react-map-gl/maplibre"; import InspectorPanel from "./inspector_panel/InspectorPanel"; import PropTypes from "prop-types"; import "./CustomControls.css"; import ThemeSelector from "./ThemeSelector"; import BugIcon from "./icons/icon-bug.svg?react"; import Navigator from "./navigator/Navigator"; -import { layers } from "./Layers"; -import ThemeTypeLayer from "./ThemeTypeLayer"; -import FeaturePopup from "./FeatureSelector"; - -const PMTILES_URL = - "pmtiles://https://d3c1b7bog2u1nn.cloudfront.net/2025-04-23/"; - -const INITIAL_VIEW_STATE = { - latitude: 38.90678, - longitude: -77.036495, - zoom: 15, - bearing: 0, - pitch: 0, -}; +import Map from "./MapLibreMap"; +import CompareToggle from "./CompareToggle"; +import VersionSelector from "./VersionSelector"; +import QaVersionSelector from "./QaVersionSelector"; +import { useComparisonState } from "./util/ComparisonState"; -// this reference must remain constant to avoid re-renders -const MAP_STYLE = { - version: 8, - glyphs: "https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf", - sources: {}, - layers: [], -}; +const getPmtilesUrl = (version) => + `pmtiles://https://d3c1b7bog2u1nn.cloudfront.net/${version.split(".")[0]}/`; + +const getQaPmtilesUrl = (ds) => + `pmtiles://https://dr23tlpzrppt2.cloudfront.net/nightlies/ds=${ds}/`; const ThemeSource = ({ name, url }) => { return ; @@ -48,29 +30,51 @@ ThemeSource.propTypes = { url: PropTypes.string.isRequired, }; -export default function Map({ +export default function MapContainer({ mode, features, setFeatures, activeFeature, setActiveFeature, + activeOsmFeature, setZoom, navigatorOpen, setNavigatorOpen, themeRef, - visibleTypes, - setVisibleTypes, + viewState, + setViewState, + onMove }) { - const mapRef = useRef(); + const leftMapRef = useRef(); + const rightMapRef = useRef(); const [cursor, setCursor] = useState("auto"); + + const { + compareMode, + setCompareMode, + activeMap, + setActiveMap, + leftMapStyle, + rightMapStyle, + leftVersion, + setLeftVersion, + rightVersion, + setRightVersion, + leftQaVersion, + setLeftQaVersion, + rightQaVersion, + setRightQaVersion, + } = useComparisonState(); + const [activeThemes, setActiveThemes] = useState([ - "places", + "base", "addresses", "buildings", "transportation", ]); + const [visibleTypes, setVisibleTypes] = useState([]); const [interactiveLayerIds, setInteractiveLayerIds] = useState([]); const [lastClickedCoords, setLastClickedCoords] = useState(); @@ -82,7 +86,7 @@ export default function Map({ }, [activeThemes]); const syncInteractiveLayerIds = useCallback(() => { - const layers = mapRef.current.getStyle().layers; + const layers = leftMapRef.current.getStyle().layers; const layersToShow = layers .filter((layer) => { return visibleTypes.indexOf(layer["source-layer"]) >= 0; @@ -115,24 +119,32 @@ export default function Map({ }, []); useEffect(() => { - window.map = mapRef.current; + window.map = leftMapRef.current; }); const activeFeatureRef = useRef(null); + const highlightedOsmFeaturesRef = useRef([]); useEffect(() => { // Remove feature state from previous active feature if (activeFeatureRef.current) { - mapRef.current.removeFeatureState({ + leftMapRef.current.removeFeatureState({ source: activeFeatureRef.current.source, sourceLayer: activeFeatureRef.current.sourceLayer, id: activeFeatureRef.current.id, }); + if (rightMapRef.current) { + rightMapRef.current.removeFeatureState({ + source: activeFeatureRef.current.source, + sourceLayer: activeFeatureRef.current.sourceLayer, + id: activeFeatureRef.current.id, + }); + } } // Set feature state for new active feature if (activeFeature) { - mapRef.current.setFeatureState( + leftMapRef.current.setFeatureState( { source: activeFeature.source, sourceLayer: activeFeature.sourceLayer, @@ -140,11 +152,76 @@ export default function Map({ }, { selected: true } ); + if (rightMapRef.current) { + rightMapRef.current.setFeatureState( + { + source: activeFeature.source, + sourceLayer: activeFeature.sourceLayer, + id: activeFeature.id, + }, + { selected: true } + ); + } } activeFeatureRef.current = activeFeature; }, [activeFeature]); + + useEffect(() => { + + // Remove highlighted feature state from previous osm features + if (highlightedOsmFeaturesRef.current) { + highlightedOsmFeaturesRef.current.forEach(feature => { + leftMapRef.current.removeFeatureState({ + source: feature.source, + sourceLayer: feature.sourceLayer, + id: feature.id, + }); + if (rightMapRef.current) { + rightMapRef.current.removeFeatureState({ + source: feature.source, + sourceLayer: feature.sourceLayer, + id: feature.id, + }); + } + }) + } + + // If there is an active OSM Feature to highlight, we should find all the features that reference it + // and give them the 'highlighted' state. + if (activeOsmFeature){ + const features =leftMapRef.current.queryRenderedFeatures(); + + const filteredFeatures = features?.filter(feature => feature?.properties?.sources?.includes(activeOsmFeature)); + highlightedOsmFeaturesRef.current = filteredFeatures; + + filteredFeatures.forEach(feature => { + leftMapRef.current.setFeatureState( + { + source: feature.source, + sourceLayer: feature.sourceLayer, + id: feature.id, + }, + { highlighted: true} + ); + + if (rightMapRef.current){ + rightMapRef.current.setFeatureState( + { + source: feature.source, + sourceLayer: feature.sourceLayer, + id: feature.id, + }, + { highlighted: true} + ); + } + }) + } + + }, [activeOsmFeature]); + + const onClick = useCallback( (event) => { setLastClickedCoords({ @@ -183,172 +260,98 @@ export default function Map({ return ( <>
- - - - - - - - - + {compareMode && ( + <> + + + + + + )} + { + setActiveMap("left"); + onClick(event); + }} + onMove={onMove} + cursor={cursor} + onZoom={handleZoom} + interactiveLayerIds={interactiveLayerIds} + style={leftMapStyle} + mapId="leftMap" + mode={mode} + visibleTypes={visibleTypes} + activeThemes={activeThemes} + PMTILES_URL={getPmtilesUrl(leftVersion)} + QA_PMTILES_URL={getQaPmtilesUrl(leftQaVersion)} + showControls={!compareMode} + lastClickedCoords={lastClickedCoords} features={features} - onClose={() => setLastClickedCoords(null)} setActiveFeature={setActiveFeature} activeFeature={activeFeature} + enableFeatureSelector={activeMap === "left"} /> +
- {[false, true].map((label) => { - return layers.map((props, i) => ( - - )); - })} - - - - - - + {compareMode && ( +
+ + + { + setActiveMap("right"); + onClick(event); + }} + cursor={cursor} + onZoom={handleZoom} + onMove={onMove} + interactiveLayerIds={interactiveLayerIds} + style={rightMapStyle} + mapId="rightMap" + mode={mode} + visibleTypes={visibleTypes} + activeThemes={activeThemes} + PMTILES_URL={getPmtilesUrl(rightVersion)} + QA_PMTILES_URL={getQaPmtilesUrl(rightQaVersion)} + showControls={true} + lastClickedCoords={lastClickedCoords} + setLastClickedCoords={setLastClickedCoords} + features={features} + setActiveFeature={setActiveFeature} + activeFeature={activeFeature} + enableFeatureSelector={activeMap === "right"} + /> +
+ )}
- - {features.length > 0 && ( + />
@@ -380,6 +383,10 @@ export default function Map({
+ ); diff --git a/site/src/MapLibreMap.jsx b/site/src/MapLibreMap.jsx new file mode 100644 index 00000000..dc118f81 --- /dev/null +++ b/site/src/MapLibreMap.jsx @@ -0,0 +1,232 @@ +import { + Map as MapLibreMap, + NavigationControl, + GeolocateControl, + AttributionControl, + Source, +} from "react-map-gl/maplibre"; +import ThemeTypeLayer from "./ThemeTypeLayer"; +import { Layer } from "react-map-gl/maplibre"; +import PropTypes from "prop-types"; +import { layers } from "./Layers"; +import FeatureSelector from "./FeatureSelector"; + +const ThemeSource = ({ name, url }) => { + return ; +}; + +ThemeSource.propTypes = { + name: PropTypes.string.isRequired, + url: PropTypes.string.isRequired, +}; + +export const INITIAL_VIEW_STATE = { + latitude: 48.72776, + longitude: 4.57531, + zoom: 17, + bearing: 0, + pitch: 0, +}; + +// this reference must remain constant to avoid re-renders +const MAP_STYLE = { + version: 8, + glyphs: "https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf", + sources: {}, + layers: [], +}; + +export default function Map({ + viewState, + mapRef, + onMouseEnter, + onMouseLeave, + onLoad, + onClick, + cursor, + onZoom, + padding, + onMoveStart, + onMove, + interactiveLayerIds, + style, + mapId, + mode, + visibleTypes, + activeThemes, + PMTILES_URL, + QA_PMTILES_URL, + showControls, + lastClickedCoords, + features, + setActiveFeature, + activeFeature, + enableFeatureSelector, + setLastClickedCoords, +}) { + return ( + + + + + + + + + {enableFeatureSelector && ( + setLastClickedCoords(null)} + setActiveFeature={setActiveFeature} + activeFeature={activeFeature} + /> + )} + + {[false, true].map((label) => { + return layers.map((props, i) => ( + + )); + })} + + + + {showControls && ( + <> + + + + + )} + + ); +} diff --git a/site/src/QaVersionSelector.css b/site/src/QaVersionSelector.css new file mode 100644 index 00000000..e459cfc9 --- /dev/null +++ b/site/src/QaVersionSelector.css @@ -0,0 +1,16 @@ +.qa-version-selector { + position: absolute; + top: 10px; + z-index: 1; + padding: 8px; + border-radius: 4px; + border: 1px solid #ccc; + background: white; +} + +/* Add dark mode support */ +[data-theme="dark"] .qa-version-selector { + background: #1a1a1a; + border-color: #404040; + color: #ffffff; +} diff --git a/site/src/QaVersionSelector.jsx b/site/src/QaVersionSelector.jsx new file mode 100644 index 00000000..b5f16d05 --- /dev/null +++ b/site/src/QaVersionSelector.jsx @@ -0,0 +1,26 @@ +import PropTypes from "prop-types"; +import "./QaVersionSelector.css"; +import { TWO_WEEKS_DS } from "./VersionConstants"; + +export default function QaVersionSelector({ version, onChange, style }) { + return ( + + ); +} + +QaVersionSelector.propTypes = { + version: PropTypes.string.isRequired, + onChange: PropTypes.func.isRequired, + style: PropTypes.object, +}; diff --git a/site/src/ThemeSelector.jsx b/site/src/ThemeSelector.jsx index 8a3b6f03..6d201094 100644 --- a/site/src/ThemeSelector.jsx +++ b/site/src/ThemeSelector.jsx @@ -97,6 +97,9 @@ const ThemeSelector = ({ if (!newSelectedThemes[layer.theme]) { newSelectedThemes[layer.theme] = true; } + if (layer.type === 'division_area' || layer.type === 'division_boundary'){ + return; + } newSelectedTypes[layer.type] = true; }); @@ -244,6 +247,7 @@ const ThemeSelector = ({ className={`theme-box ${ mode === "theme-dark" ? "dark" : "light" }`} + key={theme} sx={{ paddingLeft: "5px" }} width={220} > @@ -294,6 +298,7 @@ const ThemeSelector = ({ className={`type-selector-checkbox ${ mode === "theme-dark" ? "dark" : "light" }`} + key={`${layer.theme}-${layer.type}`} control={ { "case", ["boolean", ["feature-state", "selected"], false], highlightColor || "white", + ["boolean", ["feature-state", "highlighted"], false], + "pink", color, ]; }; diff --git a/site/src/VersionConstants.js b/site/src/VersionConstants.js new file mode 100644 index 00000000..c54eab35 --- /dev/null +++ b/site/src/VersionConstants.js @@ -0,0 +1,31 @@ + +function getLastTwoWeeksDateStamps() { + const dateStamps = []; + const today = new Date(); + + for (let i = 0; i < 14; i++) { + const pastDate = new Date(today); + pastDate.setDate(today.getDate() - i); + const year = pastDate.getFullYear(); + const month = String(pastDate.getMonth() + 1).padStart(2, '0'); + const day = String(pastDate.getDate()).padStart(2, '0'); + dateStamps.push(`${year}-${month}-${day}`); + } + + return dateStamps; +} + +export const TWO_WEEKS_DS = getLastTwoWeeksDateStamps(); + +// TODO: Get from manifest once the generation is finalized! +export const VERSION_OPTIONS = [ + "2025-03-19.1", + "2025-02-19.0", + "2025-01-22.0", + "2024-12-18.0", + "2024-11-13.0", + "2024-10-23.0", + "2024-09-18.0", + "2024-08-20.0", + "2024-07-22.0", +]; \ No newline at end of file diff --git a/site/src/VersionSelector.css b/site/src/VersionSelector.css new file mode 100644 index 00000000..28ad87ea --- /dev/null +++ b/site/src/VersionSelector.css @@ -0,0 +1,16 @@ +.version-selector { + position: absolute; + top: 10px; + z-index: 1; + padding: 8px; + border-radius: 4px; + border: 1px solid #ccc; + background: white; +} + +/* Add dark mode support */ +[data-theme="dark"] .version-selector { + background: #1a1a1a; + border-color: #404040; + color: #ffffff; +} diff --git a/site/src/VersionSelector.jsx b/site/src/VersionSelector.jsx new file mode 100644 index 00000000..15eaa319 --- /dev/null +++ b/site/src/VersionSelector.jsx @@ -0,0 +1,26 @@ +import PropTypes from "prop-types"; +import "./VersionSelector.css"; +import { VERSION_OPTIONS } from "./VersionConstants"; + +export default function VersionSelector({ version, onChange, style }) { + return ( + + ); +} + +VersionSelector.propTypes = { + version: PropTypes.string.isRequired, + onChange: PropTypes.func.isRequired, + style: PropTypes.object, +}; diff --git a/site/src/index.css b/site/src/index.css index eb1304a0..3309d758 100644 --- a/site/src/index.css +++ b/site/src/index.css @@ -35,6 +35,10 @@ body { min-height: 100vh; } +[data-theme="dark"] body { + background: #1a1a1a; +} + h1 { font-size: 3.2em; line-height: 1.1; diff --git a/site/src/inspector_panel/InspectorPanel.jsx b/site/src/inspector_panel/InspectorPanel.jsx index f9931a4f..444ce02c 100644 --- a/site/src/inspector_panel/InspectorPanel.jsx +++ b/site/src/inspector_panel/InspectorPanel.jsx @@ -121,7 +121,6 @@ function InspectorPanel({