I am trying to implement OpenLayers6 ("ol": "^6.14.1"
) into a ReactJS project, but all the documentation is created in normal JS files and I can't find any good examples or tutorials with functional components and OpenLayers6.
I have troubles figuring out how to implement the majority of things, because the docs seem to ignore the lifecycle of React.
What I've managed to do until now is to add a marker and a popup right above the marker; to close the popup and delete the marker by deleting the whole vector layer(which seems overkill).
import { useState, useEffect, useRef } from 'react';
// import ModalUI from '../UI/ModalUI';
import classes from './MapUI.module.css';
import { drawerActions } from '../../store/drawer-slice';
import 'ol/ol.css';
import { Map, View, Overlay, Feature } from 'ol';
import Point from 'ol/geom/Point';
import { Vector as VectorLayer } from 'ol/layer';
import VectorSource from 'ol/source/Vector';
import { fromLonLat, toLonLat } from 'ol/proj';
import { toStringHDMS } from 'ol/coordinate';
import TileLayer from 'ol/layer/Tile';
import OSM from 'ol/source/OSM';
import PopUp from './PopUp';
import { useDispatch } from 'react-redux';
export default function MapUI() {
const mapRef = useRef();
const popup = useRef();
const [coordinates, setCoordinates] = useState('');
const [newMarker, setNewMarker] = useState(
new Feature({
geometry: new Point([[]]),
name: '',
})
);
const [newMarkersLayer, setNewMarkersLayer] = useState(
new VectorLayer({
properties: { name: 'newMarkers' },
source: new VectorSource({
features: [newMarker],
}),
})
);
const closePopup = () => {
map.getOverlayById('map-popup').setPosition(undefined);
map.removeLayer(newMarkersLayer);
};
const [map] = useState(
new Map({
target: '',
layers: [
new TileLayer({
source: new OSM(),
}),
new VectorLayer({
properties: { name: 'existingMarkers' },
source: new VectorSource({
// features: [marker],
}),
}),
],
view: new View({
center: fromLonLat([26.08, 44.46]),
zoom: 15,
minZoom: 10,
maxZoom: 20,
}),
})
);
useEffect(() => {
const overlay = new Overlay({
element: popup.current,
id: 'map-popup',
autoPan: {
animation: {
duration: 250,
},
},
});
// console.log('useEffect in MapUI.jsx');
map.addOverlay(overlay);
map.setTarget(mapRef.current);
map.on('singleclick', function (evt) {
map.addLayer(newMarkersLayer);
newMarker.getGeometry().setCoordinates(evt.coordinate);
setCoordinates(toStringHDMS(toLonLat(evt.coordinate)));
overlay.setPosition(evt.coordinate);
});
}, [map]);
return (
<>
<div
style={{ height: '100%', width: '100%' }}
ref={mapRef}
className='map-container'
/>
<div id='map-popup' className={classes['ol-popup']} ref={popup}>
<PopUp coordinates={coordinates} closePopup={closePopup} />
</div>
</>
);
}
The project in the end will have an array of markers that will be requested from a back-end and will populate the given map while also keeping the ability to add new markers to the map (and to the back-end).
The general issue that I face is with how all ol objects are used in the documentation. Everything is just created in a file using const
and then operated upon.
But in React I have to use useEffect()
and useState()
and can't just create dependencies or manipulate state however the docs say.
I am looking for some guidelines on how to properly use OpenLayers6 in React. On this note I have some questions:
- How can I remove a marker without removing the whole layer ?
- How can I make a marker stay on the map ?
- How can I render an array or markers on the map ?
- Is it correct the way I use
useState()
to create the initial map ? - Is it correct the way I use
useState()
to keep the marker and the VectorLayer on which the marker will be placed ?