how to keep reference to data structures inside recursive calls? - javascript

I have JS code with google maps api.
When I click on a marker I display more markers.
When I click on these markers I display more markers...and so on
I connect all these by polylines and the resulting structure looks like a spider map.
When I click on any marker agian, I want to hide the markers and polylines it created.
How do I create and keep a reference to these polylines and markers which are formed inside the recursive calls of the eventlistener for every parent marker, so that I could later on do a setMap(null);

I haven't tested it, but I believe you could add an array of shown markers inside a Marker object. It would look somehow like this:
var originalMarkers = new Array(); //load markers you show at the beginning to some array
function onMarkerClick(event) {
if (marker.childMarkers){
for (child in marker.childMarkers) {
child.setMap(null);
//and so on, e.g remove polylines
marker.childMarkers = null;
}
}
else {
marker.childMarkers = new Array();
//load markers you show after click here
}
}

Related

How to open popup for a specific marker inside a leaflet MarkerClusterGroup?

I want to open a popup for a marker that is under a markercluster when the map is zoomed out. This function is called when a user clicks on a search result.
This is the code that I am using:
map.eachLayer(function (layer) {
if (layer.options && layer.options.pane === "markerPane") {
if (layer.options.title == locationId) {
layer.openPopup()
}
}
});
I tried adding this code but it didn't work as well:
layer.zoomToBounds({padding: [20, 20]});
So you want to so something about a cluster's marker whenever a specific marker in such cluster fulfills a condition.
You can iterate through all visible cluster markers, then leverage getAllChildMarkers; but that will get messy soon, as you will have to deal with the fact that a cluster and the cluster's marker are different entities, so iterating through visible markers doesn't necessarily mean iterating through the visible clusters.
I suggest an approach based on getVisibleParent. Store a reference to each original marker, indexed by the ID you'll be using later for lookup, e.g. ...
var clusterGroup = L.markerClusterGroup();
var markers = {}; // Yay using Object as a hashmap!
for (var i in dataset) {
// Create individual marker based on a item in the dataset, e.g.
var marker = L.marker(dataset[i].latlng);
// Add that to the clusterGroup (but not to the map)
clusterGroup.addMarker(marker);
// Save the individual marker in the hashmap, indexed by the
// desired property, e.g. "locationId"
markers[ dataset[i].locationId ] = marker;
}
// Adding the cluster to the map after all items have been inserted should
// be slightly more performant than doing that before.
clusterGroup.addTo(map);
So with that, one should be able to look up the marker by the desired ID, see if it's in a cluster or directly visible, and do something about it:
function highlightLocationId(id) {
// hashmap lookup
var marker = markers[i];
// Sanity check
if (!marker) { return; }
// What cluster is this marker in?
var cluster = clusterGroup.getVisibleParent(marker);
// Is the marker really in a cluster, or visible standalone?
if (cluster) {
// It's in a cluster, do something about its cluster.
cluster.openPopup();
} else {
// It's not in a cluster but directly in the map, do something about it.
marker.openPopup();
}
}
I found a fix for this issue. Before trying to open the popup in the cluster I teleport to the coordinates of the location. Then the cluster will automatically open up.
map.setView([coordinates[1], coordinates[0]], 20);
I define which coordinates should be used and what the zoom level should be. After this function I use the layer.openPopup() function to open the popup.

How to modify or remove some existing data in GeoJSON Leaflet object?

Recently I asked about referencing the data of an existing GeoJSON Leaflet object. My Leaflet map consists of data coming in a stream to my GeoJSON Leaflet object. User inputs can change a filter for the GeoJSON data, so to make the filter apply to both the existing and new data I am keeping track of my data in an array called myFeatures. Whenever the filters change or an item in myFeatures changes, I do the following:
myGeoJson.clearLayers();
myGeoJson.addData(myFeatures);
This is working to make my map update according to the newly updated feature data or the changes in the filter.
I am applying pop-ups to the GeoJSON object when I initialize my GeoJSON object:
var myGeoJson = L.geoJson(myFeatures, {
style: function(feature) {
...
},
pointToLayer: function(feature, latlng) {
return L.circleMarker(latlng, geojsonMarkerOptions);
},
filter: function(feature, layer) {
...
},
onEachFeature: function(feature, layer) {
if (feature.properties && feature.properties.popupContent) {
layer.bindPopup(feature.properties.popupContent);
}
}
});
When I click on an individual feature, the pop-up appears. However, the pop-up dismisses pretty quickly, thanks to clearLayers and addData being called. :(
Is there some kind of way to stop the pop-up dismissing in this situation?
Or - better question - is there a way to modifying existing data in a GeoJSON object or remove some (not all) data from a GeoJSON object?
To provide some context, my GeoJSON shows circle markers for each feature. The circle markers are colored based on a property of the feature. The property can actually change over time, so the marker's styling needs to be updated. A marker also times out after a while and needs to be removed from the map, but the other markers need to stay on the map.
There are for sure better ways to do that, but if you don't want to modify your code architecture too much, you could just create your popups in a specific layer, which you won't clear when you add your new data.
To give you an idea (markers play below the role of myGeoJson in your example):
var popup_id = {};
var popup_layer = new L.layerGroup();
var markers = new L.layerGroup();
$.each(testData, function(index, p) {
var marker = L.marker(L.latLng(p.lat, p.lon));
markers.addLayer(marker);
popup = new L.popup({offset: new L.Point(0, -30)});
popup.setLatLng(L.latLng(p.lat, p.lon));
popup.setContent(p.text);
popup_id[p.id] = popup;
marker.on('click', function() {
popup_id[p.id].openPopup();
popup_layer.addLayer(popup_id[p.id]);
markers.clearLayers();
})
});
popup_layer.addTo(map);
markers.addTo(map);
You also keep track of all your popups in a dictionary popup_id.
Since you haven't provided us with a JSfiddle it is a bit difficult to find the perfect answer for your case, but I hope that the popup layer (also here in my fiddle) gives you a good direction.

Leaflet markercluster: How can I create a markercluster with numbered markers?

I am trying to create a markercluster that has markers with IDs, because I would later like to remove a single marker from the map. However, I'm having trouble even getting these labeled markers to appear on the map. I'm loading them from a geoJSON formatted variable individualPoints. I can add these markers to the map directly with the following code snippet:
// create object to save markers
var markersID = {};
// exchanges latitude & longitude
function switchLatLong(oldLocation) {
return new L.LatLng(oldLocation[1], oldLocation[0]);
}
var geoJsonLayer = L.geoJson(individualPoints, {
onEachFeature: function(feature, layer) {
/* can add directly to the map--this works */
markersID[feature.properties.id] =
L.marker(switchLatLong(feature.geometry.coordinates))
.addTo(map);
}
});
But I want them to be in a cluster, so I think I need to tag each marker with an ID (as in this question), then add this group to the cluster as follows:
var geoJsonLayer = L.geoJson(individualPoints, {
onEachFeature: function(feature, layer) {
/* tag the marker with the id */
markersID[feature.properties.id] =
L.marker(switchLatLong(feature.geometry.coordinates));
}
});
// create a marker cluster group, and add the markers to this.
var markers = L.markerClusterGroup({});
/* here's where I'm trying to toggle to use the markers with ID numbers */
//markers.addLayer(geoJsonLayer); // this works
markers.addLayers(markersID); // this doesn't
Adding geoJsonLayer works just fine, but adding markersID doesn't. So I must be adding markersID incorrectly.
Here's a jsfiddle.
https://jsfiddle.net/anfw0n6w/
Questions: Is there some better way to create markers that have an ID that I can refer to later? How else can I add markersID to the markerClusterGroup?
Is there some better way to create markers that have an ID that I can refer to later?
There are ways that work :-)
How else can I add markersID to the markerClusterGroup?
Since your markersID is a plain object, you cannot. addLayer accepts a Marker or a Layer Group (like your L.geoJson), and addLayers accepts an array of Markers.
Do not mix your need to refer to your markers later on, with the need to add them to your Marker Cluster Group.
What happens is that within your onEachFeature, you duplicate your Markers (create new ones) in markersID, instead of keeping a reference to the same layer that is built by L.geoJson. If you just keep a reference to layer, then you can refer directly to those markers, and you can directly add your L.geoJson layer group to your Marker Cluster Group:
var markersID = {};
var geoJsonLayer = L.geoJson(individualPoints, {
onEachFeature: function(feature, layer) {
// make a marker with that number
// actually no need to duplicate the marker.
//markersID[feature.properties.id] = L.marker(switchLatLong(feature.geometry.coordinates));
markersID[feature.properties.id] = layer;
}
});
markers.addLayer(geoJsonLayer); // this works
map.addLayer(markers);
// This will work, you just need to get the appropriate `id`.
markers.removeLayer(markersID[id]);
Demo: https://jsfiddle.net/anfw0n6w/1/

Use external links to open popup windows on leaflet map in drupal

Ok, here is my situation. I am using the leaflet map module with drupal. I have the map integrated on my website with views. I have nodes that contain content that I want to be displayed via a popup window. When I click each individual marker, the popup works exactly as I want. however, I want to be able to click an external link to be able to also open the popup. I have viewed and implemented this code from another question:
var markers = [];
var marker1 = L.marker([51.497, -0.09],{title:"marker_1"}).addTo(map).bindPopup("Marker 1");
markers.push(marker1);
var marker2 = L.marker([51.495, -0.083],{title:"marker_2"}).addTo(map).bindPopup("Marker 2");
markers.push(marker2);
var marker3 = L.marker([51.49, -0.097],{title:"marker_3"}).addTo(map).bindPopup("Marker 3");
markers.push(marker3);
function markerFunction(id){
for (var i in markers){
var markerID = markers[i].options.title;
if (markerID == id){
markers[i].openPopup();
};
}
}
$("a").click(function(){
markerFunction($(this)[0].id);
});
by user abenrob, but that doesn't work with markers generated by drupal.
My question has 2 parts, as I can see it.
1: How do I access the map inside my different block? I have set up the links from my menu block to call my function that contains the aforementioned code, and they call it correctly. However, when my Javascript needs to speak to the map, I get nothing.
Currently I have "var map = document.getElementById('leaflet-map');", but that seems to be pulling the div, not the map contained inside the div.
2: How do I access the list of markers generated by my map in drupal. Currently, as a test, I am just generating a marker manually and using the bindPopup function to bind the div containing the popup on the page, but I can't add it to the map (see part 1). Ideally I would not want to recreate the markers in javascript if they are already created in Drupal, but we don't always live in an ideal world, but it seems that if I get the map to connect, I could at least work with that.
In case anyone else stumbles across this with the same question, I figured out the first question. I accessed the map created by Drupal through the Leaflet module by utilizing the following code:
// This accesses the leaflet map created by drupal and sets the map variables so that they can be used with the functions
var map;
$(document).bind('leaflet.map', function(e, settingsLeaflet, lMap)
{
map = lMap;
});
I am still working on the second question. When I figure it out, I will add another update.
Edit: I was able to access the markers in the second question by using the following code:
var markers = {};
var markersList = [];
// This accesses the leaflet map features and pulls the marker variables so that they can be used with the functions
$(document).bind('leaflet.feature', function(e, lFeature, feature)
{
markers[feature.feature_id] = lFeature;
markersList.push(lFeature);
});
from there it was as easy as looping through the markers list, as such:
// This function takes the variable id, which is passed from the HTML call of this function. It then loops through the marker list and compares the id with the value of the title of each marker. If it finds a match, then it opens the popup bound to that specific marker.
function markerPopups(id)
{
// Loops through the markers list
for (var i = 0; i < markersList.length; i++)
{
// Sets a variable to get the title of the marker, which
var markerID = markersList[i].options.title.replace(/[^a-zA-Z0-9]/g, '_');
// Compares the variable passed through the function to the title of the marker. If there is a match, it opens the popup for that marker.
if(markerID == id)
{
markersList[i].openPopup();
}
}
}
Also, it wasn't needed to access the map once you accessed the pre-made markers, so you can ignore the first part, unless you need to use the map for anything else.

Display markers on Google Map using JSON

I have the following test Google Map: http://dev.driz.co.uk/googlemap/
I'm using the design of Foursquare markers as an example to test out my code and so far I have implemented a simple display of where YOU the user is in the world with a small avatar and when you hover it tells you this in the form of a tooltip.
The next part is using some JSON data: http://dev.driz.co.uk/googlemap/data.json
I want to display those 5 posts on the map using the coordinates that are stored in the data and display them in a similar fashion to the current marker. The user will then hover the marker and be able to see a tooltip with the following information as an example:
Cameron asked:
Is Star Wars 3d still on at the cinema?
2012-05-20 02:31:21
The user should be able to click on the marker to be taken to the post.
I've had a look around the Developer section of Google Maps, but don't seem to getting the right stuff and not sure how best to use it with my tooltip function and map implementation.
Can anyone help? Post some code examples?
Thanks
Follow these steps
Pull the data using an AJAX request and store it in a variable.
You can use jQuery for this. http://api.jquery.com/jQuery.getJSON/
$.getJSON('http://dev.driz.co.uk/googlemap/data.json', function(data){
// loop and add markers
});
Or you can use plain javascript and a JSON parser.
http://www.degraeve.com/reference/simple-ajax-example.php
http://www.json.org/
Loop in data and pick each item and add to map
for (var i = 0; i < data.length; i++) {
var item = data[i];
var markerLatlng = new google.maps.LatLng(item.Post.latitude, item.Post.longitude);
var marker = new google.maps.Marker({
position: markerLatlng
});
marker.item = item; // store information of each item in marker
marker.setMap(map); // where `map` is the instance of Google Map
google.maps.event.addListener(marker, "click", function(mark) {
// retrieve information using `this.item` and create dynamic HTML to show it.
// this.item.Post.datetime, this.item.Post.content etc.
});
}

Categories