diff --git a/client/cypress/e2e/latlngurl.spec.js b/client/cypress/e2e/latlngurl.spec.js index a3a1f56c..671e7403 100644 --- a/client/cypress/e2e/latlngurl.spec.js +++ b/client/cypress/e2e/latlngurl.spec.js @@ -4,7 +4,7 @@ describe('LatLng Test', () => { it('Checks that as the map zooms the lat/lng coordinates in the URL update', () => { cy.visit('http://localhost:8000/en/cejst?flags=mb'); cy.url().should('include', '#3'); - cy.get('.mapboxgl-ctrl-zoom-in > .mapboxgl-ctrl-icon').click(); + cy.get('.maplibregl-ctrl-zoom-in > .maplibregl-ctrl-icon').click(); cy.url().should('include', '#4'); cy.getMap().then((map) => { cy.panTo(map, [-77.9, 35.04]); diff --git a/client/cypress/support/commands.js b/client/cypress/support/commands.js index d16eb064..af290115 100644 --- a/client/cypress/support/commands.js +++ b/client/cypress/support/commands.js @@ -3,7 +3,7 @@ // For some interactions, we need access to the underlying map // Below adapted from https://github.com/codeforcologne/edelgard-map Cypress.Commands.add('getMap', () => { - return cy.window().its('mapboxGlMap'); + return cy.window().its('underlyingMap'); }); Cypress.Commands.add('waitForMove', (map) => { @@ -18,6 +18,6 @@ Cypress.Commands.add('panTo', (map, lngLat) => { }); Cypress.Commands.add('getMapCanvas', () => { - return cy.get('.mapboxgl-canvas'); + return cy.get('.maplibregl-canvas'); }); diff --git a/client/package-lock.json b/client/package-lock.json index 22646c64..0de7e06c 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -2344,26 +2344,6 @@ "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", "integrity": "sha1-zlblOfg1UrWNENZy6k1vya3HsjQ=" }, - "@mapbox/mapbox-gl-style-spec": { - "version": "13.20.1", - "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-style-spec/-/mapbox-gl-style-spec-13.20.1.tgz", - "integrity": "sha512-xVCJ3IbKoPwcPrxxtGAxUqHEVxXi1hnJtLIFqgkuZfnzj0KeRbk3dZlDr/KNo1/doJjIoFgPFUO/HMOT+wXGPA==", - "requires": { - "@mapbox/jsonlint-lines-primitives": "~2.0.2", - "@mapbox/point-geometry": "^0.1.0", - "@mapbox/unitbezier": "^0.0.0", - "csscolorparser": "~1.0.2", - "json-stringify-pretty-compact": "^2.0.0", - "minimist": "^1.2.5", - "rw": "^1.3.3", - "sort-object": "^0.3.2" - } - }, - "@mapbox/mapbox-gl-supported": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.5.0.tgz", - "integrity": "sha512-/PT1P6DNf7vjEEiPkVIRJkvibbqWtqnyGaBz3nfRdcxclNSnSdaLU5tfAgcD7I8Yt5i+L19s406YLl1koLnLbg==" - }, "@mapbox/point-geometry": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", @@ -2655,12 +2635,6 @@ "integrity": "sha512-M2BiThcbxMxSKX8W4z5u9jKZn6datnM3+FpEU+eYw0//l31E2xhqi7vTAuJ/Sf0P3yhp66SDJgPu3bRRpvrdQQ==", "dev": true }, - "@types/arcgis-rest-api": { - "version": "10.4.4", - "resolved": "https://registry.npmjs.org/@types/arcgis-rest-api/-/arcgis-rest-api-10.4.4.tgz", - "integrity": "sha512-5NwSfj4po+03fauyr4F5AxYzu8pbbqmxay+pNr5ef2V3Mj+7OylvV48VKuVoO9m799jhZdH3EQgQBHm3Y6q1Sw==", - "dev": true - }, "@types/aria-query": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.1.tgz", @@ -2783,9 +2757,9 @@ "dev": true }, "@types/geojson": { - "version": "7946.0.7", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz", - "integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==", + "version": "7946.0.8", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz", + "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==", "dev": true }, "@types/get-port": { @@ -2902,10 +2876,10 @@ "integrity": "sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q==", "dev": true }, - "@types/mapbox-gl": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-2.3.0.tgz", - "integrity": "sha512-Vf5f/jWOgSKbQCm1q0KA7n2DrHZHEWT1NvKuWZl5k7LTYaw8LkZXe9AatWD16t5pKFNBVRpg2VVNXX89d7hyPQ==", + "@types/maplibre-gl": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@types/maplibre-gl/-/maplibre-gl-1.13.1.tgz", + "integrity": "sha512-AD7gFneLUYOWKhiwUvfD3LmQy4agJ2k2fe2V6XWCmt6R41d1q1H+BuyHJxFg31OTtDCag4u9ZZpQbjUmUG0gjQ==", "dev": true, "requires": { "@types/geojson": "*" @@ -2955,18 +2929,6 @@ } } }, - "@types/ol": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@types/ol/-/ol-6.5.1.tgz", - "integrity": "sha512-SHQjTKZ0s5EvhSnI0nyXEhmS/Ez+045c0TvSad0bZmwgldOsVTCG4b43G7q9dHjN+FQIET1Y4s/i2JFDsEZqEA==", - "dev": true, - "requires": { - "@types/arcgis-rest-api": "*", - "@types/geojson": "*", - "@types/rbush": "*", - "@types/topojson-specification": "*" - } - }, "@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -2985,12 +2947,6 @@ "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", "dev": true }, - "@types/rbush": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/rbush/-/rbush-3.0.0.tgz", - "integrity": "sha512-W3ue/GYWXBOpkRm0VSoifrP3HV0Ni47aVJWvXyWMcbtpBy/l/K/smBRiJ+fI8f7shXRjZBiux+iJzYbh7VmcZg==", - "dev": true - }, "@types/reach__router": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.8.tgz", @@ -3097,15 +3053,6 @@ "integrity": "sha1-EHPEvIJHVK49EM+riKsCN7qWTk0=", "dev": true }, - "@types/topojson-specification": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/topojson-specification/-/topojson-specification-1.0.1.tgz", - "integrity": "sha512-ZZYZUgkmUls9Uhxx2WZNt9f/h2+H3abUUjOVmq+AaaDFckC5oAwd+MDp95kBirk+XCXrYj0hfpI6DSUiJMrpYQ==", - "dev": true, - "requires": { - "@types/geojson": "*" - } - }, "@types/unist": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", @@ -13083,11 +13030,6 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, - "json-stringify-pretty-compact": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-2.0.0.tgz", - "integrity": "sha512-WRitRfs6BGq4q8gTgOy4ek7iPFXjbra0H3PmDLKm2xnZ+Gh1HUhiKGgCZkSPNULlP7mvfu6FV/mOLhCarspADQ==" - }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -13718,10 +13660,10 @@ "object-visit": "^1.0.0" } }, - "mapbox-gl": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.13.1.tgz", - "integrity": "sha512-GSyubcoSF5MyaP8z+DasLu5v7KmDK2pp4S5+VQ5WdVQUOaAqQY4jwl4JpcdNho3uWm2bIKs7x1l7q3ynGmW60g==", + "maplibre-gl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-1.15.0.tgz", + "integrity": "sha512-C3Mq7HDTndvAs8w+Ai1QzvVdN7xG2+2iHjtp3Pkmk7tJeSMcqZzQYHKyOCBkpTs7g2P/aFqMU8Tg853RIZxIZg==", "requires": { "@mapbox/geojson-rewind": "^0.5.0", "@mapbox/geojson-types": "^1.0.2", @@ -13746,13 +13688,15 @@ "supercluster": "^7.1.0", "tinyqueue": "^2.0.3", "vt-pbf": "^3.1.1" + }, + "dependencies": { + "@mapbox/mapbox-gl-supported": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.5.0.tgz", + "integrity": "sha512-/PT1P6DNf7vjEEiPkVIRJkvibbqWtqnyGaBz3nfRdcxclNSnSdaLU5tfAgcD7I8Yt5i+L19s406YLl1koLnLbg==" + } } }, - "mapbox-to-css-font": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/mapbox-to-css-font/-/mapbox-to-css-font-2.4.0.tgz", - "integrity": "sha512-v674D0WtpxCXlA6E+sBlG1QJWdUkz/s9qAD91bJSXBGuBL5lL4tJXpoJEftecphCh2SVQCjWMS2vhylc3AIQTg==" - }, "markdown-escapes": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz", @@ -14727,26 +14671,6 @@ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "dev": true }, - "ol": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/ol/-/ol-6.5.0.tgz", - "integrity": "sha512-a5ebahrjF5yCPFle1rc0aHzKp/9A4LlUnjh+S3I+x4EgcvcddDhpOX3WDOs0Pg9/wEElrikHSGEvbeej2Hh4Ug==", - "requires": { - "ol-mapbox-style": "^6.1.1", - "pbf": "3.2.1", - "rbush": "^3.0.1" - } - }, - "ol-mapbox-style": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/ol-mapbox-style/-/ol-mapbox-style-6.3.2.tgz", - "integrity": "sha512-itWZuwZHilztRM9983WmJ+ounaXIS0PdXF8h5xJd7cJhSv02M27w4RQkhiUw35/VLlUdTT/ei3KYi0w2TGDw2A==", - "requires": { - "@mapbox/mapbox-gl-style-spec": "^13.14.0", - "mapbox-to-css-font": "^2.4.0", - "webfont-matcher": "^1.1.0" - } - }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -16088,14 +16012,6 @@ } } }, - "rbush": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", - "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", - "requires": { - "quickselect": "^2.0.0" - } - }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -18017,16 +17933,6 @@ } } }, - "sort-asc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/sort-asc/-/sort-asc-0.1.0.tgz", - "integrity": "sha1-q3md9h/HPqCVbHnEtTHtHp53J+k=" - }, - "sort-desc": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/sort-desc/-/sort-desc-0.1.1.tgz", - "integrity": "sha1-GYuMDN6wlcRjNBhh45JdTuNZqe4=" - }, "sort-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", @@ -18044,15 +17950,6 @@ } } }, - "sort-object": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/sort-object/-/sort-object-0.3.2.tgz", - "integrity": "sha1-mODRme3kDgfGGoRAPGHWw7KQ+eI=", - "requires": { - "sort-asc": "^0.1.0", - "sort-desc": "^0.1.1" - } - }, "source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -19902,11 +19799,6 @@ "minimalistic-assert": "^1.0.0" } }, - "webfont-matcher": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/webfont-matcher/-/webfont-matcher-1.1.0.tgz", - "integrity": "sha1-mM6VCXsp4x++czBT4Q5XFkLRxsc=" - }, "webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", diff --git a/client/package.json b/client/package.json index d2dfa08b..1d36b771 100644 --- a/client/package.json +++ b/client/package.json @@ -34,9 +34,8 @@ "@testing-library/react": "^11.2.7", "@types/chroma-js": "^2.1.3", "@types/jest": "^26.0.23", - "@types/mapbox-gl": "^2.3.0", + "@types/maplibre-gl": "^1.13.1", "@types/node": "^15.3.1", - "@types/ol": "^6.5.1", "@types/react": "^17.0.1", "@types/react-dom": "^17.0.1", "@types/react-helmet": "^6.1.1", @@ -72,9 +71,7 @@ "dependencies": { "@trussworks/react-uswds": "git+https://www.github.com/nathillardusds/react-uswds#nathillardusds/ssr", "chroma-js": "^2.1.2", - "mapbox-gl": "^1.13.0", - "ol": "^6.5.0", - "ol-mapbox-style": "^6.3.2", + "maplibre-gl": ">=1.14.0", "query-string": "^7.0.0", "react": "^17.0.1", "react-dom": "^17.0.1", diff --git a/client/src/components/mapboxMap.module.scss b/client/src/components/J40Map.module.scss similarity index 100% rename from client/src/components/mapboxMap.module.scss rename to client/src/components/J40Map.module.scss diff --git a/client/src/components/J40Map.module.scss.d.ts b/client/src/components/J40Map.module.scss.d.ts new file mode 100644 index 00000000..ec1e79e0 --- /dev/null +++ b/client/src/components/J40Map.module.scss.d.ts @@ -0,0 +1,13 @@ +declare namespace J40MapModuleScssNamespace { + export interface IJ40MapModuleScss { + mapContainer: string; + j40Popup: string; + } +} + +declare const J40MapModuleScssModule: J40MapModuleScssNamespace.IJ40MapModuleScss & { + /** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */ + locals: J40MapModuleScssNamespace.IJ40MapModuleScss; +}; + +export = J40MapModuleScssModule; diff --git a/client/src/components/mapboxMap.tsx b/client/src/components/J40Map.tsx similarity index 88% rename from client/src/components/mapboxMap.tsx rename to client/src/components/J40Map.tsx index 5571e071..219bb62d 100644 --- a/client/src/components/mapboxMap.tsx +++ b/client/src/components/J40Map.tsx @@ -1,29 +1,28 @@ /* eslint-disable no-unused-vars */ import React, {useRef, useEffect, useState} from 'react'; -import {LngLatBoundsLike, +import maplibregl, {LngLatBoundsLike, Map, NavigationControl, PopupOptions, Popup, - LngLatLike} from 'mapbox-gl'; + LngLatLike} from 'maplibre-gl'; +import 'maplibre-gl/dist/maplibre-gl.css'; import mapStyle from '../data/mapStyle'; import ZoomWarning from './zoomWarning'; import PopupContent from './popupContent'; import * as constants from '../data/constants'; import ReactDOM from 'react-dom'; -import 'mapbox-gl/dist/mapbox-gl.css'; -import * as styles from './mapboxMap.module.scss'; +import * as styles from './J40Map.module.scss'; declare global { interface Window { Cypress?: object; - mapboxGlMap: Map; + underlyingMap: Map; } } +type ClickEvent = maplibregl.MapMouseEvent & maplibregl.EventData; -type ClickEvent = mapboxgl.MapMouseEvent & mapboxgl.EventData; - -const MapboxMap = () => { +const J40Map = () => { const mapContainer = React.useRef(null); const map = useRef() as React.MutableRefObject; const [zoom, setZoom] = useState(constants.GLOBAL_MIN_ZOOM); @@ -38,7 +37,6 @@ const MapboxMap = () => { center: constants.DEFAULT_CENTER as LngLatLike, zoom: zoom, minZoom: constants.GLOBAL_MIN_ZOOM, - maxZoom: constants.GLOBAL_MAX_ZOOM, maxBounds: constants.GLOBAL_MAX_BOUNDS as LngLatBoundsLike, hash: true, // Adds hash of zoom/lat/long to the url }); @@ -52,7 +50,7 @@ const MapboxMap = () => { initialMap.on('load', () => { if (window.Cypress) { - window.mapboxGlMap = initialMap; + window.underlyingMap = initialMap; } }); @@ -104,4 +102,4 @@ const MapboxMap = () => { ); }; -export default MapboxMap; +export default J40Map; diff --git a/client/src/components/mapControls.module.scss b/client/src/components/mapControls.module.scss deleted file mode 100644 index c738e696..00000000 --- a/client/src/components/mapControls.module.scss +++ /dev/null @@ -1,3 +0,0 @@ -.mapControlContainer { - margin: 18.5px 42px 23px 42px; -} diff --git a/client/src/components/mapControls.module.scss.d.ts b/client/src/components/mapControls.module.scss.d.ts deleted file mode 100644 index 85999a0d..00000000 --- a/client/src/components/mapControls.module.scss.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -declare namespace MapControlModuleScssNamespace { - export interface IMapControlModuleScss { - mapControlContainer: string; - } -} - -declare const MapControlModuleScssModule: MapControlModuleScssNamespace.IMapControlModuleScss & { - /** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */ - locals: MapControlModuleScssNamespace.IMapControlModuleScss; -}; - -export = MapControlModuleScssModule; diff --git a/client/src/components/mapControls.tsx b/client/src/components/mapControls.tsx deleted file mode 100644 index fe87713e..00000000 --- a/client/src/components/mapControls.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import {Button, ButtonGroup} from '@trussworks/react-uswds'; -import Feature from 'ol/Feature'; -import Geometry from 'ol/geom/Geometry'; -import * as styles from './mapControls.module.scss'; - -interface IMapControlsProps { - setFeatures: (arg0: Feature[]) => void; -} - -const MapControls = ({setFeatures}: IMapControlsProps) => { - return ( - <> -
-

Explore the Tool

- - - - - -
- - ); -}; - -export default MapControls; diff --git a/client/src/components/mapWrapper.tsx b/client/src/components/mapWrapper.tsx index 41c9b28d..1768cb1b 100644 --- a/client/src/components/mapWrapper.tsx +++ b/client/src/components/mapWrapper.tsx @@ -1,16 +1,11 @@ import * as React from 'react'; -import {useFlags} from '../contexts/FlagContext'; -import MapboxMap from './mapboxMap'; -import OpenLayersMap from './openlayersMap'; +import J40Map from './J40Map'; const MapWrapper = () => { - const flags = useFlags(); return (
{ - flags.includes('mb') ? - : - + }
); diff --git a/client/src/components/mapboxMap.module.scss.d.ts b/client/src/components/mapboxMap.module.scss.d.ts deleted file mode 100644 index 15389f65..00000000 --- a/client/src/components/mapboxMap.module.scss.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -declare namespace MapboxMapModuleScssNamespace { - export interface IMapboxMapModuleScss { - mapContainer: string; - j40Popup: string; - } -} - -declare const MapboxMapModuleScssModule: MapboxMapModuleScssNamespace.IMapboxMapModuleScss & { - /** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */ - locals: MapboxMapModuleScssNamespace.IMapboxMapModuleScss; -}; - -export = MapboxMapModuleScssModule; diff --git a/client/src/components/openlayersMap.module.scss b/client/src/components/openlayersMap.module.scss deleted file mode 100644 index d81b910c..00000000 --- a/client/src/components/openlayersMap.module.scss +++ /dev/null @@ -1,6 +0,0 @@ -.mapContainer { - height: 676px; - margin-bottom: 29px; - max-width: revert; - margin-top: 50px; -} diff --git a/client/src/components/openlayersMap.module.scss.d.ts b/client/src/components/openlayersMap.module.scss.d.ts deleted file mode 100644 index 9e20d4b3..00000000 --- a/client/src/components/openlayersMap.module.scss.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -declare namespace MapModuleScssNamespace { - export interface IMapModuleScss { - mapContainer:string; - } -} - -declare const MapModuleScssModule: MapModuleScssNamespace.IMapModuleScss & { - /** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */ - locals: MapModuleScssNamespace.IMapModuleScss; -}; - -export = MapModuleScssModule; diff --git a/client/src/components/openlayersMap.tsx b/client/src/components/openlayersMap.tsx deleted file mode 100644 index 19dd21de..00000000 --- a/client/src/components/openlayersMap.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import React, {useState, useEffect, useRef} from 'react'; -import Map from 'ol/Map'; -import View from 'ol/View'; -import Feature, {FeatureLike} from 'ol/Feature'; -import Geometry from 'ol/geom/Geometry'; -import VectorLayer from 'ol/layer/Vector'; -import VectorSource from 'ol/source/Vector'; -import {fromLonLat} from 'ol/proj'; -import {Coordinate} from 'ol/coordinate'; -import olms from 'ol-mapbox-style'; -import mapStyle from '../data/mapStyle'; -import ZoomWarning from './zoomWarning'; -import OpenlayersPopup from './openlayersPopup'; -import {transformExtent} from 'ol/src/proj'; -import * as styles from './openlayersMap.module.scss'; -import * as constants from '../data/constants'; -import {Extent} from 'ol/src/extent'; - -interface IMapWrapperProps { - features: Feature[], -} - -// The below adapted from -// https://taylor.callsen.me/using-openlayers-with-react-functional-components/ -const MapWrapper = ({features}: IMapWrapperProps) => { - const [map, setMap] = useState(); - const [featuresLayer, setFeaturesLayer] = useState(); - const [selectedFeature, setSelectedFeature] = useState(); - const [currentZoom, setCurrentZoom] = useState(4); - const [currentOverlayPosition, setCurrentOverlayPosition] = useState([]); - - const mapElement = useRef() as - React.MutableRefObject; - - // create state ref that can be accessed in OpenLayers onclick callback function - // https://stackoverflow.com/a/60643670 - const mapRef = useRef() as React.MutableRefObject; - if (map) { - mapRef.current = map; - } - - const transform = (extent: Extent) : Extent => { - return transformExtent(extent, 'EPSG:4326', 'EPSG:3857'); - }; - - - useEffect( () => { - const view = new View({ - center: fromLonLat(constants.DEFAULT_CENTER), - zoom: 4, - maxZoom: constants.GLOBAL_MAX_ZOOM, - minZoom: constants.GLOBAL_MIN_ZOOM, - extent: transform(constants.GLOBAL_MAX_BOUNDS.flat()) as [number, number, number, number], - }); - - // create and add initial vector source layer, to be replaced layer - const initialFeaturesLayer = new VectorLayer({ - source: new VectorSource(), - }); - - const initialMap = new Map({ - target: mapElement.current, - view: view, - controls: [], - }); - const currentZoom = Math.floor(initialMap.getView().getZoom() || constants.GLOBAL_MIN_ZOOM); - - initialMap.on('moveend', handleMoveEnd); - initialMap.on('click', handleMapClick); - setMap(initialMap); - setCurrentZoom(currentZoom); - setFeaturesLayer(initialFeaturesLayer); - olms(initialMap, mapStyle); - }, []); - - - // update map if features prop changes - useEffect( () => { - if (features.length) { // may be empty on first render - // set features to map - featuresLayer?.setSource( - new VectorSource({ - features: features, - }), - ); - const extent = featuresLayer?.getSource().getExtent(); - if (extent) { - // fit map to feature extent (with 100px of padding) - map?.getView().fit(extent, { - padding: [100, 100, 100, 100], - }); - } - } - }, [features]); - - const handleMapClick = (event: { pixel: any }) => { - const clickedCoord = mapRef.current.getCoordinateFromPixel(event.pixel); - - let featureFound = false; - mapRef.current.forEachFeatureAtPixel(event.pixel, (feature) => { - featureFound = true; - setSelectedFeature(feature); - return true; - }); - - if (!featureFound) { - setSelectedFeature(undefined); - } - - setCurrentOverlayPosition(clickedCoord); - }; - - const handleMoveEnd = () => { - const newZoom = Math.floor(mapRef.current.getView().getZoom() || constants.GLOBAL_MIN_ZOOM); - if (currentZoom != newZoom) { - setCurrentZoom(newZoom); - } - }; - - return ( - <> -
- {map? - : - '' - } - - - ); -}; - -export default MapWrapper; diff --git a/client/src/components/openlayersPopup.module.scss b/client/src/components/openlayersPopup.module.scss deleted file mode 100644 index 6d3449f8..00000000 --- a/client/src/components/openlayersPopup.module.scss +++ /dev/null @@ -1,52 +0,0 @@ -.popupContainer { - position: absolute; - background-color: white; - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2); - padding: 15px; - border-radius: 10px; - border: 1px solid #cccccc; - bottom: 12px; - left: -50px; - min-width: 280px; - overflow: scroll; -} - -.popupContainer:after, -.popupContainer:before { - top: 100%; - border: solid transparent; - content: " "; - height: 0; - width: 0; - position: absolute; - pointer-events: none; -} - -.popupContainer:after { - border-top-color: white; - border-width: 10px; - left: 48px; - margin-left: -10px; -} - -.popupContainer:before { - border-top-color: #cccccc; - border-width: 11px; - left: 48px; - margin-left: -11px; -} - -.popupCloser { - text-decoration: none; - position: absolute; - top: 2px; - right: 8px; -} - -.popupCloser:after { - content: "✖"; -} - -.popupContent { - max-height: 300px; -} diff --git a/client/src/components/openlayersPopup.module.scss.d.ts b/client/src/components/openlayersPopup.module.scss.d.ts deleted file mode 100644 index 0061d9a9..00000000 --- a/client/src/components/openlayersPopup.module.scss.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -declare namespace MapControlModuleScssNamespace { - export interface IMapControlModuleScss { - popupContainer: string; - popupCloser: string; - popupContent: string; - } -} - -declare const MapControlModuleScssModule: MapControlModuleScssNamespace.IMapControlModuleScss & { - /** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */ - locals: MapControlModuleScssNamespace.IMapControlModuleScss; -}; - -export = MapControlModuleScssModule; diff --git a/client/src/components/openlayersPopup.tsx b/client/src/components/openlayersPopup.tsx deleted file mode 100644 index 4edd0b72..00000000 --- a/client/src/components/openlayersPopup.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React, {useRef, useEffect, useState} from 'react'; -import * as styles from './openlayersPopup.module.scss'; -import Overlay from 'ol/Overlay'; -import {Coordinate} from 'ol/Coordinate'; -import Map from 'ol/Map'; -import {FeatureLike} from 'ol/Feature'; -import PopupContent from './popupContent'; - -interface IOpenlayersPopupProps { - map: Map, - selectedFeature: FeatureLike; - position: Coordinate; -} - -const OpenlayersPopup = ({map, selectedFeature, position}: IOpenlayersPopupProps) => { - const popupContainerElement = useRef(null); - const popupCloserElement = useRef(null); - const popupContentElement = useRef(null); - const [currentOverlay, setCurrentOverlay] = useState(); - - useEffect(() => { - popupCloserElement.current!.onclick = function() { - overlay.setPosition(undefined); - popupCloserElement.current!.blur(); - return false; - }; - - const overlay = new Overlay({ - element: popupContainerElement.current!, - autoPan: true, - autoPanAnimation: { - duration: 250, - }, - }); - setCurrentOverlay(overlay); - map.addOverlay(overlay); - }, []); - - useEffect( () => { - if (position && currentOverlay && selectedFeature) { // may be empty on first render - currentOverlay.setPosition(position); - } - }, [position]); - - return ( - <> -
- -
- -
-
- - ); -}; - -export default OpenlayersPopup; diff --git a/client/src/data/mapStyle.tsx b/client/src/data/mapStyle.tsx index bfefbfe1..149b89e1 100644 --- a/client/src/data/mapStyle.tsx +++ b/client/src/data/mapStyle.tsx @@ -1,4 +1,4 @@ -import {Style, FillPaint} from 'mapbox-gl'; +import {Style, FillPaint} from 'maplibre-gl'; import chroma from 'chroma-js'; import * as constants from '../data/constants'; @@ -8,27 +8,24 @@ function hexToHSLA(hex:string, alpha:number) { } /** - * `MakePaint` generates a zoom-faded Mapbox style formatted layer given a set of parameters. + * `MakePaint` generates a zoom-faded Maplibre style formatted layer given a set of parameters. * * @param {string} field : the field within the data to consult * @param {number} minRamp : the minimum value this can assume * @param {number} medRamp : the medium value this can assume * @param {number} maxRamp : the maximum value this can assume - * @param {boolean} high : whether this is a "high" or "low" layer - * @return {FillPaint} a mapboxgl fill layer + * @return {FillPaint} a maplibregl fill layer **/ function makePaint({ field, minRamp, medRamp, maxRamp, - high = true, }: { field: string; minRamp: number; medRamp: number; maxRamp: number; - high: boolean; }): FillPaint { const paintDescriptor : FillPaint = { 'fill-color': [ @@ -88,14 +85,12 @@ const mapStyle : Style = { 'source': 'carto', 'type': 'raster', 'minzoom': constants.GLOBAL_MIN_ZOOM - 1, - 'maxzoom': constants.GLOBAL_MAX_ZOOM + 1, }, { 'id': 'geo', 'source': 'geo', 'type': 'raster', 'minzoom': constants.GLOBAL_MIN_ZOOM - 1, - 'maxzoom': constants.GLOBAL_MAX_ZOOM + 1, 'layout': { // Make the layer visible by default. 'visibility': 'none', @@ -115,7 +110,6 @@ const mapStyle : Style = { minRamp: 0, medRamp: 0.6, maxRamp: 0.75, - high: true, }), 'minzoom': constants.GLOBAL_MIN_ZOOM, 'maxzoom': constants.GLOBAL_MAX_ZOOM, @@ -126,7 +120,6 @@ const mapStyle : Style = { 'source-layer': 'blocks', 'type': 'line', 'minzoom': constants.GLOBAL_MIN_ZOOM_HIGH, - 'maxzoom': constants.GLOBAL_MAX_ZOOM_HIGH, 'layout': { 'visibility': 'visible', 'line-join': 'round', @@ -143,7 +136,6 @@ const mapStyle : Style = { 'type': 'raster', 'source': 'labels', 'minzoom': constants.GLOBAL_MIN_ZOOM, - 'maxzoom': constants.GLOBAL_MAX_ZOOM, }, ], }; diff --git a/client/src/styles/global.scss b/client/src/styles/global.scss index 33433940..38fd2a40 100644 --- a/client/src/styles/global.scss +++ b/client/src/styles/global.scss @@ -192,16 +192,16 @@ $j40-max-width: 80ex; } } -// Mapbox overrides +// Maplibre overrides // Note that these need to be here to properly override defaults -.mapboxgl-popup-close-button { +.maplibregl-popup-close-button { font-size: 3em; margin-right: 12px; margin-top: 15px; } -.mapboxgl-popup-content { +.maplibregl-popup-content { box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.5) !important; border-radius: 8px !important; pointer-events: all !important; diff --git a/client/tsconfig.json b/client/tsconfig.json index 71c1a120..09b97a00 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -15,20 +15,9 @@ "allowJs": true, "resolveJsonModule": true, "baseUrl": "./", - "paths": { - "ol": ["node_modules/ol/src"], - "ol/*": ["node_modules/ol/src/*"], - "ol-mapbox-style": ["node_modules/ol-mapbox-style/src"], - "ol-mapbox-style/*": ["node_modules/ol-mapbox-style/src/*"] - } }, "include": [ "./src/**/*ts", "./src/**/*tsx", - "node_modules/ol/**/*", - "node_modules/ol-mapbox-style/**/*" ], - "typeAcquisition": { - "exclude": ["ol", "ol-mapbox-style"] - } }