Style layer.editing in leaflet - leaflet

I'm working on getting the events of deletion and edition outside the box of leaflet, meaning using buttons binded to events instead of the drawtoolbar.
The editing button calls layer.editing.enable();
What I've done earlier is style the polygones under editition mode using an extend of the class Edit:
L.Edit.PolyVerticesEdit = L.Edit.PolyVerticesEdit.extend({
options: {
icon: new L.DivIcon({
className: 'edit-marker',
iconSize: [28, 28],
html: '<i class="fa fa-circle fa-2x fa-circle"></i>'
})
}
});
So, when I do an edit using the drawtoolbar, the shapes are styled correctly. However, when I use the button( layer.editing.enable()) the shapes are under default styles. So my conclusion was that draw Toolbar doesn't class the same method as layer.editing.enable(); from here comes my question.
How can I style the polygon vertices using layer.editing.enable() ?
Case of using buttons
case of using toolbar

Related

mapbox - why do all my features disappear when I change the map style

I am using mapbox gl js to create some maps. To style the map, I use the following code;
// add a styled map to the contianer id map
const map = new mapboxgl.Map({
container: 'map', // container ID
style: 'mapbox://styles/bogdanvectuel/cjq1fxndm5qr02roa06jcfb61',
});
Above is a public map style from bogdanvectuel. I wanted to changed to my own style but when I change over to a mapbox offered style like the outdoors one, all of a sudden all my skiareas points disappear which is defined as follows;
/** Add ski areas but just the center point as source / dot **/
map.addSource('aa_winter_spoorts_points', {
type: 'vector',
// aa_winter_spoorts_points
url: 'mapbox://username.aa_winter_spoorts_points'
});
/** Add ski areas but just the center point as layer / dot **/
map.addLayer({
'id': 'aa_winter_spoorts_points',
'type': 'symbol', // background, fill, line, symbol, raster, circle, fill-extrusion, heatmap, hillshade, sky.
'source': 'aa_winter_spoorts_points',
'source-layer': 'skiareas',
'layout': {
'visibility': 'visible',
'icon-image': 'airfield-11',
'icon-allow-overlap': true
},
});
I cannot understand why that would happen. Any pointers appreciated.
When you use addSource() and addLayer(), you are manually adding the source and layer to the map's style. Changing to a different style after map load essentially starts over from scratch. Using setStyle() will clear the existing style, so anything you added after map load would need to be added again.
One approach may just be to move your addSource() and addLayer() calls to a new function like addCustomSourcesAndLayers(). You could then call that after the first map load, and again after setStyle()
You can see this in action in geojson.io. We allow you to switch to a different style and then re-draw the user-defined layers after the new style loads.

Need proper way to render jsx component inside Leaflet popup when using geojson pointToLayer function

Hi is there any way to pass jsx component to bindPopup function so I can push redux commands on button click?
pointToLayer={(
geoJsonPoint: Feature<Point, DeviceProperties>,
latlng,
) => {
const marker = L.marker(latlng);
marker.setIcon(
markerIcon({ variant: geoJsonPoint.properties.relation }),
);
const sddds = (
<div className="font-quicksand">
<h2>{geoJsonPoint.properties.id}</h2>
<h2>{geoJsonPoint.properties.name}</h2>
<p>{geoJsonPoint.properties.description}</p>
<p>{geoJsonPoint.properties.ownerId}</p>
<a
onClick={() => {
dispatch(setDevice(geoJsonPoint.properties));
}}
>
Open device details
</a>
</div>
);
marker.bindPopup(renderToString(sddds));
return marker;
}}
I know I can use react leaflet component but that way I cant pass props into every marker options (I mean marker as layer).
So this has been discussed a bit. There is an issue in the react-leaflet repo discussing this, whose conclusion is to simply use vanilla JS within the bindPopup method to create your popup. I don't like this solution at all, especially when you're trying to use very react oriented event handlers (like react-redux actions) from within a popup.
The question React-leaflet geojson onEachFeature popup with custom react component was asked, which you may have read, as you use react's renderToString method in your code. But as you've probably discovered, this does not maintain any interactivity or JS that your JSX may include. The answerer there came up with the idea of using a modal instead of a popup, but that doesn't exactly answer your question or truly using JSX in a popup based off of a point-layer geojson.
Ultimately, you will not be able to return JSX from the pointToLayer function that is interactive. I think this would be a nice feature that react-leaflet doesn't currently implement. Within the closure of the pointToLayer function, there's no good way to directly write fully functional JSX.
I played with this for a bit, trying to harness pointToLayer and save the feature of each iteration to state, and then render a Marker with Popup from that, but it got me thinking - why bother? Just ditch the GeoJSON component altogether and render your Markers and Popups directly from the JSON object. Like this:
{myGeoJson.features.map((feature, index) => {
return (
<Marker
key={index}
position={L.latLng(feature.geometry.coordinates.reverse())}
>
<Popup>
<button
onClick={() => { yourReduxAction() }}
>
Click meeee
</button>
</Popup>
</Marker>
);
})}
Working sandbox
In this way, you need to work a little harder by manually transforming your GeoJSON into Markers with Popups, but not nearly as hard as trying to bend over backwards by going from JSX (<GeoJSON />) to vanilla JS (pointToLayer) back to JSX (<Popup />).
These are two solutions I have come to and want to share if someone is having same problem.
My problem with using leaflet-react Popup component is that it will not pass geojson properties to marker layer when I just map over geojson object because react-leaflet Marker does not have api for feature like geojson layer does and I need to access those properties via marker layers in other parts of map.
Solution 1:
Use ReactDOM.render() inside pointToLayer method, react will show warning about pure functions but it will work. You just shoud not render imported component because it will complain about store and redux provider, instead paste component code inside render. If you want to avoid warnings create another function / hook and render code inside its useEffect() to container (div or something).
Here is example:
const popup = L.popup();
const marker = L.marker(latlng);
const container = L.DomUtil.create('div');
render(
<div>
<h2>{props.id}</h2>
<h2>{props.name}</h2>
<p>{props.description}</p>
<p>{props.ownerId}</p>
<a onClick={() => dispatch(setDevice(geoJsonPoint.properties))}></a>
</div>,
container,
);
popup.setContent(container);
marker.bindPopup(popup);
return marker;
With custom hook / function:
const useRenderPopup = (props) => {
const container = L.DomUtil('div');
const dispatch = useAppDispatch()
useEffect(() => {
render(
<div>
<h2>{props.id}</h2>
<h2>{props.name}</h2>
<p>{props.description}</p>
<p>{props.ownerId}</p>
<a onClick={() => dispatch(setDevice(props.geoJsonPoint.properties))}></a>
</div>,
container,
);
},[])
return container;
}
and just call this function like popup.setContent(useRenderPopup(someprop)), this way there will be no warning.
Solution 2:
Render everything static with renderToString() and other stuff that need to trigger redux update attach event listeners.
const popup = L.popup();
const marker = L.marker(latlng);
const link = L.DomUtil.create('a');
const container = L.DomUtil.create('div');
const content = <DeviceSummary {...geoJsonPoint.properties} />;
marker.setIcon(markerIcon({ variant: geoJsonPoint.properties.relation }));
link.addEventListener('click', () =>
dispatch(setDevice(geoJsonPoint.properties)),
);
link.innerHTML = 'Show device details';
container.innerHTML = renderToString(content);
container.appendChild(link);
popup.setContent(container);
marker.bindPopup(popup);
return marker;
Here DeviceSummary component is static so I render it as a string and later append link with redux callback added as event listener to it.
(both solutions except custom function example goes into pointToLatyer method inside geoJSON layer)

Add Maki Icon instead of Mapbox Geocoder Marker

Just a quick question, if anyone has ever replaced the Mapbox default marker with a Maki icon. I've only seen examples of using Maki icons for point tilesets/layers, but I'm wanting to use it for non-tileset features, specifically replacing the marker that adds after geocoding, at the location of the address just geocoded.
Or, trying to find something that is similar to Google Maps symbols below. Any suggestions appreciated.
var pinImage = {
path: google.maps.SymbolPath.CIRCLE,
fillColor: '#ff4b00',
fillOpacity: .9,
scale: 5,
strokeColor: '#CDDC39',
strokeWeight: 0,
strokeOpacity: .5
}
The MapboxGeocoder control has a marker option https://github.com/mapbox/mapbox-gl-geocoder/blob/master/API.md#parameters which controls the marker placed on the map when you select a result.
There is an example at https://docs.mapbox.com/mapbox-gl-js/example/custom-marker-icons/ to create a Marker with a custom icon.
So you could create an HTML Element which contains either an SVG or PNG icon from Maki and use that as your element in your custom Marker passed to the MapboxGeocoder control.
I think this sample is what you are looking for Use a custom render function with a geocoder
It allows you to add a custom render function including the icon...
var geocoder = new MapboxGeocoder({
accessToken: mapboxgl.accessToken,
types: 'poi',
// see https://docs.mapbox.com/api/search/#geocoding-response-object for information about the schema of each response feature
render: function(item) {
// extract the item's maki icon or use a default
var maki = item.properties.maki || 'marker';
return (
"<div class='geocoder-dropdown-item'><img class='geocoder-dropdown-icon' src='https://unpkg.com/#mapbox/maki#6.1.0/icons/" +
maki +
"-15.svg'><span class='geocoder-dropdown-text'>" +
item.text +
'</span></div>'
);
},
mapboxgl: mapboxgl
});
map.addControl(geocoder);

How to add popup or bind popup to a point/marker in Leaflet FlowMap without breaking the 'flow' display?

I am using the following leaflet plugin:
https://github.com/jwasilgeo/Leaflet.Canvas-Flowmap-Layer
I am having issues adding a popup to the map when a user clicks on a point.
L.marker([pts[p].lat, pts[p].lng], {
icon: new L.DivIcon({
html: '<div>Test</div>'
})
}).addTo(map).bindPopup('A pretty CSS3 popup.<br> Easily customizable.')
.openPopup();
The popup shows, but I am unable to get the actual flowmap lines to show up. Is there anyway to allow for a popup and to allow for the lines to show up underneath it?
You can use the fact that the CanvasFlowmapLayer extends L.GeoJSON
You just need to overload the method creating the marker and add your popup there ...
var oneToManyFlowmapLayer = L.canvasFlowmapLayer(geoJsonFeatureCollection, {
pointToLayer: function(geoJsonPoint, latlng) {
var marker = L.circleMarker(latlng);
return marker.bindPopup('' + latlng)
},
// et caetera
Check it out here: https://yafred.github.io/Leaflet.Canvas-Flowmap-Layer/docs/main/

How to use out side leaflet.draw control with leaflet.snap?

First of all I would like to thank you all for amazing libraries like leaflet/leaflet.draw and leaflet.snap.
What I want to do is outside leaflet.draw control with supporting leaflet snap. This is nicely working with in side map draw control.
Below I show how did I call outside leaflet draw control:
<div><button id="draw_mark1" onclick="drawMarker1()" >Draw Marker1</button></div>
<div><button id="draw_polyline1" onclick="drawPolyline1()" >Draw Polyline1</button></div>
function drawMarker1(){
var markerDrawer1 = new L.Draw.MarkerA(map, { icon: new myIcon_xx() });
markerDrawer1.enable();
}
function drawPolyline1(){
var polylineDrawer1 = new L.Draw.PolylineType1(map);
polylineDrawer1.enable();
}
note:- leaflet.snap not in the tag list. I want to tag it too.