I think this means I'm missing something basic about the working of nested functions in javascript, but I'm failing to add or remove a layer from a leaflet map using a click function. Specifically, I have a leaflet map with numerous vector layers defined. One of these layers is added to the map on page load, and then subsequent layers are supposed to load on a click event in a layer selector.
Here's the function that handles the click, then updates the sidebar legend, updates classes in the layer selector, and is supposed to update the visible layer:
// ADD THE LAYER CONTROL FUNCTION
$('.layer').click(function() {
var oldLayer = $('.active').attr('id');
var newLayer = $(this).attr('id');
console.log(oldLayer + ' --> ' + newLayer);
$('#infobits').html('<h2>' + this.text + '</h2><hr>' + legendFormatter(
this.id));
map.removeLayer(oldLayer);
$('.layer').removeClass('active');
$(this).addClass('active');
map.addLayer(newLayer);
});
Every time I click on a new layer I get Uncaught TypeError: undefined is not a function, no matter where I put the function (I even had it inside the .getJSON() call for a bit). What's perplexing is that is I go to the JS console in my browser and type verbatim what the function is producing on line 49 of the above script (map.removeLayer(Percent_TC_bg)), the map removes the layer in question.
What can I do to get my map adding and removing layers without error?
I suppose TypeError is thrown inside addLayer or removeLayer. Map#addLayer and #removeLayer accepts ILayer object as parameter, but oldLayer and newLayer is obviously String, which came directly from HTML element's ID property.
So changing oldLayer and newLayer to appreciate ILayer object will fix the problem, I guess.
Edit: Igor beat me to it.
Mics is right - the HTML element's ID is a string, and doesn't reference the layer. You could do something like this after you define your layers to work around this issue:
var myLayers = {
"Percent_TC_bg": Percent_TC_bg,
"Percent_TC": Percent_TC
};
And then add the handler like so
$('.layer').click(function() {
var oldLayer = myLayers[$('.active').attr('id')];
var newLayer = myLayers[$(this).attr('id')];
// ...
});
Related
I'm trying to create custom zoom in/out buttons and I've got it working except for the most important part, the zooming..
I've created a map chart like this:
$scope.chart = new Highcharts.Map(config);
Then I have two functions which run whenever you click on zoom in or out:
highcharts.zoomIn = function() {
$scope.chart.mapZoom(0.5);
};
highcharts.zoomOut = function() {
$scope.chart.mapZoom(2);
};
Setting the mapZoom of the chart doesn't give me an error nor does it do anything. I tried calling $scope.chart.redraw() afterwards but it didn't help either. Also saw that it already calls the redraw function in the source code of the mapZoom function.
I can't find any information on how to do this so what exactly am I doing wrong here?
I am building the functionality that allows mapbox markers to be edited from within a CMS. The functionality should open up and populate a form when a map marker is clicked, and then allow the map marker to be dragged. When the form is saved, the content is submitted via ajax and then the map is reloaded with the featureLayer.loadURL("my_geojson_endpoint").
I have added comments throughout my code below to outline how I am getting to the error.
N.B. I define a property db_id in the geojson to identify each point because when you apply a filter, _leaflet_id changes. I also have jquery included in the code.
Code:
// loop through each marker, adding a click handler
$.each(points._layers, function (item) {
var point = points._layers[item];
attachClickHandler(point);
});
function attachClickHandler(point) {
// open the edit state for the marker on click
$(point._icon).on("click", function () {
openEditState(point);
})
}
function openEditState (point) {
disableEditOthers(point);
displayContent(point);
point.dragging.enable(); // this line causes the error
$(point._icon).off("click");
}
function disableEditOthers (point) {
// hide the other markers from the map (using db_id as mentioned above)
points.setFilter(function (f) {
return f.db_id === point.feature.db_id;
})
// this functions as a callback to display the popup
// since applying the filter on click, does not show the popup
setTimeout(function () {
for (key in points._layers) {
points._layers[key].openPopup();
}
}, 0)
}
In the map creation step I have been able to call this dragging.enable() method on each of the markers and provide "draggability" to all of them, however this is undesirable from a usability point of view. I want the user to clear swap in and out of an edit state.
I discovered this issue on github, solved by the solution to this. However after swapping my mapbox.js version out to the standalone and including the latest version of leaflet (0.7.3) the same error still occured.
Am I calling the function on the wrong property of the object? dumping out the "point" variable just before the line which errors does not show that the draggable property has the enable() function defined.
Any help is much appreciated.
Ok so as a slight workaround, but still not solving the original error.
$.each(points._layers, function (item) {
points._layers[item].dragging.enable()
})
Because I have filtered out the other points, enabling dragging on all points is working around the issue.
If you can provide a fix to my original fix (avoiding the loop) I am happy to accept it.
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.
I have a question regarding Google Maps and a jquery plugin that I use to display maps, etc.
All works fine, Marker Positioning, reload of page (with new db query to retrieve new data based on the new map coordinates, etc. ....)
Only thing that I did not manage to get to work is to change the behavior of the info windows (bubbles) on the map.
I would like them to open "onMouseOver" instead of "onClick" - can you help me out?
What I am using is this: http://gmap.nurtext.de/download.html
I know it's not the "most up tp date" thing, but it does what I need (and it's easy).
Sample is here: http://www.divessi.com/code/geo/divecenter.php?lat=48.14&lon=11.73&s=600
Christian
This should work. When looping throught the markers, add this:
GEvent.addListener(gmarker, 'mouseover', function() {
gmarker.openInfoWindowHtml(opts.html_prepend + marker.html + opts.html_append);
});
mouseover, or any other GMarker event http://code.google.com/apis/maps/documentation/javascript/v2/reference.html#GMarker
EDIT. I notice that you are using the deprecated Google Maps v2. I edited the code. Basically is what you had there. But be sure to add the listener before adding the marker to the overlay, just in case.
EDIT2. To avoid have the same infowindow for all your markers, wrap the code in a function so that a closure is created: (for more info read "The infamous Loop Problem" http://robertnyman.com/2008/10/09/explaining-javascript-scope-and-closures/ )
function listenMarker (gmarker,marker) {
GEvent.addListener(gmarker, 'mouseover', function() {
gmarker.openInfoWindowHtml(opts.html_prepend + marker.html + opts.html_append);
});
}
Put the function at the end of your file for example and call it inside the loop.
There is a js file included in the sample you linked to above
/code/js/jquery.gmap-1.1.0_CK.js
There are several lines of code in there related to the mouseover event and they are commented out:
//GEvent.addListener(gmarker,"mouseover", function() {
// this.openInfoWindowHtml(myHtml[j]);
//});
and
//GEvent.addListener(marker, "mouseover", function() {this.openInfoWindowHtml("no." + j + opts.markers[j]);});
//GEvent.addListener(gmarker,"mouseover", function() {
// this.openInfoWindowHtml("test " + j); //+ ": " + opts.html_prepend + marker.html + opts.html_append
//});
This is the reference for the Google Maps Javascript API V2 that the plugin is using:
http://code.google.com/apis/maps/documentation/javascript/v2/reference.html#GMarker
You can see definitions of the methods used by the plugin.
I'd recommend having a look at the reference then try uncommenting some of the lines above as you see fit.
Version 3 of the API is quite easy to use in case you wanted to try it without a plugin.
HTH
I have this code that generates markets I want to be clickable with a pop up info window.
for (i = 0; i < marker_array.length; i++) {
var point = new GLatLng(marker_array[i][0], marker_array[i][1]);
var marker = new GMarker(point, markerOptions);
GEvent.addListener(marker, "click", function() {
marker.openInfoWindowHtml(html_data);
});
map.addOverlay(marker);
}
The problem is that only one market ends up clickable. No matter which one gets clicked, an info window with the one clickable marker's data pops up over that one clickable marker. All of the markers load and are in the correct locations, so the problem is only with getting the pop up window data to appear for each one.
I've checked out the section about "unrolling" the marker function here and it seems like that's probably where I'm going wrong, but I have not been able to get this to work through testing the changes they suggest.
I believe your problem is that the variable html_data is the same for all iterations of this loop. You should update that variable each go-through in the loop for the values to be different.
I'm not quite sure if I follow, but are you saying that all popups have the same data in them?
I think that this is the problem, and that's because the way the event listeners work. When the click function happens it evaluates the listener event. So the HTML you're showing is always the same, as the variable is always being re-written to.
I used an array that matches my marker data for my HTML and it works well:
function createMarker(posn, title, icon, i) {
var marker = new GMarker(posn, {title: title, icon: icon, draggable:false});
GEvent.addListener(marker, 'mouseover', function() {
map.closeInfoWindow()
marker.openInfoWindowHtml(infoText[i])
} );
return marker;
}
I found the same case, and i have a solution for this problem. I suggest you to create a custom class extending Marker class. In this custom class, you should make a constructor that have a parameter(s) for your data, and this class should also have its own info window variable that will be called from your main application. For example:
The custom class:
public class StoreSpot extends Marker
{
public var infoWindow:InfoWindowOptions;
public var store_id:String;
public var address:String;
public var name:String;
...
}
The main application:
tempMarker = new StoreSpot(
tempLatlng,
new MarkerOptions({
icon:new spotStore(),
iconAlignment:MarkerOptions.ALIGN_HORIZONTAL_CENTER,
iconOffset:new Point(0,-50)
}),
temp.store_id,
temp.name,
temp.address,
temp.detail
);
This way you can place different info window for different marker. Hope this works for you. Enter code here.