Using the same Plug-in for multiple purposes in leaflet - leaflet

I am trying to use the leaflet-draw tool for two different things:
as a "regular" tool to create new geometries
if I draw a line, I perform some calculations with turf.js, giving me points nearby.
I've set up two individual draw controls for each purpose. For the second, I have all but the draw:polyline disabled. The problem: I save my elements with the
map.on('draw:created', function(){...});
"command". But this way I (or the eventhandler, respectively :)) cant differentiate, if the line was drawn with the first or the second button. So basically i can use the draw tool either for one thing or the other. Is there a way where I can use the same tool for different applications on the same map?
Thanks for any hints or work arounds.

An alternative would be to use Leaflet-Geoman instead of Leaflet-Draw.
There you can create copies of Draw instances and add them a new shape name:
// copy a rectangle and customize its name, block, title and actions
map.pm.Toolbar.copyDrawControl('Polygon', {
name: 'PolygonCopy',
block: 'custom',
title: 'Display text on hover button',
actions: ['cancel', 'removeLastVertex', 'finish'],
});
And then you can check the shape name in the create event:
// listen to when a new layer is created
map.on('pm:create', function(e) {
console.log(e)
if(e.shape === 'Polygon'){
alert('Original Polygon')
}else if(e.shape === 'PolygonCopy'){
alert('Copy Polygon')
}
});
https://jsfiddle.net/falkedesign/r0sm9auo/

Related

Mapbox GL JS Set Paint property on specific Feature in Layer

I am using Mapbox Studio as basis for mapping and styling and then using HTML for additional map features.
One of the features is to change Icon opacity when hovering or mouse enter. I've checked other examples and all other refer to feature when you create it directly in HTML. I managed to change opacity but only for whole layer.
Can I use somehow e.features[0] command line to apply changes only to one feature rather than to whole layer?
I used this code which changer opacity for whole Layer 'Icon' (Layer contains 5 icons with text):
// Change the cursor to a default and change opacity when the it enters a feature in the 'Icons' layer.
map.on('mouseenter', 'Icons', function() {
map.getCanvas().style.cursor = 'default';
var feature = e.features[0];
map.setPaintProperty('Icons', 'icon-opacity', 0.5);
});
// Change it back to a pointer and reset opacity when it leaves.
map.on('mouseleave', 'Icons', function() {
map.getCanvas().style.cursor = '',
map.setPaintProperty('Icons', 'icon-opacity', 1);
});
Thank you!!!
There are a few ways which you could achieve this. One approach is to add each feature as separate layer, so that when you want to change the opacity of an icon added in a layer 'specific-icon-layer', you can pass 'specific-icon-layer' to the Map#on method. This is likely the most straightforward option if you have a relatively minimal number of markers.
Another approach is to add unique IDs to each icon feature, so that you can use a filter expression in conjunction with Map#setPaintProperty and Map#queryRenderedFeatures (or Map#querySourceFeatures). For example, suppose you add an 'id' property to each GeoJSON feature representing an icon in the source for the 'Icons' layer. Then, you could set up an event listener similar to this example, retrieve the 'id' of the returned feature, and use the 'id' (suppose here it is 'example-id') to update the paint property for the 'Icons' layer:
map.setPaintProperty(
'Icons',
'icon-opacity',
['match', ['get', 'id'], 'example-id', 0.5 , 1]
);
Here, we use match and get expressions to say "if the 'id' of a feature is 'example-id', paint its icon with opacity 0.5, otherwise use opacity 1."
Check the example at https://docs.mapbox.com/mapbox-gl-js/example/hover-styles/
This approach makes use of setFeatureState and feature-state expressions
The problem with using map.setPaintProperty(layer, property, filter, matchValue, styleValue, fallbackStyleValue) every time is that it restyles every feature on the layer instead of only the feature being interacted with. This can cause poor performance when the layer has a high number of features.

Disabling edit of Mapbox GL Draw polygons

When creating a polygon using Mapbox GL Draw I do not want the polygon to then be editable, clickable etc. After ending the draw event, I'd like to to just appear as finished like below when it's deselected.
Maybe this is embedded in the simple_select config option?
The Mapbox Draw plugin provides an interface for writing and hooking in custom modes, where a mode is defined as way to group sets of user interactions into one behavior. Mapbox Draw Static Mode is a custom mode that displays data stored in Draw but does not let users interact with it, which sounds like what you are looking for.
modes.simple_select.onDrag function disable the onDrag event.
I give you a small example to can contextualize it.
Inside your #map-init method you should have:
const modes = MapboxDraw.modes;
let draw = new MapboxDraw({
// your mapBoxDraw options
});
map.on("draw.create", updateArea);
modes.draw_polygon.clickAnywhere = function (state, e) {
//your polygon click restrictions
}
modes.simple_select.onDrag = function (state, e) {
//when polygon is deselected onDrag will be false and user not be able to drag it
};
function updateArea(e) {
// your stuff when you're drawing
}

No setBounds function for Leaflet imageOverlay

I'm reading an imageOverlay URL from an ArcGIS webserver that uses the leaflet getBound() coordinates as part of the URL (we have large maps that are filtered for the current window 'extent'). Apologies for not including the actual path (I'm working with sensitive client data). Eg:
http://myarcgiswebserver.com/MapServer/export/dpi=96&format=png32&bbox=27.119750976562504%2C-31.194007509998823%2C32.39044189453126%2C-29.692824739380754&size=1719%2C434
[bbox] = current imageBounds
When dragging my map the imageOverlay url is updated correctly but my leaflet window is no longer aligned to the imageBound values that were set when first adding the imageOverlay which results in a skewed output (this is my assumption):
The only workaround is to remove the existing imageOverlay and add a new one (which ruins the user experience as the map disappears then reappears each time the window is dragged or zoomed).
Am i approaching this problem incorrectly or would the introduction of a function to update the current imageBounds resolve this? Perhaps not a new function but the expansion of setUrl with additional parameters...?
Many thanks for any feedback...
As #ghybs pointed out, your use case might be better served by using the WMS
interface of your ArcGIS server.
Anyway, you say
The only workaround is to remove the existing imageOverlay and add a new one (which ruins the user experience as the map disappears then reappears each time the window is dragged or zoomed).
Well, that glitch is due to you probably doing something like:
Remove old overlay
Add new overlay
Wait until the image is received from the network
Wait one frame so the new overlay is shown
and instead you should be doing something like:
Add new overlay
Wait until the image is received from the network
Remove old overlay
Wait one frame so the new overlay is shown
The problem is just the async wait and the possible race conditions there, but should be easy to hack together, e.g.:
var activeOverlay = null;
var overlayInRequest = null;
map.on('moveend zoomend', {
// If we are already requesting a new overlay, ignore it.
// This might need some additional debouncing logic to prevent
// lots of concurrent requests
if (overlayInRequest) {
overlayInRequest.off('load', showOverlay);
}
overlayInRequest = L.imageOverlay( computeUrl( map.getBounds() ), myOverlayOptions );
overlayInRequest.on('load', showOverlay);
});
function showOverlay(ev) {
activeOverlay.remove();
activeOverlay = overlayInRequest;
activeOverlay.addTo(map);
overlayInRequest = undefined;
}
If you use an ImageOverlay but change its url dynamically, with a new image that reflects a new bounding box, then indeed that is the reason for the behaviour you describe: you display an image that has been generated using a new bbox, but positioned in the initial bbox, since the image overlay remains at the same geographical position on the map.
Instead, it sounds to me that you should use a TileLayer.WMS.
It would automatically manage the bounding box update for you. You may need to find the correct options to fit your service provider required URL syntax, though.
Example: http://playground-leaflet.rhcloud.com/yel/1/edit?html,output

how to get the value of the pie slice in fusion chart

I'm new to FusionCharts.
I have generated a pie chart with slice, my question is:
When I click on the slice in the pie chart, I need the label name of the slice.
How can I write the on click event and get the name of label?
I don't much about charts and such, I'm fine using Word for this. But in 2D Graphics I've learned you can use
...contains(shape)
...intersects(shape)
To see if shapes are inside eachother (to contain) or if they overlap eachother (to intersect).
When adding a mouseListener, you can use
e.getX() and e.getY()
to get the current X and Y position of mouse on screen.
So something like
for (pieChartPieces p: piece)
{
if (pieChartShape.contains(e.getX, e.getY))
{
//* Some kind of notifier for user pops up here.
}
}
Check out this fiddle here
It uses "dataplotClick" event to retrieve the label of the pie slice.
You have to use link attribute, as seen here.
"link": "JavaScript:populate('411');",
See the jsFiddle.
Don't forget to implement the populate() with the whatever you need to do.
Link to a similar question (with answer).

Click-through markers and polylines in Leaflet

In Leaflet, is it possible to define a marker or polyline with {clickable:false}, so that a click is passed through to whatever lies beneath - be it the map or a clickable geometry object?
At the moment I solve this problem by making the marker/polyline clickable and passing the event onwards myself. But this leads to the mouse cursor always showing as the hand symbol. Ideally, the mouse cursor should look like the normal pointer or the hand, depending on whether what is beneath the marker/polyline is clickable.
This may not be the answer you are looking for, but you can use featureGroups to have all of your clickable polylines come to the front so that the actions are surfaced.
var lg_noclick = new L.FeatureGroup().addTo(map);
var lg_click = new L.FeatureGroup().addTo(map);
// Add lines
lg_click.bringToFront();
updated fiddle
Also if you can afford to know your lines before hand, correct ordering of when you add the lines it will work as well.
I know this is not ideal but it suited my situation just fine, so it might be good for you as well.
This hides the icon and brings it back after a second using mouseenter and mouseleave events:
$('.leaflet-marker-icon').mouseenter(function() {
$(this).hide();
});
$('.leaflet-marker-icon').mouseleave(function() {
$(this).delay(1000).show(0);
});