From 59c42338aa164c0aa000dc1fee8bd0e7ecfd8681 Mon Sep 17 00:00:00 2001 From: Seb-sti1 <65665540+seb-sti1@users.noreply.github.com> Date: Mon, 6 Nov 2023 20:51:19 +0100 Subject: [PATCH] Main page: Select a road --- frontend/package-lock.json | 178 ------------------ .../src/Components/Map/DetectMapClick.tsx | 22 +++ .../src/Components/Map/ForceMapUpdate.tsx | 1 + frontend/src/Components/Map/Road.tsx | 8 +- frontend/src/Components/Map/Roads.tsx | 35 ++-- frontend/src/pages/Main.tsx | 39 +++- 6 files changed, 86 insertions(+), 197 deletions(-) create mode 100644 frontend/src/Components/Map/DetectMapClick.tsx diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8026e705..0fbafc20 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -15,9 +15,7 @@ "chartjs-plugin-zoom": "^2.0.1", "d3": "^7.8.5", "leaflet": "^1.9.4", - "multiselect-react-dropdown": "^2.0.25", "react": "^18.2.0", - "react-burger-menu": "^3.0.9", "react-chartjs-2": "^5.2.0", "react-datepicker": "^4.19.0", "react-dom": "^18.2.0", @@ -42,7 +40,6 @@ "@types/leaflet": "^1.9.4", "@types/node": "^20.6.2", "@types/react": "^18.2.21", - "@types/react-burger-menu": "^2.8.5", "@types/react-datepicker": "^4.15.1", "@types/react-dom": "^18.2.7", "@types/react-router-dom": "^5.3.3", @@ -4155,15 +4152,6 @@ "csstype": "^3.0.2" } }, - "node_modules/@types/react-burger-menu": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/@types/react-burger-menu/-/react-burger-menu-2.8.5.tgz", - "integrity": "sha512-2VCnDyg4JGzaTByh17qwu2rVnjylF+L5Lzw9CwiiDmcYBMlW9XE8+q0ZChV+9qiBW6SNjBiz/KH+bOyv28THAQ==", - "dev": true, - "dependencies": { - "@types/react": "*" - } - }, "node_modules/@types/react-datepicker": { "version": "4.15.1", "resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-4.15.1.tgz", @@ -5057,15 +5045,6 @@ "ajv": "^6.9.1" } }, - "node_modules/amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", - "optional": true, - "engines": { - "node": ">=0.4.2" - } - }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -5316,76 +5295,6 @@ "util": "0.10.3" } }, - "node_modules/ast-transform": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/ast-transform/-/ast-transform-0.0.0.tgz", - "integrity": "sha512-e/JfLiSoakfmL4wmTGPjv0HpTICVmxwXgYOB8x+mzozHL8v+dSfCbrJ8J8hJ0YBP0XcYu1aLZ6b/3TnxNK3P2A==", - "dependencies": { - "escodegen": "~1.2.0", - "esprima": "~1.0.4", - "through": "~2.3.4" - } - }, - "node_modules/ast-transform/node_modules/escodegen": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.2.0.tgz", - "integrity": "sha512-yLy3Cc+zAC0WSmoT2fig3J87TpQ8UaZGx8ahCAs9FL8qNbyV7CVyPKS74DG4bsHiL5ew9sxdYx131OkBQMFnvA==", - "dependencies": { - "esprima": "~1.0.4", - "estraverse": "~1.5.0", - "esutils": "~1.0.0" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=0.4.0" - }, - "optionalDependencies": { - "source-map": "~0.1.30" - } - }, - "node_modules/ast-transform/node_modules/esprima": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", - "integrity": "sha512-rp5dMKN8zEs9dfi9g0X1ClLmV//WRyk/R15mppFNICIFRG5P92VP7Z04p8pk++gABo9W2tY+kHyu6P1mEHgmTA==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ast-transform/node_modules/estraverse": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz", - "integrity": "sha512-FpCjJDfmo3vsc/1zKSeqR5k42tcIhxFIlvq+h9j0fO2q/h2uLKyweq7rYJ+0CoVvrGQOxIS5wyBrW/+vF58BUQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ast-transform/node_modules/esutils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz", - "integrity": "sha512-x/iYH53X3quDwfHRz4y8rn4XcEwwCJeWsul9pF1zldMbGtgOtMNBEOuYWwB1EQlK2LRa1fev3YAgym/RElp5Cg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ast-transform/node_modules/source-map": { - "version": "0.1.43", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", - "integrity": "sha512-VtCvB9SIQhk3aF6h+N85EaqIaBFIAfZ9Cu+NJHHVvc8BbEcnvDcFw6sqQ2dQrT6SlOrZq3tIvyD9+EGq/lJryQ==", - "optional": true, - "dependencies": { - "amdefine": ">=0.0.4" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/ast-types": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.14.2.tgz", @@ -5967,37 +5876,6 @@ "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" }, - "node_modules/browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", - "dependencies": { - "resolve": "1.1.7" - } - }, - "node_modules/browser-resolve/node_modules/resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==" - }, - "node_modules/browserify-optional": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-optional/-/browserify-optional-1.0.1.tgz", - "integrity": "sha512-VrhjbZ+Ba5mDiSYEuPelekQMfTbhcA2DhLk2VQWqdcCROWeFqlTcXZ7yfRkXCIl8E+g4gINJYJiRB7WEtfomAQ==", - "dependencies": { - "ast-transform": "0.0.0", - "ast-types": "^0.7.0", - "browser-resolve": "^1.8.1" - } - }, - "node_modules/browserify-optional/node_modules/ast-types": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.7.8.tgz", - "integrity": "sha512-RIOpVnVlltB6PcBJ5BMLx+H+6JJ/zjDGU0t7f0L6c2M1dqcK92VQopLBlPQ9R80AVXelfqYgjcPLtHtDbNFg0Q==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/browserslist": { "version": "4.21.10", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", @@ -9434,11 +9312,6 @@ "node": ">= 0.6" } }, - "node_modules/eve": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/eve/-/eve-0.5.4.tgz", - "integrity": "sha512-aqprQ9MAOh1t66PrHxDFmMXPlgNO6Uv1uqvxmwjprQV50jaQ2RqO7O1neY4PJwC+hMnkyMDphu2AQPOPZdjQog==" - }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -13945,14 +13818,6 @@ "multicast-dns": "cli.js" } }, - "node_modules/multiselect-react-dropdown": { - "version": "2.0.25", - "resolved": "https://registry.npmjs.org/multiselect-react-dropdown/-/multiselect-react-dropdown-2.0.25.tgz", - "integrity": "sha512-z8kUSyBNOuV7vn4Dk35snzXWtIfTdSEEXhgDdLMvOmR+xJFx35vc1voUlSuOvrk3khb+hXC219Qs9szOvNm25Q==", - "peerDependencies": { - "react": "^16.7.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -16299,25 +16164,6 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, - "node_modules/react-burger-menu": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/react-burger-menu/-/react-burger-menu-3.0.9.tgz", - "integrity": "sha512-Qy15hkCxwxNEKfqdAv43F+8ZSl+/c6KkqrBwGP0CesFYJ02onHtiUFUbuhSWCMtBH8/n0HhfekFlp/NyCdKYzQ==", - "dependencies": { - "browserify-optional": "^1.0.0", - "classnames": "^2.2.6", - "eve": "~0.5.1", - "prop-types": "^15.7.2", - "snapsvg-cjs": "0.0.6" - }, - "engines": { - "node": ">=4.0.0" - }, - "peerDependencies": { - "react": ">=0.14.0", - "react-dom": ">=0.14.0" - } - }, "node_modules/react-chartjs-2": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz", @@ -18973,25 +18819,6 @@ "node": ">=8" } }, - "node_modules/snapsvg": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/snapsvg/-/snapsvg-0.5.1.tgz", - "integrity": "sha512-CjwWYsL7+CCk1vCk9BBKGYS4WJVDfJAOMWU+Zhzf8wf6pAm/xT34wnpaMPAgcgCNkxuU6OkQPPd8wGuRCY9aNw==", - "dependencies": { - "eve": "~0.5.1" - } - }, - "node_modules/snapsvg-cjs": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/snapsvg-cjs/-/snapsvg-cjs-0.0.6.tgz", - "integrity": "sha512-7NNvoGrc3BQvWz5rWK1DsD5/Vni4STswz5B3JrBADboQWcN8OBVGjYVJFPT5JkUXb2iVnEflZANhufEpEcTHXw==", - "dependencies": { - "snapsvg": "0.5.1" - }, - "peerDependencies": { - "eve": "~0.5.1" - } - }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -19772,11 +19599,6 @@ "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==" }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" - }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", diff --git a/frontend/src/Components/Map/DetectMapClick.tsx b/frontend/src/Components/Map/DetectMapClick.tsx new file mode 100644 index 00000000..ee7af46f --- /dev/null +++ b/frontend/src/Components/Map/DetectMapClick.tsx @@ -0,0 +1,22 @@ +import { useMapEvents } from 'react-leaflet'; +import { LatLng } from 'leaflet'; +import { FC } from 'react'; + +interface Props { + /** The callback function */ + onClick: (latLng: LatLng) => void; +} + +/** + * A component that detects a click on the map and calls the callback function + */ +const DetectMapClick: FC = ({ onClick }) => { + useMapEvents({ + click: (e) => { + onClick(e.latlng); + }, + }); + return null; +}; + +export default DetectMapClick; diff --git a/frontend/src/Components/Map/ForceMapUpdate.tsx b/frontend/src/Components/Map/ForceMapUpdate.tsx index 91ba5c73..4afdaf14 100644 --- a/frontend/src/Components/Map/ForceMapUpdate.tsx +++ b/frontend/src/Components/Map/ForceMapUpdate.tsx @@ -19,6 +19,7 @@ const ForceMapUpdate: React.FC = ({ triggerUpdate, position }) => { map.invalidateSize(); }, [triggerUpdate]); + // TODO: not working when moving too little useEffect(() => { if (position) { map.flyTo(position); diff --git a/frontend/src/Components/Map/Road.tsx b/frontend/src/Components/Map/Road.tsx index cb96586c..4000796c 100644 --- a/frontend/src/Components/Map/Road.tsx +++ b/frontend/src/Components/Map/Road.tsx @@ -2,6 +2,7 @@ import { IRoad } from '../../models/path'; import { LatLng, LatLngLiteral } from 'leaflet'; import { Polyline } from 'react-leaflet'; import React, { ReactElement, useEffect } from 'react'; +import L from 'leaflet'; interface Props { /** The roads */ @@ -47,11 +48,12 @@ const Road: React.FC = ({ road, onClick }) => { pathOptions={{ weight: 5, opacity: opacity }} positions={data} eventHandlers={{ - click: ({ latlng }) => { - if (onClick) onClick(latlng); + click: (e) => { + L.DomEvent.stopPropagation(e); // used to prevent the map to detect the click + if (onClick) onClick(e.latlng); }, mouseover: () => { - setOpacity(0.8); + setOpacity(0.9); }, mouseout: () => { setOpacity(0.2); diff --git a/frontend/src/Components/Map/Roads.tsx b/frontend/src/Components/Map/Roads.tsx index c92efbe4..4f2bfd01 100644 --- a/frontend/src/Components/Map/Roads.tsx +++ b/frontend/src/Components/Map/Roads.tsx @@ -2,38 +2,45 @@ import React from 'react'; import { IRoad } from '../../models/path'; import Road from './Road'; import { LatLng } from '../../models/models'; -import { useNavigate } from 'react-router-dom'; interface Props { /** The roads */ roads?: IRoad[]; + /** The selected road index */ + selectedRoadIdx?: number; /** The event handlers, when a road is selected */ - onSelectedRoad?: (road: IRoad, position: LatLng) => void; + onSelectedRoad?: (index: number, road: IRoad, position: LatLng) => void; } /** * A components that renders the roads on the map */ -const Roads: React.FC = ({ roads, onSelectedRoad }) => { - const navigate = useNavigate(); - +const Roads: React.FC = ({ roads, selectedRoadIdx, onSelectedRoad }) => { return ( <> - {roads?.map((road) => ( + {selectedRoadIdx === undefined || selectedRoadIdx === -1 || !roads ? ( + roads?.map((road, index) => ( + { + if (onSelectedRoad) { + onSelectedRoad(index, road, position); + } + }} + /> + )) + ) : ( { - console.log(road, position); - // TODO: remove when possible to select a road - navigate('/road-details'); - if (onSelectedRoad) { - onSelectedRoad(road, position); + onSelectedRoad(selectedRoadIdx, roads[selectedRoadIdx], position); } }} /> - ))} + )} ); }; diff --git a/frontend/src/pages/Main.tsx b/frontend/src/pages/Main.tsx index 70425291..a0229831 100644 --- a/frontend/src/pages/Main.tsx +++ b/frontend/src/pages/Main.tsx @@ -11,6 +11,7 @@ import { conditionTypes, DateRange, YearMonth } from '../models/conditions'; import MonthFilter from '../Components/Map/Inputs/MonthFilter'; import Selector from '../Components/Map/Inputs/Selector'; import '../css/navbar.css'; +import DetectMapClick from '../Components/Map/DetectMapClick'; /** * Component rendering the main page @@ -18,6 +19,9 @@ import '../css/navbar.css'; const Main: FC = () => { // The roads loaded from the database const [roads, setRoads] = useState(); + // Select road index + const [selectedRoadIdx, setSelectedRoadIdx] = useState(-1); + // The position to move too (used by the Search component) const [moveToPosition, setMoveToPosition] = useState(); // The indicator to display on the map @@ -56,7 +60,20 @@ const Main: FC = () => {
{ - console.log(value); + const osm_id = value?.properties?.datasource?.raw?.osm_id; + if (osm_id && roads) { + console.debug(osm_id, roads.length); + for (let idx = 0; idx < roads.length; idx++) { + if ( + Object.keys(roads[idx].geometries).includes(String(osm_id)) + ) { + console.debug('Found road', idx); + setSelectedRoadIdx(idx); + break; + } + } + } + const coordinates = value?.geometry?.coordinates; if (coordinates) { const position = { @@ -98,7 +115,25 @@ const Main: FC = () => {
- + { + // If no road is selected, select the road + if (selectedRoadIdx === -1) { + setSelectedRoadIdx(index); + } + setMoveToPosition(position); + }} + /> + { + if (selectedRoadIdx !== -1) { + // TODO: prevent this if the click is on a road + setSelectedRoadIdx(-1); + } + }} + />