So, I'm trying to map bus routes using leaflet w/geojson for the coordinates. I'm having a difficult time with one aspect where, on a click, the bus line is boldened, and, ideally, the last clicked on feature returns to the default style.
What I have so far
function $onEachFeature(feature, layer) {
layer.on({
click: function(e) {
//calls up the feature clicked on
var $layer = e.target;
var highlightStyle = {
opacity: 1,
weight: 5
};
$layer.bringToFront();
$layer.setStyle(highlightStyle);
}
});
}
//imagine all the leaflet map tile code here
//this is where the features get added in and the $oneachfeature function
var busFeature = L.geoJson(busRoutes, {
style: defaultBusRouteColor,
onEachFeature : $onEachFeature
});
busFeature.addTo(map);
Above, what I have now successfully changes the style of the feature to what's in highlightStyle. However, when another feature is clicked, the style remains. How do I remove the previously clicked on feature's style so that only one feature at a time has the style highlightStyle?
Things I've already tried: using addClass/removeClass to jQuery methods, layer.resetStyle() with leaflet, and a bunch of other things that still didn't work. Note: this would ideally be used in a mobile version, as the desktop version uses a hover function that emphasizes the features, with no problem. this:
function $oneachfeature(feature, layer){
layer.on({
mouseover: function (e){makes feature bold}
});
layer.on({
mouseout: function (e){makes feature normal again}
});
}
Any suggestions?
Store a reference to the highlighted layer so you can later call resetStyle on it:
// Variable to store selected
var selected
// Create new geojson layer
new L.GeoJSON(collection, {
// Set default style
'style': function () {
return {
'color': 'yellow',
}
}
}).on('click', function (e) {
// Check for selected
if (selected) {
// Reset selected to default style
e.target.resetStyle(selected)
}
// Assign new selected
selected = e.layer
// Bring selected to front
selected.bringToFront()
// Style selected
selected.setStyle({
'color': 'red'
})
}).addTo(map)
Example: http://embed.plnkr.co/RnQO1s/preview
Reference: http://leafletjs.com/reference.html#geojson-resetstyle
using resetStyle() would seem to be an easier solution...simply reset the style of the layer before applying the new style to the feature. This requires only a sinlge line of code adding to your original function:
function $onEachFeature(feature, layer) {
layer.on({
click: function(e) {
//calls up the feature clicked on
var $layer = e.target;
var highlightStyle = {
opacity: 1,
weight: 5
};
busFeature.resetStyle();
$layer.bringToFront();
$layer.setStyle(highlightStyle);
}
});
}
Remove previous Highlight before adding the next:
.removeLayer() works to remove the previously set geoJSON selection using .addTo()
theMap = yourMap.Map
geoJson = yourMap.geoJSON();
onclick() {
const highlightedFeature = {
'color': '#12FF38',
'fillColor': '#30D8E0',
'fillOpacity': 0.3
};
this.theMap.removeLayer(this.geoJson);
this.geoJson = yourMap.geoJSON( Feature, {
style: highlightedFeature
});
this.geoJson.addTo(this.theMap);
}
Related
I am new to JS and I'm seeking for help because I am at dead end. I struggle for a couple of days on events in JS / LeafletJS :(
I want to highlight the element on the map and corresponding data listed in div. I don't know how to select both and do an event on mouseover and mouseout to highlight it
I've stopped on highlight object on map but with no success with div :(
function highlightFeature(e) {
var layer = e.target;
layer.setStyle({
color: '#666' ,
fillOpacity: 0.7
})
}
function resetHighlight(e) {
geoJSON.resetStyle(e.target);
};
function onEachFeature(feature, layer) {
layer.on({
mouseover : highlightFeature,
mouseout: resetHighlight
});
};
Here's JSFiddle of what I've done so far.
https://jsfiddle.net/JohnDoeJr/jcxz2ruw/6/
I expect that when you put the mouse over multipolygon it will change appearance (highlight) and in div the corresponding data will also highlight and vice versa.
Just add an id to each layer and the assign same id to the corresponding text
geoJSON.eachLayer(function (layer) {
layer._path.id = layer.feature.properties.Name; // This assigns id to each layer with the layer name
);
function updateList(target){
var displayed = target.getLayers();
var list = document.getElementById('displayed-list');
list.innerHTML = "";
displayed.forEach(function(borders){
var li = document.createElement('li');
li.innerHTML = borders.feature.properties.Name;
li.setAttribute("id", borders.feature.properties.Name); // this assigns id to the text with layer's name
list.appendChild(li);
});
}
And add these style on hover over th geoJSON layer
function highlightFeature(e) {
var layer = e.target;
layer.setStyle({
color: '#666' ,
fillOpacity: 0.7
})
id = e.target.feature.properties.Name
$("li#"+id).css("font-weight","bold")
}
function resetHighlight(e) {
geoJSON.resetStyle(e.target);
id = e.target.feature.properties.Name
$("li#"+id).css("font-weight","")
};
And don't forget to include jQuery.
You can add the same function to text on hover which I have not included.
Is there a way to override the reset zoom button click event in highcharts? When I click the reset zoom button, I want the chart to display the specific interval using setExtremes instead of zooming out the whole chart.
you can capture it in the selection property of your chart object. Just use the setExtremes()function with where you want to zoom, and a timeout of 0ms so that the graph's 'reset graph' function doesn't overwrite it.
chart: {
events: {
selection: function(event) {
if (!event.xAxis) {
setTimeout(function(){chart.xAxis[0].setExtremes(0, 5)}, 0);
}
}
...
}
working example: http://jsfiddle.net/rhavelka/M7cfm/64/
edit: but make sure that you want to zoom in on that specific interval and essentially hiding data from the user (unless you have a scrollbar on the graph like this example)
You can overwrite default Chart.prototype.showResetZoom and provide your own logic in zoomOut function:
(function(H) {
H.Chart.prototype.showResetZoom = function() {
var chart = this,
defaultOptions = H.defaultOptions,
fireEvent = H.fireEvent,
lang = defaultOptions.lang,
btnOptions = chart.options.chart.resetZoomButton,
theme = btnOptions.theme,
states = theme.states,
alignTo = btnOptions.relativeTo === 'chart' ? null : 'plotBox';
function zoomOut() {
//chart.zoomOut(); // default action
chart.xAxis[0].setExtremes(3, 7); // new action
}
fireEvent(this, 'beforeShowResetZoom', null, function() {
chart.resetZoomButton = chart.renderer.button(
lang.resetZoom,
null,
null,
zoomOut,
theme,
states && states.hover
)
.attr({
align: btnOptions.position.align,
title: lang.resetZoomTitle
})
.addClass('highcharts-reset-zoom')
.add()
.align(btnOptions.position, false, alignTo);
});
}
})(Highcharts);
Live demo: http://jsfiddle.net/BlackLabel/ay0t2e9f/
Docs reference: https://www.highcharts.com/docs/extending-highcharts/extending-highcharts
quick (and I believe for some of you an easy) question regarding cursor styling while hovering above geojson layer/s.
So, I have one clip layer that I'm using to create a mask around wms layers, and another one that represents some administrative areas.
As you can see in picture below
What I would like is to change style of cursor when I'm hovering above administrative areas but it seems that I'm missing something.
I'm trying to isolate to layer only administrative borders layer using this block of code:
map.on('pointermove', function(e) {
if (e.dragging) return;
var pixel = e.map.getEventPixel(e.originalEvent);
var hit = e.map.forEachFeatureAtPixel(pixel, function(feature, layer) {
return vectorJLS.get('layer_name') === 'jls';
});
e.map.getTargetElement().style.cursor = hit ? 'pointer' : '';
});
UPDATE
While JGH tweak code a bit it still doesn't work. I've detected that problem lies in layer that I'm using for mask clipping, when removed, code that JGH provided, works.
Here is code that I'm using for mask clipping
var clipLayer = new ol.layer.Image({
source: new ol.source.ImageVector({
source: new ol.source.Vector({
url: 'geojson/clip_wgs.geojson',
format: new ol.format.GeoJSON()
}),
style: new ol.style.Style({
fill: new ol.style.Fill({
color: 'black'
})
})
})
});
clipLayer.on('postcompose', function(e) {
e.context.globalCompositeOperation = 'source-over';
});
clipLayer.on('precompose', function(e) {
e.context.globalCompositeOperation = 'destination-in';
});
clipLayer.setMap(map);
Is it possible to somehow ignore clip layer when changing cursor style or should I take another approach?
UPDATE - 2
I've tweaked code a bit, but still without any success while clipedLayer is on.
map.on('pointermove', function(e) {
if (e.dragging) return;
var pixel = e.map.getEventPixel(e.originalEvent);
// initialize the hit variable to false (not found)
var hit = map.hasFeatureAtPixel(e.pixel, {
layerFilter: function(layer) {
return vectorJLS.get('layer_name') === 'jls';
}
});
console.log(hit)
});
Interesting problem if I might add
Finally, with help from fellow JGH I've found appropriate solution for my problem.
Searching release pages and google machine I've stumbled upon some interesting information regarding layer filters and its usage in method hasFeatureAtPixel. This block of code is valid for versions below 3.20.1 but more about that on OpenLayers Git
map.on('pointermove', function(e) {
if (e.dragging) return;
var pixel = e.map.getEventPixel(e.originalEvent);
var hit = map.hasFeatureAtPixel(e.pixel, {
layerFilter: function(layer) {
return layer.get('layer_name') === 'jls';
}
});
e.map.getTargetElement().style.cursor = hit ? 'pointer' : '';
});
For newer versions you should use layer filter like this (I'm using 4.6.5)
map.hasFeatureAtPixel(pixel, {
layerFilter: layerFilterFn.bind(layerFilterThis)
});
Or for my particular problem like this
map.on('pointermove', function(e) {
if (e.dragging) return;
var pixel = e.map.getEventPixel(e.originalEvent);
var hit = map.hasFeatureAtPixel(e.pixel, {
layerFilter: function(layer) {
return layer.get('layer_name') === 'jls';
}
});
e.map.getTargetElement().style.cursor = hit ? 'pointer' : '';
});
Hope it helps :)
In your function, you are basically looping through all the layers at the mouse location. In that loop, if the layer has the proper name you set the pointer, else if it has a different name, you remove the pointer (or set it to something else).
As it is, it is dependent on the layer order:
ex: layer 1 = target -> set custom pointer. Layer 2 = other layer -> remove pointer. ==> final pointer: removed
ex: Layer 1 = other layer -> remove pointer. Layer 2 = target -> set custom pointer. ==> final pointer: custom pointer
The looping occurs when you set the hit variable, i.e. it corresponds to the last layer only as you are overriding the value for each layer.
map.on('pointermove', function(e) {
if (e.dragging) return;
var pixel = e.map.getEventPixel(e.originalEvent);
// initialize the hit variable to false (not found)
var hit = false;
e.map.forEachFeatureAtPixel(pixel, function(feature, layer) {
if ( vectorJLS.get('layer_name') === 'jls') {
//IF we have found the layer, flag it (but don't return anything!)
hit = true;
}
});
e.map.getTargetElement().style.cursor = hit ? 'pointer' : '';
});
I created a function to mouse over a colorless layer and everything. So far so good, and also created a function to go back as it was before the mouse. But when I use the opacity, the reset function does not return to normal and leaves the opacity in the default state (0.7) and not in the state it is in at the moment.
Function to mouseouver:
function highlightFeature_stComerciais(e) {
layerStComerciais = e.target;
layerStComerciais.setStyle({
weight: 5,
color: '#666',
dashArray: ''
});
info.update(layerStComerciais.feature.properties);
}
function to mouseout
function resetHighlight_stComerciais(e) {
setoresComerciaisOverlay.resetStyle(e.target);
info.update();
}
opacity:
$('#sldOpacity').on('change', function(){
$('#image-opacity').html(this.value);
setoresComerciaisOverlay.setStyle({ fillOpacity: this.value })
});
The default opacity is 0.7, assuming I put 0 of opacity, when I do mouse over the Layer all right, but when I mouse out it returns to 0.7 and I do not want this. Why my reset doesnt work how I want? Thanks!
I guess you build your vector layers using GeoJSON data and L.geoJSON factory, that returns a Leaflet GeoJSON Layer Group.
The resetStyle method on that group re-applies the style that was applied at the time of creation of that group, or the default one if you did not provide style:
Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
If you later change the style of one or all of the vector layers in that group, it will be therefore overridden when you use resetStyle, and the initial style will be applied.
An easy workaround is simply to modify the GeoJSON Layer Group's style option as well. However, it will affect all its child layers.
group.options.style = newStyle;
Another solution would be to record the new style of each vector layer, and use that recorded value when you want to restore the previous state, instead of using the group's resetStyle method.
var map = L.map("map").setView([48.85, 2.35], 12);
var geojson = {
type: "Feature",
geometry: {
type: "Point",
coordinates: [2.35, 48.85]
}
};
var point;
var startStyle = {
color: "red"
};
var newStyle = {
color: 'green'
};
var group = L.geoJSON(geojson, {
style: startStyle,
pointToLayer: function(feature, latlng) {
point = L.circleMarker(latlng);
assignStyle(point, startStyle);
return point;
}
}).addTo(map);
// Record the style to the individual vector layer if necessary.
function assignStyle(leafletLayer, newStyle) {
leafletLayer.setStyle(newStyle);
leafletLayer._recordedStyle = newStyle;
}
// When desired, apply the style that has been previously recorded.
function reassignStyle(leafletLayer) {
leafletLayer.setStyle(leafletLayer._recordedStyle);
}
document.getElementById('button').addEventListener('click', function(event) {
event.preventDefault();
// Either use the wrapper function that records the new style…
//assignStyle(point, newStyle);
// Or simply modify the group's style option, if it is acceptable affecting all child layers.
point.setStyle(newStyle);
group.options.style = newStyle;
});
group.on({
mouseover: function(e) {
e.target.setStyle({
color: 'blue'
});
},
mouseout: function(e) {
//reassignStyle(e.layer);
group.resetStyle(e.layer);
}
});
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.0.3/dist/leaflet.css">
<script src="https://unpkg.com/leaflet#1.0.3/dist/leaflet-src.js"></script>
<div id="map" style="height: 100px"></div>
<button id="button">Change color to Green…</button>
<p>…then mouseover and mouseout</p>
So, I'm building a map of water taking permits in Toronto. Work in progress available here: http://pennybeames.net/maps/PermitsTO.html
I'm using L.mapbox.featureLayer to add my markers:
var permitsLayer = L.mapbox.featureLayer(permits, {
pointToLayer: function (feature, latlng) {
return L.circleMarker(latlng, permitsStyle(feature));}
}).addTo(map);
I've got the custom info control from the Leaflet choropleth example working (http://leafletjs.com/examples/choropleth.html).
I change the style and update the info in the custom control as follows:
featureLayer.eachLayer(function (layer) {
layer.on('mousemove', function (e) {
// highlight feature
layer.setStyle({
weight: 2,
opacity: 1,
fillOpacity: 0.9
});
//update properties in DomUtil
info.update(layer.feature.properties);
});
});
All is working smoothly until we get to mouseout. The info control updates fine (either switching to the properties of the new target or reverting to its empty state), but the style won't change. The highlight stays on.
featureLayer.eachLayer(function (layer) {
layer.on('mouseout', function (e) {
//reset style
featureLayer.setStyle(permitsStyle);
//empty properties in DomUtil
info.update();
});
});
I don't get any errors, but I also don't see any change. I've tried
featureLayer.resetStyle(e.target)
but then all I get is Uncaught TypeError: featureLayer.resetStyle is not a function.
There is probably a really simple question, but it's eluding me. Insight, hivemind?
Don't bind to your events inside the eachLayer loop, just do featureLayer.on('myevent',function(e) {e.layer.setStyle(MY_NEW_STYLE)})