From 4bfdc4682843a443b169e2f14fde2d0e9b0c1ce7 Mon Sep 17 00:00:00 2001 From: Miina Saroma Date: Thu, 26 Sep 2024 16:20:38 +0300 Subject: [PATCH 1/9] Add draggable marker and latlng to Map --- frontend/src/components/MapPage.tsx | 63 ++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/MapPage.tsx b/frontend/src/components/MapPage.tsx index 6318686c..ccedf6e3 100644 --- a/frontend/src/components/MapPage.tsx +++ b/frontend/src/components/MapPage.tsx @@ -1,7 +1,43 @@ -import { useRef } from 'react' -import { MapContainer, TileLayer } from 'react-leaflet' +import { useRef, useState, useMemo, useCallback } from 'react' +import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet' import 'leaflet/dist/leaflet.css' +const DraggableMarker = ({ setMarkerPos }: { setMarkerPos: any }) => { + const center = { + lat: 61.5, + lng: 25.01, + } + const [draggable, setDraggable] = useState(false) + const [position, setPosition] = useState(center) + const markerRef = useRef(null) + const eventHandlers = useMemo( + () => ({ + dragend() { + const marker = markerRef.current + if (marker != null) { + setPosition(marker.getLatLng()) + console.log(marker.getLatLng()) + setMarkerPos(marker.getLatLng()) + } + }, + }), + [] + ) + const toggleDraggable = useCallback(() => { + setDraggable(d => !d) + }, []) + + return ( + + + + {draggable ? 'Marker is draggable' : 'Click here to make marker draggable'} + + + + ) +} + export const MapPage = () => { const mapRef = useRef(null) const latitude = 30 @@ -9,13 +45,22 @@ export const MapPage = () => { document.title = 'Map' + const [markerPos, setMarkerPos] = useState(null) + return ( - - - {/* Additional map layers or components can be added here */} - +
+ + + {/* Additional map layers or components can be added here */} + {/* */} + + +

+ Lat: {markerPos ? markerPos.lat : null} Lon: {markerPos ? markerPos.lng : null} +

+
) } From 6666908ea3a855df6404f00f125a35959cb6a51a Mon Sep 17 00:00:00 2001 From: Miina Saroma Date: Thu, 26 Sep 2024 16:21:58 +0300 Subject: [PATCH 2/9] Refactor Map to a component --- frontend/src/components/Map/Map.tsx | 66 +++++++++++++++++++++++++ frontend/src/components/Map/README.md | 70 +++++++++++++++++++++++++++ frontend/src/components/MapPage.tsx | 64 +----------------------- 3 files changed, 138 insertions(+), 62 deletions(-) create mode 100644 frontend/src/components/Map/Map.tsx create mode 100644 frontend/src/components/Map/README.md diff --git a/frontend/src/components/Map/Map.tsx b/frontend/src/components/Map/Map.tsx new file mode 100644 index 00000000..50f6d18e --- /dev/null +++ b/frontend/src/components/Map/Map.tsx @@ -0,0 +1,66 @@ +import { useRef, useState, useMemo, useCallback } from 'react' +import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet' +import 'leaflet/dist/leaflet.css' + +const DraggableMarker = ({ setMarkerPos }: { setMarkerPos: any }) => { + const center = { + lat: 61.5, + lng: 25.01, + } + const [draggable, setDraggable] = useState(false) + const [position, setPosition] = useState(center) + const markerRef = useRef(null) + const eventHandlers = useMemo( + () => ({ + dragend() { + const marker = markerRef.current + if (marker != null) { + setPosition(marker.getLatLng()) + console.log(marker.getLatLng()) + setMarkerPos(marker.getLatLng()) + } + }, + }), + [] + ) + const toggleDraggable = useCallback(() => { + setDraggable(d => !d) + }, []) + + return ( + + + + {draggable ? 'Marker is draggable' : 'Click here to make marker draggable'} + + + + ) +} + +export const Map = () => { + const mapRef = useRef(null) + const latitude = 30 + const longitude = -5 + + document.title = 'Map' + + const [markerPos, setMarkerPos] = useState(null) + + return ( +
+ + + {/* Additional map layers or components can be added here */} + {/* */} + + +

+ Lat: {markerPos ? markerPos.lat : null} Lon: {markerPos ? markerPos.lng : null} +

+
+ ) +} diff --git a/frontend/src/components/Map/README.md b/frontend/src/components/Map/README.md new file mode 100644 index 00000000..a54e7faf --- /dev/null +++ b/frontend/src/components/Map/README.md @@ -0,0 +1,70 @@ +# Map thoughts and stuff I'm writing, very incomplete bilingual documentation + thoughts + +# Functionality for now + +- there is a page for a map +- you can toggle drag of the marker by first clicking it and then clicking the pop-up text +- when the marker is draggable, you can double-click it to grab it + - then while holding the mouse button, you can move the marker around + - now you can see the lat/lng on the Map Page + +### Thinking: +- OSM:n Tile Usage Policy. +- en tiedä mitä server-side rendering on, mut react-leaflet ei sovi sen kaa yhteen +- Make sure the map container has a defined height, for example by setting it in CSS: #map { height: 180px; } + +OpenStreetMap tiles are fine for programming your Leaflet map, but read the Tile Usage Policy https://operations.osmfoundation.org/policies/tiles/ of OpenStreetMap if you’re going to use the tiles in production. + +> OpenStreetMap data is free for everyone to use. Our tile servers are not. +> Because OpenStreetMap data is free, many other organisations provide map tiles made from OSM data. If your project doesn’t meet our requirements, you can get OSM-derived map tiles elsewhere. + +Leaflet makes direct calls to the DOM when it is loaded, therefore React Leaflet is not compatible with server-side rendering. + https://medium.com/@timndichu/getting-started-with-leaflet-js-and-react-rendering-a-simple-map-ef9ee0498202 + + +### TODO + TODO: + - show lat/lon DONE + + - do things in Locality + - add button to locality + - open map in locality by pressing the button + - a small popup? + - add a way to click/drag a location + - some confirmation or show info or "is this ok?", maybe? + - add input from map to fields + + +### maybe later + - show country based on lat/lon? + - make the draggable marker always draggable + + - add search bar to Map (by cities or something) + - make draggable marker move when a city is chosen + - make map move closer to the city when a city is chosen + +### NOW_DATABASE test/current versiossa + +Locality -> New -> get coordinates -button + +-> popup. + +Clicking on map => shows Lat + Long + - esim Latitude: 60.722200435142156, Longitude: 26.3671875 + +- sisältää myös "search location" + - jossa nuuksio tuo 1 tulos + - new york tuo 1 tulos + - new ei tuo mitään + - york tuo 2, both same from UK? + - Moscow ja joku muu jota pitäis olla useampi kaupunki, nii löyty vaan 1? + + - tuloslista on tyyliä "Helsinki, Finland" + - tuloksen klikkaaminen ei muuta näkyvää Lat/Lon + - tulee marker kartalle + - sen klikkaaminen muuttaa Lat/Lon + + +### NOW_DATABASE dev versiossa +- ? + diff --git a/frontend/src/components/MapPage.tsx b/frontend/src/components/MapPage.tsx index ccedf6e3..f9c2befc 100644 --- a/frontend/src/components/MapPage.tsx +++ b/frontend/src/components/MapPage.tsx @@ -1,66 +1,6 @@ -import { useRef, useState, useMemo, useCallback } from 'react' -import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet' +import { Map } from './Map/Map' import 'leaflet/dist/leaflet.css' -const DraggableMarker = ({ setMarkerPos }: { setMarkerPos: any }) => { - const center = { - lat: 61.5, - lng: 25.01, - } - const [draggable, setDraggable] = useState(false) - const [position, setPosition] = useState(center) - const markerRef = useRef(null) - const eventHandlers = useMemo( - () => ({ - dragend() { - const marker = markerRef.current - if (marker != null) { - setPosition(marker.getLatLng()) - console.log(marker.getLatLng()) - setMarkerPos(marker.getLatLng()) - } - }, - }), - [] - ) - const toggleDraggable = useCallback(() => { - setDraggable(d => !d) - }, []) - - return ( - - - - {draggable ? 'Marker is draggable' : 'Click here to make marker draggable'} - - - - ) -} - export const MapPage = () => { - const mapRef = useRef(null) - const latitude = 30 - const longitude = -5 - - document.title = 'Map' - - const [markerPos, setMarkerPos] = useState(null) - - return ( -
- - - {/* Additional map layers or components can be added here */} - {/* */} - - -

- Lat: {markerPos ? markerPos.lat : null} Lon: {markerPos ? markerPos.lng : null} -

-
- ) + return } From 511db02b30065b2d7be729e50f11b1c4332fecf0 Mon Sep 17 00:00:00 2001 From: Miina Saroma Date: Tue, 1 Oct 2024 12:56:51 +0300 Subject: [PATCH 3/9] Make Map dependant on coord props --- frontend/src/components/Map/Map.tsx | 11 +++-------- frontend/src/components/MapPage.tsx | 5 ++++- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/Map/Map.tsx b/frontend/src/components/Map/Map.tsx index 50f6d18e..377e3cdc 100644 --- a/frontend/src/components/Map/Map.tsx +++ b/frontend/src/components/Map/Map.tsx @@ -38,15 +38,12 @@ const DraggableMarker = ({ setMarkerPos }: { setMarkerPos: any }) => { ) } -export const Map = () => { +export const Map = ({ coordinates, setCoordinates }) => { const mapRef = useRef(null) const latitude = 30 const longitude = -5 - document.title = 'Map' - const [markerPos, setMarkerPos] = useState(null) - return (
@@ -54,12 +51,10 @@ export const Map = () => { attribution='© OpenStreetMap contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> - {/* Additional map layers or components can be added here */} - {/* */} - +

- Lat: {markerPos ? markerPos.lat : null} Lon: {markerPos ? markerPos.lng : null} + Lat: {coordinates ? coordinates.lat : null} Lon: {coordinates ? coordinates.lng : null}

) diff --git a/frontend/src/components/MapPage.tsx b/frontend/src/components/MapPage.tsx index f9c2befc..c1a07070 100644 --- a/frontend/src/components/MapPage.tsx +++ b/frontend/src/components/MapPage.tsx @@ -1,6 +1,9 @@ import { Map } from './Map/Map' +import { useState } from 'react' import 'leaflet/dist/leaflet.css' export const MapPage = () => { - return + const [coordinates, setCoordinates] = useState(null) + + return } From 6c496d1c3f0e30026d0381c7f76c4e7cf50586d9 Mon Sep 17 00:00:00 2001 From: Miina Saroma Date: Thu, 3 Oct 2024 15:52:07 +0300 Subject: [PATCH 4/9] Location coordinates are copied to dec input fields --- .../components/Locality/Tabs/LocalityTab.tsx | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/Locality/Tabs/LocalityTab.tsx b/frontend/src/components/Locality/Tabs/LocalityTab.tsx index e8a85471..a6333e26 100644 --- a/frontend/src/components/Locality/Tabs/LocalityTab.tsx +++ b/frontend/src/components/Locality/Tabs/LocalityTab.tsx @@ -7,9 +7,13 @@ import { useForm } from 'react-hook-form' import { EditableTable } from '@/components/DetailView/common/EditableTable' import { EditingModal } from '@/components/DetailView/common/EditingModal' import { emptyOption } from '@/components/DetailView/common/misc' +import { Map } from '@/components/Map/Map' +import { useState } from 'react' export const LocalityTab = () => { const { textField, radioSelection, dropdown, mode, bigTextField } = useDetailContext() + const { editData, setEditData } = useDetailContext() + const [coordinates, setCoordinates] = useState(null) const approximateCoordinatesOptions = [ { display: 'No', value: 'false' }, @@ -83,6 +87,22 @@ export const LocalityTab = () => { ) + // eslint-disable-next-line @typescript-eslint/require-await + const onSaveCoord = async () => { + console.log('Coordinates:', coordinates.lat, coordinates.lng) //this is just dec + setEditData({ ...editData, dec_lat: coordinates.lat, dec_long: coordinates.lng }) + return Object.keys(errors).length === 0 //no idea if this is needed, just copypasted + } + + // TODO name this better, plagiarized from editingModal + const coordinateButton = ( + + + + + + ) + return ( <> @@ -90,7 +110,10 @@ export const LocalityTab = () => { - + + + {!mode.read && coordinateButton} + {!mode.read && editingModal} From 1f806bcf13af5dc8d12ed706ce4eba434d863dd9 Mon Sep 17 00:00:00 2001 From: Miina Saroma Date: Tue, 8 Oct 2024 14:23:31 +0300 Subject: [PATCH 5/9] Map marker has default coordinates and drag is always on --- .../components/Locality/Tabs/LocalityTab.tsx | 8 +++-- frontend/src/components/Map/Map.tsx | 30 +++++-------------- frontend/src/components/MapPage.tsx | 2 +- 3 files changed, 13 insertions(+), 27 deletions(-) diff --git a/frontend/src/components/Locality/Tabs/LocalityTab.tsx b/frontend/src/components/Locality/Tabs/LocalityTab.tsx index a6333e26..81a932b9 100644 --- a/frontend/src/components/Locality/Tabs/LocalityTab.tsx +++ b/frontend/src/components/Locality/Tabs/LocalityTab.tsx @@ -13,7 +13,6 @@ import { useState } from 'react' export const LocalityTab = () => { const { textField, radioSelection, dropdown, mode, bigTextField } = useDetailContext() const { editData, setEditData } = useDetailContext() - const [coordinates, setCoordinates] = useState(null) const approximateCoordinatesOptions = [ { display: 'No', value: 'false' }, @@ -87,7 +86,10 @@ export const LocalityTab = () => { ) - // eslint-disable-next-line @typescript-eslint/require-await + // Kumpula Coordinates, could be changed later to be existing coordinates if exists + const [coordinates, setCoordinates] = useState({ lat: 60.202665856, lng: 24.957662836 }) + + // ONLY dec coordinates, no conversion to dms yet const onSaveCoord = async () => { console.log('Coordinates:', coordinates.lat, coordinates.lng) //this is just dec setEditData({ ...editData, dec_lat: coordinates.lat, dec_long: coordinates.lng }) @@ -96,7 +98,7 @@ export const LocalityTab = () => { // TODO name this better, plagiarized from editingModal const coordinateButton = ( - + diff --git a/frontend/src/components/Map/Map.tsx b/frontend/src/components/Map/Map.tsx index 377e3cdc..15271e60 100644 --- a/frontend/src/components/Map/Map.tsx +++ b/frontend/src/components/Map/Map.tsx @@ -1,14 +1,9 @@ -import { useRef, useState, useMemo, useCallback } from 'react' -import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet' +import { useRef, useState, useMemo } from 'react' +import { MapContainer, TileLayer, Marker } from 'react-leaflet' import 'leaflet/dist/leaflet.css' -const DraggableMarker = ({ setMarkerPos }: { setMarkerPos: any }) => { - const center = { - lat: 61.5, - lng: 25.01, - } - const [draggable, setDraggable] = useState(false) - const [position, setPosition] = useState(center) +const DraggableMarker = ({ setMarkerPos, coord }: { setMarkerPos: any }) => { + const [position, setPosition] = useState(coord) const markerRef = useRef(null) const eventHandlers = useMemo( () => ({ @@ -21,21 +16,10 @@ const DraggableMarker = ({ setMarkerPos }: { setMarkerPos: any }) => { } }, }), - [] + [setMarkerPos] ) - const toggleDraggable = useCallback(() => { - setDraggable(d => !d) - }, []) - return ( - - - - {draggable ? 'Marker is draggable' : 'Click here to make marker draggable'} - - - - ) + return } export const Map = ({ coordinates, setCoordinates }) => { @@ -51,7 +35,7 @@ export const Map = ({ coordinates, setCoordinates }) => { attribution='© OpenStreetMap contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> - +

Lat: {coordinates ? coordinates.lat : null} Lon: {coordinates ? coordinates.lng : null} diff --git a/frontend/src/components/MapPage.tsx b/frontend/src/components/MapPage.tsx index c1a07070..c4c7b258 100644 --- a/frontend/src/components/MapPage.tsx +++ b/frontend/src/components/MapPage.tsx @@ -3,7 +3,7 @@ import { useState } from 'react' import 'leaflet/dist/leaflet.css' export const MapPage = () => { - const [coordinates, setCoordinates] = useState(null) + const [coordinates, setCoordinates] = useState({ lat: 60.202665856, lng: 24.957662836 }) return } From acee088f9947dd62dd0db283cee22c08958b3c46 Mon Sep 17 00:00:00 2001 From: Miina Saroma Date: Tue, 8 Oct 2024 14:54:01 +0300 Subject: [PATCH 6/9] e2e test for locality's map coord button feature --- cypress/e2e/locality.cy.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/cypress/e2e/locality.cy.js b/cypress/e2e/locality.cy.js index d6743f2f..65f8c33b 100644 --- a/cypress/e2e/locality.cy.js +++ b/cypress/e2e/locality.cy.js @@ -25,3 +25,26 @@ describe("Locality min and max age checks work", () => { cy.contains("11.63") }) }) + +// WIP +describe('Locality\'s Map works', () => { + beforeEach('Login as admin', () => { + cy.login('testSu') + }) + + it('Opening map view in edit works', () => { + cy.visit(`/locality/20920?tab=1`) + cy.contains('Coordinates') + cy.get('[id=edit-button]').click() + cy.contains('Latitude') + cy.contains('Open Map').click() + cy.contains('OpenStreetMap') + cy.contains('Leaflet') + + cy.contains('Save').click() + cy.contains('OpenStreetMap').should('not.exist') + cy.contains('Longitude') + cy.contains('60.202665856') //breaks here + cy.contains('24.957662836') //and here + }) + }) \ No newline at end of file From 1c85ea9dcc1846855ef724874f9810f2af40924e Mon Sep 17 00:00:00 2001 From: Miina Saroma Date: Wed, 9 Oct 2024 13:39:15 +0300 Subject: [PATCH 7/9] Fix broken map test --- cypress/e2e/locality.cy.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cypress/e2e/locality.cy.js b/cypress/e2e/locality.cy.js index 65f8c33b..ce19b6a2 100644 --- a/cypress/e2e/locality.cy.js +++ b/cypress/e2e/locality.cy.js @@ -26,12 +26,12 @@ describe("Locality min and max age checks work", () => { }) }) -// WIP describe('Locality\'s Map works', () => { beforeEach('Login as admin', () => { cy.login('testSu') }) + // note that this changes only dec coordinates, dms is still the old one it('Opening map view in edit works', () => { cy.visit(`/locality/20920?tab=1`) cy.contains('Coordinates') @@ -44,7 +44,10 @@ describe('Locality\'s Map works', () => { cy.contains('Save').click() cy.contains('OpenStreetMap').should('not.exist') cy.contains('Longitude') - cy.contains('60.202665856') //breaks here - cy.contains('24.957662836') //and here + cy.contains('Finalize entry').click() + cy.contains('Complete and save').click() + cy.visit(`/locality/20920?tab=1`) + cy.contains('60.202665856') + cy.contains('24.957662836') }) }) \ No newline at end of file From 4f6b99df3889ef5d3799cb06e303f1f25fd3a7bb Mon Sep 17 00:00:00 2001 From: Miina Saroma Date: Fri, 11 Oct 2024 16:29:37 +0300 Subject: [PATCH 8/9] Add types to Map --- .../src/components/Locality/Tabs/LocalityTab.tsx | 2 +- frontend/src/components/Map/Map.tsx | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/Locality/Tabs/LocalityTab.tsx b/frontend/src/components/Locality/Tabs/LocalityTab.tsx index 81a932b9..479715e9 100644 --- a/frontend/src/components/Locality/Tabs/LocalityTab.tsx +++ b/frontend/src/components/Locality/Tabs/LocalityTab.tsx @@ -90,8 +90,8 @@ export const LocalityTab = () => { const [coordinates, setCoordinates] = useState({ lat: 60.202665856, lng: 24.957662836 }) // ONLY dec coordinates, no conversion to dms yet + // eslint-disable-next-line @typescript-eslint/require-await const onSaveCoord = async () => { - console.log('Coordinates:', coordinates.lat, coordinates.lng) //this is just dec setEditData({ ...editData, dec_lat: coordinates.lat, dec_long: coordinates.lng }) return Object.keys(errors).length === 0 //no idea if this is needed, just copypasted } diff --git a/frontend/src/components/Map/Map.tsx b/frontend/src/components/Map/Map.tsx index 15271e60..cb4dbbd5 100644 --- a/frontend/src/components/Map/Map.tsx +++ b/frontend/src/components/Map/Map.tsx @@ -2,16 +2,22 @@ import { useRef, useState, useMemo } from 'react' import { MapContainer, TileLayer, Marker } from 'react-leaflet' import 'leaflet/dist/leaflet.css' -const DraggableMarker = ({ setMarkerPos, coord }: { setMarkerPos: any }) => { +type Coordinate = { + lat: number + lng: number +} + +type CoordinateSetter = (Coordinate: Coordinate) => void + +const DraggableMarker = ({ setMarkerPos, coord }: { setMarkerPos: CoordinateSetter; coord: Coordinate }) => { const [position, setPosition] = useState(coord) - const markerRef = useRef(null) + const markerRef = useRef(null) const eventHandlers = useMemo( () => ({ dragend() { const marker = markerRef.current if (marker != null) { setPosition(marker.getLatLng()) - console.log(marker.getLatLng()) setMarkerPos(marker.getLatLng()) } }, @@ -22,7 +28,7 @@ const DraggableMarker = ({ setMarkerPos, coord }: { setMarkerPos: any }) => { return } -export const Map = ({ coordinates, setCoordinates }) => { +export const Map = ({ coordinates, setCoordinates }: { coordinates: Coordinate; setCoordinates: CoordinateSetter }) => { const mapRef = useRef(null) const latitude = 30 const longitude = -5 From 35bf4c472222891ff59e38f8e4389ddfe08f207c Mon Sep 17 00:00:00 2001 From: Miina Saroma Date: Fri, 11 Oct 2024 16:33:44 +0300 Subject: [PATCH 9/9] Refactor DraggableMarker a bit --- frontend/src/components/Map/Map.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/Map/Map.tsx b/frontend/src/components/Map/Map.tsx index cb4dbbd5..2e961a4c 100644 --- a/frontend/src/components/Map/Map.tsx +++ b/frontend/src/components/Map/Map.tsx @@ -1,4 +1,4 @@ -import { useRef, useState, useMemo } from 'react' +import { useRef, useMemo } from 'react' import { MapContainer, TileLayer, Marker } from 'react-leaflet' import 'leaflet/dist/leaflet.css' @@ -10,14 +10,12 @@ type Coordinate = { type CoordinateSetter = (Coordinate: Coordinate) => void const DraggableMarker = ({ setMarkerPos, coord }: { setMarkerPos: CoordinateSetter; coord: Coordinate }) => { - const [position, setPosition] = useState(coord) const markerRef = useRef(null) const eventHandlers = useMemo( () => ({ dragend() { const marker = markerRef.current if (marker != null) { - setPosition(marker.getLatLng()) setMarkerPos(marker.getLatLng()) } }, @@ -25,7 +23,7 @@ const DraggableMarker = ({ setMarkerPos, coord }: { setMarkerPos: CoordinateSett [setMarkerPos] ) - return + return } export const Map = ({ coordinates, setCoordinates }: { coordinates: Coordinate; setCoordinates: CoordinateSetter }) => {