I have a React (Vite) app in which I want to conditionally allow users to add markers when they click on the map.
I have the following logic
a useMap hook which does pretty much all the logic of rendering of the map and clicking on existing markers
import { Map } from "ol";
import { Vector as LayerVector } from "ol/layer";
import { Vector as SourceVector } from "ol/source";
import { defaults, ScaleLine } from "ol/control";
import proj4 from "proj4";
import { register } from "ol/proj/proj4";
import { useEffect, useMemo, useRef } from "react";
import { redMarker } from "../styles/markerStyles";
import { initMap } from "../utils/initMap";
import { MarkerDetails, onMarkerClick } from "../utils/onMarkerClick";
import { addMarker } from "../utils/addMarker";
const useMap = (
setMap: React.Dispatch<React.SetStateAction<Map | null>>,
setMarkerDetails: React.Dispatch<React.SetStateAction<MarkerDetails | null>>,
isEditEnabled: boolean
): void => {
proj4.defs(
"EPSG:2056",
"+proj=somerc +lat_0=46.95240555555556 +lon_0=7.439583333333333 +k_0=1 +x_0=2600000 +y_0=1200000 +ellps=bessel +towgs84=674.374,15.056,405.346,0,0,0,0 +units=m +no_defs"
);
register(proj4);
const { iconFeature, backgroundLayer, view } = initMap();
//set style from an array fetched from api
iconFeature.setStyle(redMarker);
const mapRef = useRef<Map | null>(null);
// const vectorSource = useMemo(() => {
// return new SourceVector({
// features: [iconFeature],
// });
// }, [iconFeature]);
// const markerLayer = useMemo(() => {
// return new LayerVector({
// source: vectorSource,
// });
// }, [vectorSource]);
useEffect(() => {
const vectorSource = new SourceVector({
features: [iconFeature],
});
const markerLayer = new LayerVector({
source: vectorSource,
});
if (!mapRef.current) {
mapRef.current = new Map({
target: "map",
controls: defaults().extend([
new ScaleLine({
units: "metric",
}),
]),
layers: [backgroundLayer, markerLayer],
view: view,
});
setMap(mapRef.current);
}
mapRef.current.setTarget("map");
//TODO: FIX THIS now working with edit mode
// if (isEditEnabled) addMarker(mapRef.current, vectorSource);
onMarkerClick(mapRef.current, iconFeature, setMarkerDetails);
return () => mapRef.current?.setTarget(undefined);
}, [
backgroundLayer,
iconFeature,
isEditEnabled,
setMap,
setMarkerDetails,
view,
]);
};
export default useMap;
addMarker.ts function
import { Feature, Map } from "ol";
import { Point } from "ol/geom";
import { Vector as SourceVector } from "ol/source";
import { redMarker } from "../styles/markerStyles";
export const addMarker = (map: Map, vectorSource: SourceVector): void => {
map.on("click", function (evt) {
//add marker on click
const location = evt.coordinate;
console.log("cal");
const x = location[0];
const y = location[1];
const createdMarker = new Feature({
geometry: new Point([x, y]),
name: "add a meaningful name",
});
createdMarker.setStyle(redMarker);
vectorSource.addFeature(createdMarker);
vectorSource.changed();
});
};
the isEditingEnabled flag comes from a setState defined in a Map component and on a click of a button I toggle this on/off
For some reason, even if the flag is ON, the marker never gets added. If I don't use the flags and I allow the user to be able to add marker at any point, it works.
I am using Open Layer 6. Any suggestions?
Also, will it hurt if I have another function that listens to the same click event but for when you click an existing marker.
import { Feature, Map } from "ol";
export type MarkerDetails = {
title?: string;
description?: string;
};
export const onMarkerClick = (
map: Map,
iconFeature: Feature,
setMarkerDetails: React.Dispatch<React.SetStateAction<MarkerDetails | null>>
): void => {
map.on("click", function (evt) {
const feature = map.forEachFeatureAtPixel(evt.pixel, function (feature) {
return feature;
});
if (feature === iconFeature) {
//get values of object - open popup
const selectedMarker = iconFeature.getProperties() as MarkerDetails;
setMarkerDetails(selectedMarker);
}
if (!feature) {
setMarkerDetails(null);
}
});
};
Thank you