How to dynamically set shadow in Leaflet.js - javascript

I would like to dynamically set the shadow of a custom marker icon at runtime, because I want to emphasize the currently clicked icon. I'm using Leaflet 1.0. Things I've tried:
Adding a shadow at runtime with the createShadow function
Adding a blank png shadow image to the custom icon and changing it at runtime by either the createShadow function or directly modifying the shadow's image source
Here's some psuedocode:
function addMyShadow(marker) {
/* try Leaflet's `createShadow` function */
// directly modify marker: Uncaught TypeError: marker.createShadow is not a function
marker.createShadow('lib/images/shadow.png');
// the createShadow is available under options.icon,
// but this doesn't seem to change anything
marker.options.icon.createShadow('lib/images/shadow.png');
/* try directly modifying the shadow's image source */
// Successfully changes the `currectSrc` property,
// but also doesn't do anything to the icon on the map
marker._shadow.currentSrc = 'lib/images/shadow.png';
}
I also tried appending a custom class with -webkit-filter: drop-shadow, but I'm already using -webkit-filter to color a set of icons. I only want to change the selected icon, not the entire class. Apparently nesting -webkit-filter overwrites the outside -webkit-filter instead of stacking filters. So this isn't a viable option for me.
Currently I'm experimenting with adding a custom class that uses css drop-shadow; it seems like I can manually change the class at runtime, but I'm hoping there's some better built-in way to add a shadow. It's also not very pretty, because it makes the shadow square even if the image has transparency.
EDIT: I want the marker to be draggable (and have the shadow follow as the marker is dragged). So any solution with two icons would need to bind both icons together somehow.

I think you should have 2 L.icon objects: one without shadow and one with a shadow.
To show the shadow, you just switch L.Icon
marker.on('click', function(e) {
if(selectedMarker) {
if(selectedMarker !== e.target) { // selected marker is NOT this one
selectedMarker.setIcon(greenIcon);
e.target.setIcon(greenIconWithShadow);
selectedMarker = e.target;
}
else { // selected marker is this one
selectedMarker.setIcon(greenIcon);
selectedMarker = false;
}
}
else { // no marker selected
selectedMarker = e.target;
e.target.setIcon(greenIconWithShadow);
}
});
The shadow is part of the L.Icon and should follow when dragged
See example http://plnkr.co/edit/PNxzJqMbcRTuo0jWPGyj?p=preview

Related

In Leaflet, how to load tiles only when clicking on each?

I have a leaflet map consisting of a satellite image with lat-long metadata. My objective is to display a rectangle highlighting the selected tile on mouseclick and fetch the corresponding tile. The L.rectangle method needs bounds to display the rectangle. In order to calculate the SE and NW corners, I use the following function that I found at https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Tile_numbers_to_lon..2Flat.
This is for SE and the same function is changed for getting NW as seen in the above link. These are used as bounds for the rectangle.
//I have a fixed zoom level for my problem :4
function(xtile,ytile,zoom) //here i am giving the tile location coordinates as the input
{
n=2.0**zoom;
lon= (xtile+1)/n*360-180;
lat_rad=Math.atan(Math.Sinh(Math.PI*(1-2*(ytile+1)/n)));
lat=lat_rad(180/Math.PI);
return[-lat,lon]
}
The output comes out to be as follows:
NW -782.67,-112.4
SE -79.163,-89.9
with(suppose) xtile=5 and ytile=14
The actual output however should have been:
NW -66.51,-45
SE -74.01,-22.5
and the corresponding(again,suppose) xtile=5 and ytile=1
So basically, for some tiles , the rectangle shows just fine. But for others, it jumps to some other tile. I have a display window for the selected tileand when the rectangle jumps, neither the selected tile nor the highlighted tile is fetched. I will be really thankful is someone can help me with this.
My objective is to display a rectangle highlighting the selected tile on mouseclick and fetch the corresponding tile.
Then a good approach to the root problem is to use a custom L.GridLayer, since it provides functionality to create empty tiles, attach events to them and handle tile coordinates without the need to handle bounding boxes explicitly.
It would go like this:
L.GridLayer.ClickyLoad = L.GridLayer.extend({
// By default, the container for a whole zoom level worth of visible tiles
// has a "pointer-events: none" CSS property. Override this whenever a new
// level container is created. This is needed for pointer (mouse) interaction.
_onCreateLevel: function(level) {
level.el.style.pointerEvents = 'inherit';
},
// The tiles shall be empty <div>s with some DOM events attached.
createTile: function(coords){
var tile = L.DomUtil.create('div');
tile.style.border = '1px solid black';
// Highlighting the tile on mouse hover is just swag.
L.DomEvent.on(tile, 'mouseover', function(){
tile.style.border = '2px solid red';
});
L.DomEvent.on(tile, 'mouseout', function(){
tile.style.border = '1px solid black';
});
// When a tile is clicked, calculate the URL of the tile (using the same
// logic as L.TileLayer.getTileUrl() ), create a <img> element, and put
// the newly created image inside the empty <div> tile.
L.DomEvent.on(tile, 'click', function(){
var img = L.DomUtil.create('img');
img.src = L.Util.template('https://tile.openstreetmap.org/{z}/{x}/{y}.png', coords);
tile.appendChild(img);
});
return tile;
}
});
// Create instance and add to map
(new L.GridLayer.ClickyLoad()).addTo(map);
See a fully working example here.
As usual with this kind of non-trivial-looking examples, reading through the Leaflet documentation, the tutorial about extending classes, and the Leaflet source code itself is recommended.

Overwrite style properties on select in OpenLayers

I want to reuse my basic style function to create an select style by overwriting some of the properties.
This approach is working for fill/stroke colors of linestrings, polygons and texts, but not for images (a circle in my case).
I don't want to create a new image since other properties should be retained.
var selectStyleFunction = function(feature, resolution) {
var style = styleFunction(feature, resolution)[0];
style.getImage().getFill().setColor("magenta");
console.log(style.getImage().getFill().getColor());
return [style];
};
In this fiddle you can see, that the above code doesn't change the feature style on selection although the log output is correct.
Is there any way to overwrite this property?
I found an answer to my question in the api dokumentation of openlayers. It was too obvious to find it in the first place.
https://openlayers.org/en/latest/apidoc/ol.style.Style.html
ol.style.Style
Container for vector feature rendering styles. Any changes made to the style or its children through set*() methods will not take effect until the feature or layer that uses the style is re-rendered.
So rendering is not triggered by the setters.

DrawImage doesn't show CSS styles

I'm trying to draw an img to a canvas. However, styles aren't carrying over, whether I set the style attribute or do it in the CSS.
Here's a JSFiddle showing what I'm trying to do.
I've also tried applying the styles to the canvas itself. This displays correctly, but when I perform canvas.toDataURL(...) it doesn't get the styles.
Is there some other way to apply styles to the generated data?
To apply filters to your canvas content, you can use the cutting-edge filter property, which uses the same syntax the CSS filter property. Before you draw your image, do
context.filter = "grayscale(1)";
And you can reset it for anything else you do in the future by
context.filter = "none";
Your filtered image will remain correctly filtered -- the filter property basically tells the canvas context, "Whatever I draw from now on should have this filter applied," so changes only affect future drawing operations.

Creating a custom control.layers class in leaflet

I have a page that displays maps using leaflet.js with many different base layers and overlays. I'd like to have separate layer controls for selecting the base maps and the overlays, and for the controls to have different icons.
To accomplish that I'm trying to on add a setButtonClass method to leaflet's L.control.layers that I can call after placing my 2nd button on the map, which would change its class and allow me to give it different styling.
So I'd initialize the controls like this:
var baseLayersControl = L.control.layers(baseMaps, null, {position: 'bottomright'});
var overlayControl = L.control.layers(null, overlayMaps, {position: 'bottomright'});
baseLayersControl.addTo(map);
overlayControl.addTo(map);
overlayControl.setButtonClass();
However, I can't get setButtonClass to change the class the way I want. I added this to leaflet-src.js:
setButtonClass: function () {
this.className = 'leaflet-control-layers2';
},
And added styling to leaflet.css for the leaflet-control-layers2 class and related classes with my new icon, but right now I'm just getting two standard layer controls on the map.
Have you checked out Leaflet.groupedlayercontrol? It groups and labels your layer control by base maps and overlays. I'd check there first and then see how you might be able to create two separate icons for base layers and overlays.

Is it possible to place size and number limitations in Leaflet.Draw?

This question is directed to Leaflet users (and those who use the Leaflet.draw plugin)...
I'm using Leaflet and would like to allow my user to draw 1--and only 1--single polygon over any area of the map. I would also like to limit the size of that polygon in some way (such as limiting the length of the side for a square or the area covered it covers--preferably specified in degrees so that the set size limits would translate regardless of the zoom level).
My end goal is simply to extract the coordinates of the 4 square vertices or the coordinates covered by the polygon area.
That said, I found the Leaflet.Draw plugin. It is fantastic, however, I need to limit its functionality to my requirements (only 1 polygon drawn at a time and, in particular, the size cannot be drawn too large). Is this possible to do? If so, how?
Regardless of if it is or is not possible, is there a better way to go about doing this?
Can I propose another solution to this issue?
I would limit the number of polygons to one by doing the following:
map.on('draw:created', function (e) {
var layer = e.layer;
if(drawnItems && drawnItems.getLayers().length!==0){
drawnItems.clearLayers();
}
drawnItems.addLayer(layer);
});
I am listening to the draw:created event and determine if there is already a marker. If there is, I remove that marker and place my new one in the desired location. Therefore, one less click for user as they no longer need to delete the previous and one marker rule is always enforced.
If you wanted to allow more than one marker you could do a FIFO delete of the oldest layer.
If you do not want to automatically delete a layer, you could either prompt the user or ignore the request.
That said, I found the Leaflet.Draw plugin. It is fantastic, however, I need to limit its functionality to my requirements (only 1 polygon drawn at a time and, in particular, the size cannot be drawn too large). Is this possible to do? If so, how?
I think you'll need to code it yourself.
I see two possibities:
hacking the draw plugin (writing your own code inside the plugin)
extending the L.Draw.Polygon class from the draw plugin (see the docs about OOP in Leaflet) to create a costum one
1 is faster, 2 is cleaner. You'll have to choose depending on the size of your project.
I did it without hacking the Leaflet Draw source.
After the controls are added to the map, I place a hidden div inside the controls. Then when a polygon is created I display that div. I used CSS to absolute position it over the controls so the buttons are then "disabled" and CSS to make the buttons look faded. If the polygon is deleted then I hide that div.
Not the best solution, but I works without having to edit the source.
After drawControl is added, I add the hidden div:
$('.leaflet-draw-section:first').append('<div class="leaflet-draw-inner-toolbar" title="Polygon already added"></div>');
Here's the JS to toggle them:
map.on('draw:created', function (e) {
var type = e.layerType,
layer = e.layer;
// keep the polygon on the map
drawnItems.addLayer(layer);
// disable the create polygon tools
$('.leaflet-draw-inner-toolbar').show();
});
map.on('draw:deleted', function(e) {
// enable the create polygon tools
$('.leaflet-draw-inner-toolbar').hide();
});
Here's the CSS:
.leaflet-draw-inner-toolbar {
background: none repeat scroll 0 0 rgba(255, 255, 255, 0.6);
bottom: 0;
display: none;
left: 0;
position: absolute;
right: 0;
top: 0;
}

Categories