Selecting feature clusters in OpenLayers 3 - javascript

At the moment I have my map set up to cluster icons upon zooming out. I want to disable the ability to select clusters but allow the user to select individual markers if they zoom in. Preferably, clicking on a cluster should select each individual feature in the cluster (in their correct locations). My select code is like so:
var selectClick = new ol.interaction.Select({
condition: ol.events.condition.click,
style: selectedMarkerStyle,
addCondition: function(mapBrowserEvent){
var features = null;
map.forEachFeatureAtPixel(mapBrowserEvent.pixel, function (feature_, layer) {
features = feature_.values_.features;
});
if(features !== null && features.length > 1){
return false;
}
return true;
}
});
map.addInteraction(selectClick);
I tried to make the addCondition check if multiple features are located at the point and then stop the event however it doesn't seem to be working. At the moment clicking on a cluster selects one of the features in the cluster, in the location of the cluster, rather than the features correct location. Is there anyway to fix this?
Thanks

Related

Leaflet js 'follow' a marker as it receives live updates from GPS devices and moves around the map. Also be able to stop tracking the marker

I have a leaflet map that is populated with markers when GPS devices POST their lat and long on random intervals.
The issue is I have to manually drag the map to follow the marker as it receives updates and moves around the map.
Hours later and much research effort:
I implemented a half solution using a button which snaps to the markers current position, but again I have to click the button every time it moves to keep the marker in sight.
onclick="mymap.fitBounds(gotomarker1.getBounds());"
Is there a way I could click the marker itself which will then keep the marker in the center of the map as it receives lat and log updates and moves around? *ideally not a button as there could be many markers and I wouldn't want a page full of buttons.
I have tried applying an on.click function that loops the gotomarker1 code above but I cant stop it once its started, this is an issue as there is more than one marker that I may want to follow or not follow (to clarify I only want to follow one marker at a time).
the way i have solved a similar problem is to store the marker that is being followed into a variable
onclick="selectedMarker = myMarker"
Whenever you receive location updates simply focus the map on the selectedMarker
// update all locations
// ...
// focus selected marker
mymap.fitBounds(selectedMarker.getBounds());
#Nejc Jaminiks logic in his answer helped me work out what I needed to do.
When a marker is created I have this code appended to the marker in real time:
.on('click', focus);
I then have a function to detect when the marker is clicked and loop through setting the view to the marker until the marker (or another marker) is clicked then it will stop following it.
var focusenabled = true;
interval = null;
function focus(e) {
if (focusenabled) {
var i = 0;
interval = setInterval(function () {
mymap.setView(e.target.getLatLng());
console.log("focus active");
}, 2000);
focusenabled = false;
document.getElementById("footer").style.backgroundColor='red';
document.getElementById("footer").style.color='white';
} else {
clearInterval(interval);
document.getElementById("footer").style.backgroundColor='#222';
document.getElementById("footer").style.color='#222';
focusenabled = true;
}
};
I use the code below to change the footer div to red and text to white when a marker is being followed:
document.getElementById("footer").style.backgroundColor='red';
document.getElementById("footer").style.color='white';
And then when a marker is not being followed the footer div returns to the background color which effectivley hides it :)
document.getElementById("footer").style.backgroundColor='#222';
document.getElementById("footer").style.color='#222';

Leaflet - access specific polyline feature (GeoJSON)?

The situation: My web application shows a map with different trails of interest (my so called POIs) and a sidebar with information about each POI. Selecting a panel of the sidebar, the related POI should be selected/highlighted on the map.
Data and platforms used: I work with Leaflet and JavaScript, no jQuery. The data are added within Leaflet as GeoJSON. The trails are represented as polylines, but I call them POIs (just to clarify). I do not and cannot use jQuery.
What works: The trails (polylines) are added like this:
var pois = L.geoJson(pois,
{
style: style_pois,
onEachFeature: onEachFeature
});
pois.addTo(map);
function onEachFeature(feature, layer)
{
layer.on('click', function (e)
{
sidebar.open('pois');
//get the ID of the clicked POI
number = feature.properties.number;
//Open the accordion tab
var panel = document.getElementsByClassName('accordion-panel');
panel[number-1].style.display="block";
//highlight the selected POI
var selectedFeature = e.target;
selectedFeature.setStyle(style_pois_selected);
});
}
What does not work: Selecting a panel of the accordion, I get the ID of the related trail (polyline), but I cannot access and highlight this certain polyline feature within Leaflet.
This is the JavaScript code, where the accordion behavior is controlled:
var acc = document.getElementsByClassName('accordion');
var panel = document.getElementsByClassName('accordion-panel');
for (var i = 0; i < acc.length; i++)
{
(function(index){
acc[i].onclick = function()
{
// Toggle between adding and removing the "active" class,
//to highlight the button that controls the panel
this.classList.toggle("active");
//Toggle between hiding and showing the active panel
var panel = this.nextElementSibling;
console.log("panel " + acc[0]);
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}
var myIndex = index + 1;
alert("INDEX " + myIndex);
}
})(i);
}
Question: Is there a possibility, based on a layer that is included as GeoJSON in Leaflet to access a certain feature based on any property?
What I tried: I only came across solutions where the different behavior of a certain polyline is accessed within the onclick function. There it is easily possible to apply another color (setStyle). I need to access it from outside the layer. I already tried to again load the pois layer as I did above, just inside the accordion JavaScript and filter it for the certain ID so that only the one polyline is represented, but it only gave me an error that it is an invalid GeoJSON object (maybe a scope issue?).
I appreciate any help!
For anyone who might come across the same problem - I found a solution.
I looked for hours to find out, if one can access a specific feature from a GeoJSON layer within Leaflet, but it seemed that there is no such method.
Although there is no official method for it, for me worked the following.
When inside the accordion, one can just access the already loaded GeoJSON dataset, in my case pois and get the layer (this actually gets the feature, not the layer! a bit misleading) at the index position. For this one, a style can then be applied.
pois.getLayer(index).setStyle(style_pois)
To read out the index of the clicked accordion panel, I asked another question and was pointed in the right direction: Simple JavaScript accordion - how to get the index of the clicked panel?
NOTE: I'd recommend you to set some JFiddle to reproduce your problem.
A solution I often use is to set the ID/Class property in each of the markers/points:
$.getJSON("data/displacement.geojson", function(data){
pathsLayer = L.geoJson(data,{
className: function(feature){ //Sets the class on element
//Assuming your JSON has a property called ID
return "ID-" + feature.properties.ID;
},
style: function (feature) {
//If needed, you can also set style based on properties
},
})
});
After that you can set a global variable to keep record of the selection ID, and then use it to select and modify the specific element. Since Leaflet uses SVG elements, I recommend you to use D3.js to select/modify elements, for instance:
var selectedID = null; //Declare global variable
// You modify selectedID by actions on sidebar, e.g.:
selectedID = 001
d3.select(".ID-" + selectedID)
.transition() //You can set easily transitions on attribute changes
.duration(1000) // in ms
.attr("attributeName", "attributeValue");
You can find an example here (although I know is a bit tricky to read using View Page Source (Ctrl + U))

HTML Link to a Leaflet / Mapbox marker

I use Mapbox for a dynamic map on a website. It works really well except, I have a sidebar which list the pins with a little more description and an image. I want to make it so when I click on that sidebar, it would fire the click event of that marker on the map.
I used to do this all the time with Google Maps but now I'm stuck because even in the case that I can keep the instance of the marker, I cannot fire the click on it for some reason. It simply does nothing (maybe I need to re-bind the click event on it but I don't know how with mapbox)
I've encountered a few questions about this on Google and SO but none bring a real answer to that question except "keep the instance" which isn't always possible in some cases.
So basically I have a jQuery click event like this:
var marker = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [lng, lat]
},
properties: {}
};
if (isPin) {
marker.properties = pinStyles.pin;
} else if (isWinery) {
marker.properties = pinStyles.winery;
} else {
marker.properties = pinStyles.user;
}
marker.properties.title = locationName;
marker.properties.description = pin.description;
var markerObject = L.mapbox.markerLayer(marker);
// Add to cluster
markers.addLayer(markerObject);
$('#marker_list a.marker_item:last').click(function() {
var geoJson = markerObject.getGeoJSON();
markerObject.fire('click'); // does nothing (openPopup makes "Uncaught TypeError: Object [object Object] has no method 'openPopup' " so I guess I'm not doing it right)
});
And I have this (click event for mapbox marker):
map.markerLayer.on('click', function(e) {
map.setView(e.layer.getLatLng(), map.getZoom());
});
Anyone has an idea about wether 1) fix the non-firing event OR 2) make an HTML link fire a mapbox marker click event OR .openPopup?
Thanks and have a nice day!
MapBox's marker layer is a collection of Leaflet markers. You can create an href to a function that look for a particular marker based on it's layer id.
map.markerLayer.getLayers() returns an array of layer objects that contain both a _leaflet_id and the method togglePopup.
Try matching your href call to the leaflet id and then fire map.markerLayer.getLayers()[i].togglePopup()
Let me know if this helps.

Get box boundaries from select box in OpenLayers

I have an OpenLayers map and I want users to be able to draw a box by dragging their mouse (similar to this example here, select the "select feature (0 features selected)" option first) and obtain the boundaries of the drawn box.
I can manage to draw the box using smth like below, however it won't work when there are no features in the map or no features selected, and that will certainly be the case.
new OpenLayers.Control.SelectFeature(this._layers.osm, {
multiple: true,
box: true,
hover: false,
toggleKey: 'ctrlKey',
multipleKey: 'shiftKey',
onBeforeSelect: function() {
console.log(arguments);
}
})
Is there an easy way to accomplish this in OpenLayers or should I do the heavy lifting myself by tracking mouse drags and drawing/removing polygons accordingly?
Thanks.
Try to use "boxselectionend" event of SelectFeature control (requires 2.12)
But this event not returns boundaries or the selection made, only returns a layers array.
Another option is to create the Handler.Box externally, that is what I do in some cases as:
var mySelectFeature = OpenLayers.Control.SelectFeature(...);
var myHandlerBox = new OpenLayers.Handler.Box(
mySelectFeature, {
done: function(bounds) {
OpenLayers.Control.SelectFeature.prototype.selectBox.apply(
mySelectFeature, arguments);
... your code ...
}
},
{}
);

Google Maps API markers and zoom

I'm relativ new to the Google Maps API.
My situation: Website with an fullscreen Google map as background.
First problem: If the user scrolls down, or zooms out too much, there is an gray background. Is it possible to limit the max zoom-out, and that he can't scroll out? Or maybe repeat the map vertical (with markers).
If it's possible to set the max zoom-out and lock him within the map, how could I dynamically calculate the max zoom-out releated to the screen-resolution?
Second problem: Is it possible to add custom javascript to a marker?
Example: I create 5 markers, each one should hold an custom value (ID from the database). On click my own function should be called with the custom value as parameter and do some stuff.
Edit: This is what I mean with grey: (vertical "end" of google maps)
Map object have propert maxZoom, set it when map created
yes it is possible, you can add click events, change popups open, and since it is javascript you can add any additional data just as simple as markr.MyData = 'something interesting'
I am not sure what gray part you mean? I can't see gray parts with max and min zoom in google maps
Okay. Got it finally working! :)
Fixing grey area (handles also zooming):
function setBounds() {
google_allowedBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(-85.000, -122.591),
new google.maps.LatLng(85.000, -122.333));
google_lastCenter = google_map.getCenter();
google_lastZoom = google_map.getZoom();
google.maps.event.addListener(google_map, "bounds_changed", function () {
checkBounds();
});
google.maps.event.addListener(google_map, 'center_changed', function () {
checkBounds();
});
}
function checkBounds() {
if (google_allowedBounds.getNorthEast().lat() > google_map.getBounds().getNorthEast().lat()) {
if(google_allowedBounds.getSouthWest().lat() < google_map.getBounds().getSouthWest().lat()) {
google_lastCenter = google_map.getCenter();
google_lastZoom = google_map.getZoom();
return true;
}
}
google_map.panTo(google_lastCenter);
google_map.setZoom(google_lastZoom);
return false;
}
Marker click handler:
google_listener = google.maps.event.addListener(marker, 'click', markerHandler);
function markerHandler(event) {
window.console.log(this);
}

Categories