How to add properties to leaflet-geoman layer when using the toolbar - leaflet

I need to add custom props to my created polys. To do so currently when the user select in the toolbar the polygon and create a shape, on the create event I convert it to json remove it from the map add the custom props to the json and reload the newly created layer.
this.map.on('pm:create', e => {
const id = getUID();
const leafId = e.layer._leaflet_id;
const featureGroup = L.featureGroup().addLayer(e.layer);
this.map.eachLayer(layer => {
if (layer._leaflet_id === leafId) {
this.map.removeLayer(layer);
}
});
const data = featureGroup.toGeoJSON();
data.features[0].properties = {
id,
name: `Zone ${id}`
};
this.zoneService.add({id, data: JSON.stringify(data)})
.pipe(
switchMap((res) => this.zoneService.getAll().pipe(this.addToMap(this.map)))
).subscribe();
});
This is working but I feel I am not doing something right here. Adding removing Adding, there must be a better way. Thanks for any help

Related

Add interactivity to POIs from different tilesets

I'm working on Mapbox Studio Tutorial and practicing adding interactivity on POIs on map.
https://docs.mapbox.com/help/tutorials/add-points-pt-3/
map.on('click', (event) => {
// If the user clicked on one of your markers, get its information.
const features = map.queryRenderedFeatures(event.point, {
layers: ['layer1',"layer2","layer3"]
});
if (!features.length) {
return;
}
const feature = features[0];
/*
Create a popup, specify its options
and properties, and add it to the map.
*/
const popup = new mapboxgl.Popup({ offset: [0, -15] })
.setLngLat(feature.geometry.coordinates)
.setHTML(
`<h3>${feature.properties.title}</h3><p>${feature.properties.description}</p>`
)
.addTo(map);
});
The error I get is that the title of POIs from layer2 and layer3 is shown as "undefined" while layer1's title can be shown when clicking it on the map.
I think "undefined" comes because the title is not stored in the feature property but have no clear idea how to do that correctly.
I tried some codes I got from the internet such as below:
if (features.length > 0) {
// Loop feature and concatenate property as HTML strings
let propertiesHTML = '';
features.forEach(feature => {
Object.entries(feature.properties).forEach(([key, value]) => {
propertiesHTML += `<p><strong>${key}:</strong> ${value}</p>`;
});
});
// Create and add a popup
const popup = new mapboxgl.Popup({ offset: [0, -15] })
.setLngLat(features[0].geometry.coordinates)
.setHTML(propertiesHTML)
.addTo(map);
With code at least map is shown, but there is no interactive popup shown when I click it.

How can i stop this onClick event from rerendering the entire treemap echart i have?

How can i stop this onClick event from rerendering the entire treemap echart i have?
I have basically a echarts treemap https://echarts.apache.org/examples/en/editor.html?c=treemap-disk as a functional component in react. I need to be able to apply filters and "grey out" certain tree nodes that dont fit the criteria. This functionality works currently but it rerenders the echart so that the user must restart from the top level and clicktheir way through all the way to the bottom level. How can i avoid the rerendering? This is a similar example i have where clicking the node displays data but also rerenders the chart losing where the node was in the map.
const onChartClick = params => {
if (params.treePathInfo.length === 9) {
setDrawerData(params);
}
};
useEffect(() => {
props.setDrawerData(drawerData);
}, [drawerData]);
const onEvents = {
click: onChartClick,
}; ```
you can try to put your chart on useMemo it works for me :
const [dataLoaded, setdataLoaded] = useState(true);
const onChartClick = params => {
if (params.treePathInfo.length === 9) {
setDrawerData(params);
}
};
useEffect(() => {
props.setDrawerData(drawerData);
setdataLoaded(false)
}, [drawerData]);
const onEvents = {
click: onChartClick,
};
const MemoChart = useMemo(() => <Charts
option={option}
onEvents={onEvents}
/>, [dataLoaded]);

Draft.js Mention Plugin is not working after rendering contentState to the editor

I am using mentions with the draft.js (like this #yourname) and sending to the database to save and fetching it to render on the web page but things are not working as expected.
On Saving to the database ->
const contentState = editorState.getCurrentContent();
const currentStateData = convertToRaw(contentState);
const richStringifyValue = JSON.stringify(currentStateData);
// sending richStringifyValue to save in Mongo DB
On Fetch and set in editor ->
const [editorState, setEditorState] = React.useState(() => EditorState.createEmpty());
const parsedData = JSON.parse(post.contentStyled);
const fromRawData = convertFromRaw(parsedData );
EditorState.createWithContent(fromRawData);
// here is the view rendered part -
<Editor
readOnly={true}
editorState={editorState}
/>
But after setting in editor (after the data fetched from API) my mentions (#... #... #...) lost the CSS. What should we do?
On Using Edit ->
On fetch and setting again in Editor ->
I don't know why is that happening, please help to resolve this issue!
You should do the following:
const [editorState, setEditorState] = React.useState(() => {
const parsedData = JSON.parse(post.contentStyled);
const fromRawData = convertFromRaw(parsedData );
return EditorState.createWithContent(fromRawData);
});
// ...
<Editor
readOnly={true}
editorState={editorState}
/>
If you override the editorState with a new created state you are removing all the decorators which were added by the plugins.
Dominic's answer made me realize what was going on with the decorators, but his approach didn't work for me either.
What I ended up doing instead was to avoid mounting the editor altogether until I have the data inside the EditorState:
const [editorState, setEditorState] = React.useState(null);
useEffect(() => {
const parsedData = JSON.parse(post.contentStyled);
const fromRawData = convertFromRaw(parsedData );
setEditorState(() => EditorState.createWithContent(fromRawData));
}, []);
editorState && <Editor readOnly={true} editorState={editorState}/>
This way you insert your persisted data into the state before instantiating the component. And afterwards any other plugin adding decorators will work as intended.

Leaflet L.Icon marker not removing on Map reload

Map contains two types of markers
Circle icon is added by:
let circle = new customCircleMarker([item.latitude, item.longitude], {
color: '#2196F3',
fillColor: '#2196F3',
fillOpacity: 0.8,
radius: radM,
addressId: item.address_id
}).bindTooltip(`Address: <b>${item.address}</b><br/>
Patients from this address: <b>${item.total_patients}</b><br/>
Production: <b>$${item.total_production}</b><br/>`)
.addTo(this.mapInstance).on('click', this.circleClick);
Icon marker is added in the following method:
// create marker object, pass custom icon as option, pass content and options to popup, add to map
// create marker object, pass custom icon as option, pass content and options to popup, add to map
L.marker([item.latitude, item.longitude], { icon: chartIcon })
.bindTooltip(`Address: <b>${item.address}</b><br/>
Patients from this address: <b>${item.total_patients}</b><br/>
Production: <b>$${item.total_production}</b><br/>`)
.addTo(this.mapInstance).on('click', this.circleClick);
On clearing map Icon marker is not removed
Map clearing function:
if (this.mapInstance) {
for (let i in this.mapInstance._layers) {
if (this.mapInstance._layers[i]._path !== undefined) {
try {
this.mapInstance.removeLayer(this.mapInstance._layers[i]);
} catch (e) {
console.log('problem with ' + e + this.mapInstance._layers[i]);
}
}
}
}
You're checking for a _path property before removing. It will skip the L.Marker layers because they don't have a _path property, only vector layers (extended from L.Path) do.
If you ever need to delete only certain layer types from your map you are best off by grouping them in a grouping layer like L.LayerGroup or L.FeatureGroup and then using their clearLayers method:
var layerGroup = new L.LayerGroup([
new L.Marker(...),
new L.CircleMarker()
]).addTo(map);
layerGroup.clearLayers();
If that is not an option you could iterate the map's layers and then check the instance of the layer:
function customClearLayers () {
map.eachLayer(function (layer) {
if (layer instanceof L.Marker || layer instanceof L.CircleMarker) {
map.removeLayer(layer);
}
});
}

Cannot set style on leaflet L.geoJSON layer

I am having a bit of trouble setting the style on a L.geoJSON based layer,my code looks like this:
var addProperties = function addProperties(prop,map)
{
//the API does not seem to support adding properties to an existing feature,
//the idea here is simple:
//(1) currentFeature.toGeoJson() needs to be called to obtain a json representation
//(2) set the properties on the geojson
//(3) create a new feature based on the geojson
//(4) remove res and add the new feature as res
var style = function style(feature){
var markerStyle = {
draggable: 'true',
icon: L.AwesomeMarkers.icon({
icon: 'link',
prefix: 'glyphicon',
markerColor: 'red',
spin: true
})
};
if(feature.geometry.type==='Point')
return markerStyle;
};
var onEachFeature = function onEachFeature(feature,layer){
console.log("Inside on each feature,checking to see if feature was passed to it ",feature);
layer.on('click',function(e){
//open display sidebar
console.log("Checking to see if setupTabs exists ",setupTabs);
setupTabs('#display-feature-tabs');
console.log("Checking to see if featureInfo exists ",featureInfo);
var featureInfoAPI =featureInfo('feature-properties');
featureInfoAPI.swap(feature.properties);
setTimeout(function() {
sidebar.show();
}, 100);
});
};
console.log("Inside add properties");
var geoJSON,feature;
if(res != null)
{
geoJSON = res.toGeoJSON();
geoJSON.properties = prop;
console.log(geoJSON);
feature = L.geoJson(geoJSON,{style:style,onEachFeature:onEachFeature});
console.log("The new feature that has been created ",feature);
removeFeature(map);
addFeature(feature);
feature.addTo(map);
}
};
I have also tried the style method as well,I am looking to add styles to the active layer for points(styles will also be added for lines and polylines by type).
To style point features, you should use pointToLayer option instead.