From 6381152fb7c72066bd0676bce93f6a082a23addb Mon Sep 17 00:00:00 2001 From: robinvandermolen Date: Tue, 10 Dec 2024 11:48:29 +0100 Subject: [PATCH] :sparkles: [open-formulieren/open-forms#2177] Map component using geoJson instead of coordinates The map component now uses and stores geoJson data, instead of coordinates. This means we can display and work with multiple shapes. One important change is that geoJson saves coordinates as lng-lat, instead of lat-lng --- src/components/Map/Map.stories.jsx | 9 +++- src/components/Map/index.jsx | 81 ++++++++++++++++++++++++++---- src/formio/components/Map.jsx | 8 +-- 3 files changed, 82 insertions(+), 16 deletions(-) diff --git a/src/components/Map/Map.stories.jsx b/src/components/Map/Map.stories.jsx index 3d9c8f100..62653ac27 100644 --- a/src/components/Map/Map.stories.jsx +++ b/src/components/Map/Map.stories.jsx @@ -16,7 +16,14 @@ export default { component: LeafletMap, decorators: [withMapLayout, ConfigDecorator], args: { - markerCoordinates: [52.1326332, 5.291266], + geoJsonFeature: { + type: 'Feature', + properties: {}, + geometry: { + type: 'Point', + coordinates: [5.291266, 52.1326332], + }, + }, defaultCenter: [52.1326332, 5.291266], defaultZoomLevel: 12, disabled: false, diff --git a/src/components/Map/index.jsx b/src/components/Map/index.jsx index e2139e7c5..8f59fba72 100644 --- a/src/components/Map/index.jsx +++ b/src/components/Map/index.jsx @@ -1,8 +1,12 @@ +import * as Leaflet from 'leaflet'; +import 'leaflet-draw/dist/leaflet.draw.css'; import {GeoSearchControl} from 'leaflet-geosearch'; +import 'leaflet/dist/leaflet.css'; import PropTypes from 'prop-types'; -import React, {useCallback, useContext, useEffect} from 'react'; +import React, {useCallback, useContext, useEffect, useRef} from 'react'; import {defineMessages, useIntl} from 'react-intl'; -import {MapContainer, Marker, TileLayer, useMap, useMapEvent} from 'react-leaflet'; +import {FeatureGroup, MapContainer, Marker, TileLayer, useMap, useMapEvent} from 'react-leaflet'; +import {EditControl} from 'react-leaflet-draw'; import {useGeolocation} from 'react-use'; import {ConfigContext} from 'Context'; @@ -60,19 +64,38 @@ const useDefaultCoordinates = () => { }; const LeaftletMap = ({ - markerCoordinates, - onMarkerSet, + geoJsonFeature, + onGeoJsonFeatureSet, defaultCenter = DEFAULT_LAT_LNG, defaultZoomLevel = DEFAULT_ZOOM, disabled = false, }) => { + const ref = useRef(); const intl = useIntl(); const defaultCoordinates = useDefaultCoordinates(); - const coordinates = markerCoordinates || defaultCoordinates; + const coordinates = defaultCoordinates; const modifiers = disabled ? ['disabled'] : []; const className = getBEMClassName('leaflet-map', modifiers); + const onFeatureCreate = event => { + // Remove the old layers and add the new one. + // This limits the amount of features to 1 + const newLayer = event.layer; + ref.current?.clearLayers(); + ref.current?.addLayer(newLayer); + + onGeoJsonFeatureSet(ref.current?.toGeoJSON()); + }; + + useEffect(() => { + if (!ref.current) { + return; + } + ref.current?.clearLayers(); + Leaflet.geoJSON(geoJsonFeature).addTo(ref.current); + }, [geoJsonFeature, ref.current]); + return ( <> + + + {coordinates ? ( <> ) : null} console.log('TODO')} options={{ showMarker: false, showPopup: false, @@ -114,16 +155,34 @@ const LeaftletMap = ({ /> {/*{disabled ? : }*/} - {markerCoordinates && markerCoordinates.length && ( - - )} + {/*{markerCoordinates && markerCoordinates.length && (*/} + {/* */} + {/*)}*/} ); }; LeaftletMap.propTypes = { - markerCoordinates: PropTypes.arrayOf(PropTypes.number), - onMarkerSet: PropTypes.func, + geoJsonFeature: PropTypes.shape({ + type: PropTypes.oneOf(['Feature']).isRequired, + properties: PropTypes.object, + geometry: PropTypes.oneOfType([ + PropTypes.shape({ + type: PropTypes.oneOf(['Point']).isRequired, + coordinates: PropTypes.arrayOf(PropTypes.number).isRequired, + }), + PropTypes.shape({ + type: PropTypes.oneOf(['LineString']).isRequired, + coordinates: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number)).isRequired, + }), + PropTypes.shape({ + type: PropTypes.oneOf(['Polygon']).isRequired, + coordinates: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number))) + .isRequired, + }), + ]).isRequired, + }), + onGeoJsonFeatureSet: PropTypes.func, disabled: PropTypes.bool, }; diff --git a/src/formio/components/Map.jsx b/src/formio/components/Map.jsx index f527ac270..c82cc9bcc 100644 --- a/src/formio/components/Map.jsx +++ b/src/formio/components/Map.jsx @@ -91,7 +91,7 @@ export default class Map extends Field { super.destroy(); } - onMarkerSet(newLatLng) { + onGeoJsonSet(newLatLng) { this.setValue(newLatLng, {modified: true}); } @@ -100,7 +100,7 @@ export default class Map extends Field { const {lat = defaultLat, lng = defaultLon} = this.component?.initialCenter || {}; const defaultCenter = [lat, lng]; - const markerCoordinates = this.getValue(); + const geoJsonFeature = this.getValue(); const container = this.refs.mapContainer; const zoom = this.component.defaultZoom; @@ -111,8 +111,8 @@ export default class Map extends Field {