In my app I had to develop a system that allows the user, with a double click, to zoom in to a cluster.
Everything work, but the problem is that the double click add a feature on the map (a point) and it is persistent (it also replace the feature I selected in case I select not the cluster but the single element, even if I have specified that features.length>=2, so the double click should work only if the user doubles click on the cluster, but it is not like that ).
I would like the select interaction perform only the zoom into the cluster and not that the event adds also a feature on the map. How could I solve this?
var selectDoubleClick = new ol.interaction.Select({
multi: true,
condition: ol.events.condition.doubleClick,
layer: this.clusterLayer,
style: this.selectedClusterStyle
});
selectDoubleClick.on('select',function(event) {
var eventFeature = event.selected[0];
var features = eventFeature.get('features');
if (features.length>=2){
var extent = ol.extent.createEmpty();
features.forEach(function(feature) {
ol.extent.extend(extent, feature.getGeometry().getExtent());
});
this.getMap().getView().fit(extent, {padding: [150, 150, 150, 150]})
}
});
this.map.addInteraction(selectDoubleClick);
I also tried to set selectDoubleClick style:null but it doesn't solve my problem
Follow this discussion, I finally found a solution:
Openlayers 4 - Make layer invisible on feature click
In particular:
The ol.interaction.Select selected features are added to an internal
unmanaged layer.
This is why the selected feature is visible even if the underlying
layer is not.
You could unselect the selected features while you zoom on it using
select_interaction.getFeatures().clear() (just like clicking away).
So I edited my code like that:
selectDoubleClick.on('select',function(event) {
var eventFeature = event.selected[0];
var features = eventFeature.get('features');
if (features.length>1){
var extent = ol.extent.createEmpty();
features.forEach(function(feature) {
ol.extent.extend(extent, feature.getGeometry().getExtent());
});
this.getMap().getView().fit(extent, {padding: [150, 150, 150, 150]})
}else{
selectDoubleClick.getFeatures().clear()
}
});
this.map.addInteraction(selectDoubleClick);
Related
I have a layer with two polylines and polylineDecorators. I would like to highlight both polylines and polylineDecorators when I hover on any of these. Right now I'm able to highlight only one at the time when hovering on it.
Here is my code:
var layer_migration = L.layerGroup({
layerName: 'layer_migration',
pane: 'linesPane',
});
function onEachFeature_migration (feature, layer) {
var polyline = L.polyline(layer.getLatLngs(),{
color: "#8B0000",weight: 5,opacity: 0.4,dashArray: '8,8',
dashOffset: 0
}).addTo(layer_migration);
var PLdecorator1 = L.polylineDecorator(polyline, {
patterns: [{
offset: '104%',
repeat: 100,
symbol: L.Symbol.arrowHead({pixelSize: 16,
pathOptions: {color: "#8B0000",fillOpacity: 0.6,weight: 0
}
})
}]
}).addTo(layer_migration)
var myfeatures = L.featureGroup([polyline,PLdecorator1]).addTo(layer_migration);
myfeatures.on('mouseover', function(e) {
var layer = e.target;
layer.setStyle({color: '#8B0000',opacity: 1,fillOpacity:1
});
});
}
Any help super appreciated.
Thanks,
P
In your mouseover callback, I think that e.target will just refer to the individual layer (polyline or decorator) that triggered the event, not the collection of layers that make up the feature group. I've not tested it, but according to the docs, you ought to be able to get the effect you want by calling .setLayer() on the feature group itself:
myfeatures.on('mouseover', function(e) {
myfeatures.setStyle({color: '#8B0000',opacity: 1,fillOpacity:1});
});
Also, if the two polylines are created by two separate calls to onEachFeature_migration(), then they will end up as two separate feature groups. To get around this, you might need to assign an empty featureGroup to myfeatures outside the function, then add the new polylines to it inside the function using myfeatures.addLayer().
Add layers in MapSettingsControl dynamically so that I can toggle there visibility.
When I tried to add layer in mapSettingControl It was disabled.
Background:
I have a web app developed using leaflet. I have 5 layers as shown in figure-1. Its working fine in leaflet. Now I am using HERE Maps javascript API for developing same web app.
How I did in Leaflet:
I add them as map overlays when adding layer control i.e.
let layerControl = L.control.layers(baseMaps, overlayMaps).addTo(map);
while when I need to add overlay in layer control dynamically I use following:
layerControl.addOverlay(somelayer, "layer name");
This work fine.
How I am doing by using Here Maps js api:
As I have five layers, two are cluster layers while two are marker layer and one geojson.
I have tried to apply same approach by creating GROUP for five layers and adding them into the layers of map setting controls.
public clusterGroup1 = new H.map.Group();
public clusterGroup2 = new H.map.Group();
public markerGroup1 = new H.map.Group();
public markerGroup2 = new H.map.Group();
public boundary = new H.map.Group();
created customized map setting ui :
this.customizedMapSetting = new H.ui.MapSettingsControl({
baseLayers: [{
label: "Normal", layer: defaultLayers.vector.normal.map
}],
layers: [{
label: "Cluster-1",
layer: this.clusterGroup1
},
{
label: "Cluster-2",
layer: this.clusterGroup2
},
{
label: "Marker-1",
layer: this.markerGroup1
},
{
label: "Marker-2",
layer: this.markerGroup2
},
{
label: "Geojson",
layer: this.boundary
}]
});
this.customizedMapSetting.setAlignment('top-right');
ui.addControl("customized", this.customizedMapSetting);
I am adding markers to a group like this:
dataArray.forEach(data=> {
let lat = data.latlng[0];
let lng = data.latlng[1];
var marker = new H.map.Marker({ lat: lat, lng: lng }, { icon: icon});
this.markerGroup1.addObject(marker);
});
Problem Statement
I am unable to get this mapSetting ui working for my layers.
How can I add clusterlayers in MapSettingControl so that I can toggle (show/hide) them? (I think I am not using right approach of group) When I add them as a map.addLayer(clusterLayer) it work fine.
How Should I add layer dynamically in MapSettingControl? Possible alternative of leaflet method :
layerControl.addOverlay(somelayer, "layer name");
Looking at the API, it seems MapSettingsControl.Options has
layers: {
label: 'test',
layer: instance of H.map.layer.Layer
}
The error we get when toggling is InvalidArgumentException from DataModel.add regarding the first argument. I believe this means that on toggle, DataModel.add is called and passed the H.map.Group that is being set in the MapSettingsControl, but the DataModel.add expects a H.map.layer.Layer but receives the H.map.Group.
I'm not sure if it's possible to simply add a H.map.Group in MapSettingsControl. I think we have to somehow add the H.map.Group as a provider for a H.map.layer.Layer and add the Layer object to MapSettingsControl.
EDIT
As for adding cluster. When I try to add a new layer for the cluster, it is grayed out. The cluster provider is created and the ObjectLayer is created as well but I think after you have created the MapSettingsControl and defined the variable that will act as the H.map.layer.Layer, updating the variable will have no effect on the toggle behaviour.
I would like to have certain layers to be always on top of others, no matter in which order they are added to the map.
I am aware of bringToFront(), but it does not meet my requirements. I would like to set the zIndex dynamically based on properties.
Leaflet has the method setZIndex(), but this apparently does not work for geoJson layers:
https://jsfiddle.net/jw2srhwn/
Any ideas?
Cannot be done for vector geometries.
zIndex is a property of HTMLElements, and vector geometries (lines and polygons) are rendered as SVG elements, or programatically as <canvas> draw calls. Those two methods have no concept of zIndex, so the only thing that works is pushing elements to the top (or bottom) of the SVG group or <canvas> draw sequence.
Also, remind that L.GeoJSON is just a specific type of L.LayerGroup, in your case containing instances of L.Polygon. Furthermore, if you read Leaflet's documentation about the setZIndex() method on L.LayerGroup:
Calls setZIndex on every layer contained in this group, passing the z-index.
So, do L.Polygons have a setZIndex() method? No. So calling that in their containing group does nothing. It will have an effect on any L.GridLayers contained in that group, though.
Coming back to your problem:
I would like to have certain layers to be always on top of others, no matter in which order they are added to the map.
Looks like the thing you're looking for is map panes. Do read the map panes tutorial.
This is one of the reason for the implementation of user defined "panes" in Leaflet 1.0 (compared to versions 0.x).
Create panes: var myPane = map.createPane("myPaneName")
If necessary, set the class / z-index of the pane element: myPane.style.zIndex = 450 (refer to z-index values of built-in panes)
When creating your layers, specify their target pane option: L.rectangle(corners, { pane: "myPaneName" })
When building through the L.geoJSON factory, you can loop through your features with the onEachFeature option to clone your layers with specified target pane.
Demo: https://jsfiddle.net/3v7hd2vx/90/
For peoples who are searching about Z-Index
All path layers (so all except for markers) have no z-index because svg layers have a fix order. The first element is painted first. So the last element is painted on top.
#IvanSanchez described good why zIndex not working.
You can control the order with layer.bringToBack() or layer.bringToFront().
With that code you have more options to control the order of the layers.
L.Path.include({
getZIndex: function() {
var node = this._path;
var index = 0;
while ( (node = node.previousElementSibling) ) {
index++;
}
return index;
},
setZIndex: function(idx) {
var obj1 = this._path;
var parent = obj1.parentNode;
if(parent.childNodes.length < idx){
idx = parent.childNodes.length-1;
}
var obj2 = parent.childNodes[idx];
if(obj2 === undefined || obj2 === null){
return;
}
var next2 = obj2.nextSibling;
if (next2 === obj1) {
parent.insertBefore(obj1, obj2);
} else {
parent.insertBefore(obj2, obj1);
if (next2) {
parent.insertBefore(obj1, next2);
} else {
parent.appendChild(obj1);
}
}
},
oneUp: function(){
this.setZIndex(this.getZIndex()+1)
},
oneDown: function(){
this.setZIndex(this.getZIndex()-1)
}
});
Then you can call
polygon.oneUp()
polygon.oneDown()
polygon.setZIndex(2)
polygon.getZIndex()
And now layergroup.setZIndex(2) are working
By default, as I know, jqTree doesn't support interaction between trees. How can I add drag-and-droppin' ability with a small effort?
I did it using the double click event
var Tree1 = $('#treeId').tree({
data: someData,
dragAndDrop: true,
}).bind(
'tree.dblclick',
function(event) {
// event.node is the clicked node
console.log(event.node);
var selectedNode = Tree2.tree('getSelectedNode');
console.log(selectedNode);
Tree2.tree('addNodeAfter',event.node,selectedNode);
}
);
node will be sent to the other tree on the double click event
I have two geoJson layers being loaded - both layers are the same data for testing purposes, but being drawn from two different json files. When I turn the layers on and off in the layer controller, the draw order of the layers change.
Any ideas why this is happening?
I have put my code into a JSFiddle: http://jsfiddle.net/lprashad/ph5y9/10/ and the JS is below:
//styling for watersheds_copy
var Orange = {
"color": "#ff7800",
"weight": 5,
"opacity": 0.65
};
var Water_Orange = L.geoJson(watersheds_copy, {
style: Orange
});
Water_Orange.addData(watersheds_copy);
//these are blue
var Water_blue = L.geoJson(watersheds, {});
Water_blue.addData(watersheds);
//This sets the inital order - last in layer list being on top. Except minimal - tile layer is always on bottom
var map = L.map('map', {
center: [41.609, -74.028],
zoom: 8,
layers: [minimal, Water_Orange, Water_blue]
});
var baseLayers = {
"Minimal": minimal,
"Night View": midnight
};
//This controls the order in the layer switcher. This does not change draw order
var overlays = {
"Water_Orange": Water_Orange,
"Water_blue": Water_blue
};
L.control.layers(baseLayers, overlays).addTo(map);
LP
While searching I happened upon this site that shows some of the Leaflet code:
http://ruby-doc.org/gems/docs/l/leaflet-js-0.7.0.3/lib/leaflet/src/control/Control_Layers_js.html
In it I found this condition for the application of autoZIndex:
if (this.options.autoZIndex && layer.setZIndex) {
this._lastZIndex++;
layer.setZIndex(this._lastZIndex);
}
TileLayer is the only layer type that has a setZIndex function, so apparently autoZIndex only works there.
I'm not sure which annoys me more. This incredible limitation or the fact that Leafet documentation doesn't point it out.
At least on 0.7.2, I had to use bringToFront in the callback of map.on('overlayadd'). autoZIndex: false did not work in my case neither. A comment on this issue may explain the reason.
It's not specific to L.GeoJson layers. As far as I can tell, it's true of all Leaflet layers with layer control. The last layer turned on is simply on top. I don't think this is a bug either. It's predictable behavior which I use and depend on when I'm designing maps with layer control...