From 754be7ad407ec1a5039832c8f14feba3f6df19ec Mon Sep 17 00:00:00 2001 From: Lucas Nandico Date: Mon, 20 May 2024 22:34:03 +0200 Subject: [PATCH 1/5] added basic geojson format support to show geofeature set with default settings Signed-off-by: Lucas Nandico --- frontend/src/components/MapView/DataFetch.tsx | 11 ++ .../components/MapView/FeatureCollection.json | 121 ++++++++++++++++++ frontend/src/components/MapView/MapView.tsx | 4 + package-lock.json | 29 +++++ package.json | 4 + 5 files changed, 169 insertions(+) create mode 100644 frontend/src/components/MapView/DataFetch.tsx create mode 100644 frontend/src/components/MapView/FeatureCollection.json create mode 100644 package-lock.json diff --git a/frontend/src/components/MapView/DataFetch.tsx b/frontend/src/components/MapView/DataFetch.tsx new file mode 100644 index 00000000..532be437 --- /dev/null +++ b/frontend/src/components/MapView/DataFetch.tsx @@ -0,0 +1,11 @@ +import { FeatureCollection, Geometry } from "geojson"; +import data from "./FeatureCollection.json"; +import { LatLng } from "leaflet"; +const geojsonData: FeatureCollection = data as FeatureCollection; + +export function fetchData( + _topLeft: LatLng, + _bottomRight: LatLng +): FeatureCollection { + return geojsonData; +} diff --git a/frontend/src/components/MapView/FeatureCollection.json b/frontend/src/components/MapView/FeatureCollection.json new file mode 100644 index 00000000..149c82fe --- /dev/null +++ b/frontend/src/components/MapView/FeatureCollection.json @@ -0,0 +1,121 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "name": "Berlin", + "amenity": "Capital City", + "popupContent": "This is Berlin, the capital of Germany!" + }, + "geometry": { + "type": "Point", + "coordinates": [13.4050, 52.5200] + } + }, + { + "type": "Feature", + "properties": { + "name": "Munich", + "amenity": "City", + "popupContent": "This is Munich, known for Oktoberfest!" + }, + "geometry": { + "type": "Point", + "coordinates": [11.5820, 48.1351] + } + }, + { + "type": "Feature", + "properties": { + "name": "Hamburg", + "amenity": "City", + "popupContent": "This is Hamburg, a major port city in northern Germany!" + }, + "geometry": { + "type": "Point", + "coordinates": [9.9937, 53.5511] + } + }, + { + "type": "Feature", + "properties": { + "name": "Frankfurt", + "amenity": "City", + "popupContent": "This is Frankfurt, a major financial hub in Germany!" + }, + "geometry": { + "type": "Point", + "coordinates": [8.6821, 50.1109] + } + }, + { + "type": "Feature", + "properties": { + "name": "Dortmund", + "amenity": "City", + "popupContent": "This is Dortmund, known for its football team!" + }, + "geometry": { + "type": "Point", + "coordinates": [7.4660, 51.5149] + } + }, + { + "type": "Feature", + "properties": { + "name": "Cologne", + "amenity": "City", + "popupContent": "This is Cologne, known for its impressive cathedral!" + } + }, + { + "type": "Feature", + "properties": { + "name": "Leipzig", + "amenity": "City", + "popupContent": "This is Leipzig, known for its vibrant cultural scene!" + }, + "geometry": { + "type": "Point", + "coordinates": [12.3731, 51.3397] + } + }, + { + "type": "Feature", + "properties": { + "name": "Hannover", + "amenity": "City", + "popupContent": "This is Hannover, known for its trade fairs!" + }, + "geometry": { + "type": "Point", + "coordinates": [9.7320, 52.3759] + } + }, + { + "type": "Feature", + "properties": { + "name": "Würzburg", + "amenity": "City", + "popupContent": "This is Würzburg, known for its baroque and rococo architecture!" + }, + "geometry": { + "type": "Point", + "coordinates": [9.9937, 49.7945] + } + }, + { + "type": "Feature", + "properties": { + "name": "Augsburg", + "amenity": "City", + "popupContent": "This is Augsburg, one of Germany's oldest cities!" + }, + "geometry": { + "type": "Point", + "coordinates": [10.8983, 48.3715] + } + } + ] +} diff --git a/frontend/src/components/MapView/MapView.tsx b/frontend/src/components/MapView/MapView.tsx index 459c89af..53715622 100644 --- a/frontend/src/components/MapView/MapView.tsx +++ b/frontend/src/components/MapView/MapView.tsx @@ -11,6 +11,8 @@ import Button from "@mui/material/Button"; import icon from "leaflet/dist/images/marker-icon.png"; import iconShadow from "leaflet/dist/images/marker-shadow.png"; import MapOptions from "./MapOptions"; +import { fetchData } from "./DataFetch"; +import { GeoJSON } from "react-leaflet"; const DefaultIcon = L.icon({ iconUrl: icon, @@ -83,6 +85,8 @@ const MapView: React.FC = () => { + + = 0.10" + } + } + } +} diff --git a/package.json b/package.json index 7c646f83..6d1a34c9 100644 --- a/package.json +++ b/package.json @@ -10,5 +10,9 @@ "deploy": "docker compose up --build -d", "deploy:dev:frontend": "cd frontend/ && npm run dev", "deploy:dev:backend": "cd backend/" + }, + "dependencies": { + "@types/geojson": "^7946.0.14", + "geojson": "^0.5.0" } } From e150071e4cbe9754e312a499d0d454b935e2376a Mon Sep 17 00:00:00 2001 From: Lucas Nandico Date: Mon, 20 May 2024 22:47:44 +0200 Subject: [PATCH 2/5] added api (fake) call on bounding box change Signed-off-by: Lucas Nandico --- frontend/src/components/MapView/DataFetch.tsx | 26 ++++++++++++++++++- frontend/src/components/MapView/MapView.tsx | 22 +++++++++++++--- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/MapView/DataFetch.tsx b/frontend/src/components/MapView/DataFetch.tsx index 532be437..92598240 100644 --- a/frontend/src/components/MapView/DataFetch.tsx +++ b/frontend/src/components/MapView/DataFetch.tsx @@ -1,6 +1,7 @@ import { FeatureCollection, Geometry } from "geojson"; import data from "./FeatureCollection.json"; -import { LatLng } from "leaflet"; +import { LatLng, LatLngBounds } from "leaflet"; +import { useEffect, useState } from "react"; const geojsonData: FeatureCollection = data as FeatureCollection; export function fetchData( @@ -9,3 +10,26 @@ export function fetchData( ): FeatureCollection { return geojsonData; } + +const useGeoData = ( + bounds: LatLngBounds, + zoom: number +): FeatureCollection | undefined => { + const [data, setData] = useState>(); + + useEffect(() => { + const fetchData = async () => { + // Uncomment and adjust the following lines to fetch data from an API + // const url = `https://example.com/api/geo?bounds=${bounds.toBBoxString()}&zoom=${zoom}`; + // const response = await fetch(url); + // const result = await response.json(); + setData(geojsonData as FeatureCollection); + }; + + fetchData(); + }, [bounds, zoom]); + + return data; +}; + +export default useGeoData; diff --git a/frontend/src/components/MapView/MapView.tsx b/frontend/src/components/MapView/MapView.tsx index 53715622..064d233f 100644 --- a/frontend/src/components/MapView/MapView.tsx +++ b/frontend/src/components/MapView/MapView.tsx @@ -6,12 +6,12 @@ import { TileLayer } from "react-leaflet/TileLayer"; import "leaflet/dist/leaflet.css"; import "./MapView.css"; import { useMap, useMapEvents } from "react-leaflet/hooks"; -import L, { LatLng } from "leaflet"; +import L, { LatLng, LatLngBounds } from "leaflet"; import Button from "@mui/material/Button"; import icon from "leaflet/dist/images/marker-icon.png"; import iconShadow from "leaflet/dist/images/marker-shadow.png"; import MapOptions from "./MapOptions"; -import { fetchData } from "./DataFetch"; +import useGeoData, { fetchData } from "./DataFetch"; import { GeoJSON } from "react-leaflet"; const DefaultIcon = L.icon({ @@ -40,8 +40,23 @@ function Btn() { const MapView: React.FC = () => { const center: LatLng = L.latLng([49.5732, 11.0288]); // Initial center coordinates + const [bounds, setBounds] = useState( + new LatLngBounds([0, 0], [0, 0]) + ); + const [zoom, setZoom] = useState(13); + const geoData = useGeoData(bounds, zoom); const [markerPosition, setMarkerPosition] = useState(center); + const UpdateBounds = () => { + useMapEvents({ + moveend: (event) => { + setBounds(event.target.getBounds()); + setZoom(event.target.getZoom()); + }, + }); + return null; + }; + const MapEventsHandler = () => { const map = useMap(); useMapEvents({ @@ -85,7 +100,8 @@ const MapView: React.FC = () => { - + {geoData && } + Date: Tue, 21 May 2024 14:19:17 +0200 Subject: [PATCH 3/5] merged (mocked) api call with state rework in frontend Signed-off-by: Lucas Nandico --- frontend/package-lock.json | 13 +++++++-- frontend/package.json | 12 ++++---- frontend/src/components/MapView/DataFetch.tsx | 17 +++++------ frontend/src/components/MapView/MapView.tsx | 28 ++++++------------- frontend/src/contexts/MapContext.tsx | 4 ++- 5 files changed, 36 insertions(+), 38 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 42f943e6..9d037343 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -16,6 +16,8 @@ "@mui/material": "^5.15.17", "@mui/x-data-grid": "^7.4.0", "@phosphor-icons/react": "^2.1.5", + "@types/geojson": "^7946.0.14", + "geojson": "^0.5.0", "leaflet": "^1.9.4", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -1775,8 +1777,7 @@ "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==", - "dev": true + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==" }, "node_modules/@types/leaflet": { "version": "1.9.12", @@ -2848,6 +2849,14 @@ "node": ">=6.9.0" } }, + "node_modules/geojson": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/geojson/-/geojson-0.5.0.tgz", + "integrity": "sha512-/Bx5lEn+qRF4TfQ5aLu6NH+UKtvIv7Lhc487y/c8BdludrCTpiWf9wyI0RTyqg49MFefIAvFDuEi5Dfd/zgNxQ==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index b0fbe53d..fa36e081 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,16 +12,18 @@ "dependencies": { "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", + "@fontsource/roboto": "^5.0.13", "@mui/icons-material": "^5.15.17", + "@mui/lab": "^5.0.0-alpha.170", "@mui/material": "^5.15.17", - "leaflet": "^1.9.4", - "react-leaflet": "^4.2.1", - "@fontsource/roboto": "^5.0.13", "@mui/x-data-grid": "^7.4.0", - "@mui/lab": "^5.0.0-alpha.170", "@phosphor-icons/react": "^2.1.5", + "@types/geojson": "^7946.0.14", + "geojson": "^0.5.0", + "leaflet": "^1.9.4", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-leaflet": "^4.2.1" }, "devDependencies": { "@types/leaflet": "^1.9.12", diff --git a/frontend/src/components/MapView/DataFetch.tsx b/frontend/src/components/MapView/DataFetch.tsx index 92598240..57cf8615 100644 --- a/frontend/src/components/MapView/DataFetch.tsx +++ b/frontend/src/components/MapView/DataFetch.tsx @@ -1,16 +1,9 @@ import { FeatureCollection, Geometry } from "geojson"; import data from "./FeatureCollection.json"; -import { LatLng, LatLngBounds } from "leaflet"; +import { LatLngBounds } from "leaflet"; import { useEffect, useState } from "react"; const geojsonData: FeatureCollection = data as FeatureCollection; -export function fetchData( - _topLeft: LatLng, - _bottomRight: LatLng -): FeatureCollection { - return geojsonData; -} - const useGeoData = ( bounds: LatLngBounds, zoom: number @@ -18,7 +11,8 @@ const useGeoData = ( const [data, setData] = useState>(); useEffect(() => { - const fetchData = async () => { + /* eslint-disable */ const fetchData = async (_bounds: LatLngBounds) => { + /* eslint-enable */ // Uncomment and adjust the following lines to fetch data from an API // const url = `https://example.com/api/geo?bounds=${bounds.toBBoxString()}&zoom=${zoom}`; // const response = await fetch(url); @@ -26,7 +20,10 @@ const useGeoData = ( setData(geojsonData as FeatureCollection); }; - fetchData(); + fetchData(bounds); + console.log( + `Data fetched for bounds: ${bounds.toBBoxString()} and zoom: ${zoom}` + ); }, [bounds, zoom]); return data; diff --git a/frontend/src/components/MapView/MapView.tsx b/frontend/src/components/MapView/MapView.tsx index 0423c8d1..3062ea85 100644 --- a/frontend/src/components/MapView/MapView.tsx +++ b/frontend/src/components/MapView/MapView.tsx @@ -6,12 +6,12 @@ import { TileLayer } from "react-leaflet/TileLayer"; import "leaflet/dist/leaflet.css"; import "./MapView.css"; import { useMap, useMapEvents } from "react-leaflet/hooks"; -import L, { LatLng, LatLngBounds } from "leaflet"; +import L from "leaflet"; import Button from "@mui/material/Button"; import icon from "leaflet/dist/images/marker-icon.png"; import iconShadow from "leaflet/dist/images/marker-shadow.png"; import MapOptions from "./MapOptions"; -import useGeoData, { fetchData } from "./DataFetch"; +import useGeoData from "./DataFetch"; import { GeoJSON } from "react-leaflet"; import { MapContext } from "../../contexts/MapContext"; @@ -41,16 +41,7 @@ function Btn() { const MapView: React.FC = () => { const { currentMapCache, setCurrentMapCache } = useContext(MapContext); - - const UpdateBounds = () => { - useMapEvents({ - moveend: (event) => { - setBounds(event.target.getBounds()); - setZoom(event.target.getZoom()); - }, - }); - return null; - }; + const geoData = useGeoData(currentMapCache.mapBounds, currentMapCache.zoom); const MapEventsHandler = () => { const map = useMap(); @@ -62,16 +53,12 @@ const MapView: React.FC = () => { selectedCoordinates: event.latlng, }); }, - zoomend: (event) => { - setCurrentMapCache({ - ...currentMapCache, - zoom: event.target.getZoom(), - }); - }, - dragend: (event) => { + moveend: (event) => { setCurrentMapCache({ ...currentMapCache, mapCenter: event.target.getCenter(), + mapBounds: event.target.getBounds(), + zoom: event.target.getZoom(), }); }, }); @@ -117,12 +104,13 @@ const MapView: React.FC = () => { zoom={currentMapCache.zoom} className="map" > + {geoData && } + - ); diff --git a/frontend/src/contexts/MapContext.tsx b/frontend/src/contexts/MapContext.tsx index dba9624a..3c06b926 100644 --- a/frontend/src/contexts/MapContext.tsx +++ b/frontend/src/contexts/MapContext.tsx @@ -1,4 +1,4 @@ -import L, { LatLng } from "leaflet"; +import L, { LatLng, LatLngBounds } from "leaflet"; import React, { createContext, useState, ReactNode } from "react"; //// TYPES //// @@ -11,6 +11,7 @@ export type CoordinatesProps = [number, number]; export type MapCacheProps = { selectedCoordinates: LatLng; mapCenter: LatLng; + mapBounds: LatLngBounds; zoom: number; }; @@ -31,6 +32,7 @@ type MapContextProviderProps = { const defaultMapCache: MapCacheProps = { selectedCoordinates: L.latLng([49.5732, 11.0288]), mapCenter: L.latLng([49.5732, 11.0288]), + mapBounds: L.latLngBounds([49.5732, 11.0288], [49.5732, 11.0288]), zoom: 13, }; From 824da1fefab744c124adbb78b61844fa4f4e98f9 Mon Sep 17 00:00:00 2001 From: Lucas Nandico Date: Tue, 21 May 2024 14:22:19 +0200 Subject: [PATCH 4/5] removed left overs from developement Signed-off-by: Lucas Nandico --- frontend/src/components/MapView/MapView.tsx | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/frontend/src/components/MapView/MapView.tsx b/frontend/src/components/MapView/MapView.tsx index 3062ea85..6cabeae9 100644 --- a/frontend/src/components/MapView/MapView.tsx +++ b/frontend/src/components/MapView/MapView.tsx @@ -7,7 +7,6 @@ import "leaflet/dist/leaflet.css"; import "./MapView.css"; import { useMap, useMapEvents } from "react-leaflet/hooks"; import L from "leaflet"; -import Button from "@mui/material/Button"; import icon from "leaflet/dist/images/marker-icon.png"; import iconShadow from "leaflet/dist/images/marker-shadow.png"; import MapOptions from "./MapOptions"; @@ -23,22 +22,6 @@ const DefaultIcon = L.icon({ L.Marker.prototype.options.icon = DefaultIcon; -function Btn() { - const map = useMap(); - return ( - - ); -} - const MapView: React.FC = () => { const { currentMapCache, setCurrentMapCache } = useContext(MapContext); const geoData = useGeoData(currentMapCache.mapBounds, currentMapCache.zoom); From c4e9108015a6994ac75aa8449ab1c6d5889d2495 Mon Sep 17 00:00:00 2001 From: Lucas Nandico Date: Tue, 21 May 2024 14:52:49 +0200 Subject: [PATCH 5/5] changed current position marker to red circle Signed-off-by: Lucas Nandico --- frontend/src/components/MapView/MapView.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/MapView/MapView.tsx b/frontend/src/components/MapView/MapView.tsx index 6cabeae9..ac9acb73 100644 --- a/frontend/src/components/MapView/MapView.tsx +++ b/frontend/src/components/MapView/MapView.tsx @@ -22,6 +22,19 @@ const DefaultIcon = L.icon({ L.Marker.prototype.options.icon = DefaultIcon; +const svgIcon = L.divIcon({ + html: ` + + + + + + `, + className: "", // Optional: add a custom class name + iconSize: [34, 34], + iconAnchor: [17, 17], // Adjust the anchor point as needed +}); + const MapView: React.FC = () => { const { currentMapCache, setCurrentMapCache } = useContext(MapContext); const geoData = useGeoData(currentMapCache.mapBounds, currentMapCache.zoom); @@ -46,7 +59,7 @@ const MapView: React.FC = () => { }, }); return ( - +