Leaflet Choropleth Map - switch to Leaflet 1.2 - leaflet

I have a problem with my map. I switched to Leaflet 1.2 and one of the functions is not working correctly.
The code is here:
http://mapaszlakow.eu/mapa1.2.html
When activating an overlay which is geojson via js and clicking a bicycle route it zooms in to a route but it is not highlighted and the info window does not show up.
Here is exactly the same example that is working but on the older version of Leaflet (I think it is 0.7).
http://mapaszlakow.eu/
I can't locate the problem, the only thing I did is switching to Leaflet 1.2, I will be greatful for help.
EDIT: I believe, the problem is somewhere here:
function select(layer) {
info.update(layer.feature.properties);
if (selected !== null) {
var previous = selected;
}
map.fitBounds(layer.getBounds());
selected = layer;
if (previous) {
dehighlight(previous);
}
}
var selected = null;

I've tested your code from http://mapaszlakow.eu/mapa1.2.html and it works almost fine - on hover it highlights route correctly, on click it zooms to route extent - except from showing the popup info. To fix this please check out the fiddle I made from your code:
http://jsfiddle.net/5z17y5oL/18/ (this fiddle must be accessed through http because your server doesn't serve data with https)
The main difference is that I passed parameter to info.update(e.feature.properties); occurrences.
EDIT
My edited fiddle is here: http://jsfiddle.net/5z17y5oL/25/
So I moved the info.update(e.feature.properties); to be called from layer click listener. On map clicked layer style is reset and info is clearing as it's receiving null.
.....
onEachFeature: function(feature, layer) {
layer.on({
.....
'click': function(e) {
select(e.target);
info.update(e.feature.properties);
}
});
map.on({
'click': function(e) {
bicyclegeojson.resetStyle(layer);
info.update(null);
selected = null;
}
});
....
//end of EDIT
One more thing - you have small error, you should change this line:
map.addControl(layerControl).addTo(map);
to
map.addControl(layerControl);

Related

leafeltjs (mapbox) z-index ordering not working

Another developer created our original map but I'm tasked with making some changes. One of these is making sure the activated marker is brought to the front when clicked on (where it is partially overlapped by other markers).
The developers have used mapbox 2.2.2.
I have looked at leafletjs's docs, have followed some instructions on other posted solutions (e.g. solution one and solution two). Neither of these makes any difference.
Examining the marker in Chrome's console I can see the value of options.zIndexOffset is being set (10000 in my test case). I've even set _zIndex to an artificially high value and can see that reflected in the marker's data structure. But visually nothing is changing.
This is how the map is set up initially. All features are from a single geojson feed:
L.mapbox.accessToken = '<access token here>';
var map = L.mapbox.map('map', 'map.id', {
}).setView([37.8, -96], 3);
var jsonFeed, jsonFeedURL;
var featureLayer = L.mapbox.featureLayer()
.addTo(map)
.setFilter(function (f) {
return false;
});
$.getJSON(jsonFeedURL, function (json) {
jsonFeed = json;
jsonFeedOld = json;
// Load all the map features from our json file
featureLayer.setGeoJSON(jsonFeed);
}).done(function(e) {
// Once the json feed has loaded via AJAX, check to see if
// we should show a default view
mapControl.activateInitialItem();
});
Below is a snippet of how I had tried setting values to change the z-index. When a visual marker on the featureLayer is clicked, 'activateMarker' is called:
featureLayer.on('click', function (e) {
mapControl.activateMarker(e);
});
The GEOjson feed has urls for the icons to show, and the active marker icon is switched to an alternative version (which is also larger). When the active feature is a single Point I've tried to set values for the marker (lines commented out, some of the various things I've tried!)
activateMarker: function (e) {
var marker = e.layer;
var feature = e.layer.feature;
this.resetMarkers();
if (feature.properties.hasOwnProperty('icon')) {
feature.properties.icon['oldIcon'] = feature.properties.icon['iconUrl'];
feature.properties.icon['iconUrl'] = feature.properties.icon['iconActive'];
feature.properties.icon['oldIconSize'] = feature.properties.icon['iconSize'];
feature.properties.icon['iconSize'] = feature.properties.icon['iconSizeActive'];
}
if (feature.geometry.type == 'Point') {
marker.setZIndexOffset(10001);
marker.addTo(featureLayer);
}
//featureLayer.setGeoJSON(jsonFeed);
}
Any advice would be greatly appreciated! I'm at the point where I don't know what else to try (and that's saying something).
What probably happens is that you just flush your markers with the last call to .setGeoJSON():
If the layer already has features, they are replaced with the new features.
You correctly adjust the GeoJSON data related to your icon, so that when re-created, your featureLayer can use the new values to show a new icon (depending on how you configured featureLayer).
But anything you changed directly on the marker is lost, as the marker is removed and replaced by a new one, re-built from the GeoJSON data.
The "cleanest" way would probably be to avoid re-creating all features at every click.
Another way could be to also change something else in your GeoJSON data that tells featureLayer to build your new marker (through the pointToLayer option) with a different zIndexOffset option.

How to use the .openPopup method in a feature group?

I have a set of markers that have binded popups, but I can't figure out how to show all the popups when the marker group is toggled in the layers control.
For example, I have my markers like so:
var testMarker = L.marker([32.9076,33.35449]).bindPopup('Marker 1');
var testMarkerTwo = L.marker([33.58259,34.64539]).bindPopup('Marker 2');
Then, I put it in a freature group and append the openPopup method:
var markerGroup = L.featureGroup([testMarker,testMarkerTwo]).openPopup().addTo(map);
This doesn't work.
My final goal is to add that featureGroup to my layers control where I can toggle the group off/on. Before I get to that part, I need to first understand why the openPopup method is not working.
Edit: The answer below appears to only work with the plain Leaflet API, not the Mapbox API.
There are a couple problems here. The first is that by default, Leaflet closes the previously opened popup each time another is opened. The second is that, while your markers have popups bound to them, markerGroup does not, and even if it did, markerGroup.openPopup would only cause a single popup to open.
To get around the first problem, you can use the hack from this answer. If you place the following code at the beginning of your script (before you define your map) it will override the default behavior and allow you to open multiple popups at once:
L.Map = L.Map.extend({
openPopup: function(popup) {
this._popup = popup;
return this.addLayer(popup).fire('popupopen', {
popup: this._popup
});
}
});
Then, once you are able to open multiple popups, you can open all popups in markerGroup using the eachLayer method:
var markerGroup = L.featureGroup([testMarker,testMarkerTwo]).addTo(map);
markerGroup.eachLayer(function(layer) {
layer.openPopup();
});
Here is an example fiddle:
http://fiddle.jshell.net/nathansnider/02gsb1Lt/

Leaflet Spiderfier/MarkerCluster - Automatically spiderfy dynamic markers

I'm building an online version of a boardgame using Leaflet as the viewport.
My source: https://github.com/edenLOL/gotta-chug-em-all/tree/master
What I'm trying to achieve, is a way to spiderfy/spread out markers that land on the same square as each other, including when they move past each other.
I'm using https://github.com/jawj/OverlappingMarkerSpiderfier-Leaflet to Spiderfy the markers, which appears to work when clicking the markers.
However, I need these markers to be spiderfied always, without requiring a click event. So I set keepSpiderfied: true for the spiderfier object (oms).
var options = { //**spiderfier
keepSpiderfied: true,
nearbyDistance: 120
};
oms = new OverlappingMarkerSpiderfier(map, options); //**spiderfier
But this doesn't work. I can't seem to load the markers in a spiderfied state, it always requires a click to spiderfy them.
For context, markers are generated dynamically during a prompt before the user sees the map.
('#playerIcons > table > tbody > tr > td').on('click', function(){
pokemonSelected = $(this).attr('class');
if ( !$(this).hasClass('pokemonSelected') ){
playerArray[pokeCounter].pokemon = ''+ pokemonSelected +'';
playerArray[pokeCounter].marker = L.marker([playerArray[pokeCounter].coords[0], playerArray[pokeCounter].coords[1]], {
icon: window[pokemonSelected]
}).addTo(map);
oms.addMarker(playerArray[pokeCounter].marker) //**spiderfier
pokeCounter += 1;
} else {
return false;
};
});
I've also tried using Leaflet/Leaflet.markercluster. This works to an extent, and spiderfies the Markers at defined zoom levels, however the markers don't move, and stay stacked on top of each other.
What am I missing here that could be causing these issues? I don't mind using either Spiderfier or MarkerCluster, as both should be able to provide the solution I'm looking for once either issue is fixed.
Spiderfier: Markers need to spiderfy automatically
MarkerCluster: Markers don't physically move when spiderfying out of cluster
Note: If you decide to open index.html, be aware the Pokemon theme song will autoplay in the background on page load (until the map is drawn), so check your volume :)
I solve the problem like this, using fire('click') аfrom leaflet.
scope.spiderMarker - the marker, i want to spidrefy. In your case i think its playerArray[pokeCounter].marker
try{
scope.spiderMarker.fire('click')}
catch (e) {
console.log(e);
};

can't tap on item in google autocomplete list on mobile

I'm making a mobile-app using Phonegap and HTML. Now I'm using the google maps/places autocomplete feature. The problem is: if I run it in my browser on my computer everything works fine and I choose a suggestion to use out of the autocomplete list - if I deploy it on my mobile I still get suggestions but I'm not able to tap one. It seems the "suggestion-overlay" is just ignored and I can tap on the page. Is there a possibility to put focus on the list of suggestions or something that way ?
Hope someone can help me. Thanks in advance.
There is indeed a conflict with FastClick and PAC. I found that I needed to add the needsclick class to both the pac-item and all its children.
$(document).on({
'DOMNodeInserted': function() {
$('.pac-item, .pac-item span', this).addClass('needsclick');
}
}, '.pac-container');
There is currently a pull request on github, but this hasn't been merged yet.
However, you can simply use this patched version of fastclick.
The patch adds the excludeNode option which let's you exclude DOM nodes handled by fastclick via regex. This is how I used it to make google autocomplete work with fastclick:
FastClick.attach(document.body, {
excludeNode: '^pac-'
});
This reply may be too late. But might be helpful for others.
I had the same issue and after debugging for hours, I found out this issue was because of adding "FastClick" library. After removing this, it worked as usual.
So for having fastClick and google suggestions, I have added this code in geo autocomplete
jQuery.fn.addGeoComplete = function(e){
var input = this;
$(input).attr("autocomplete" , "off");
var id = input.attr("id");
$(input).on("keypress", function(e){
var input = this;
var defaultBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(37.2555, -121.9245),
new google.maps.LatLng(37.2555, -121.9245));
var options = {
bounds: defaultBounds,
mapkey: "xxx"
};
//Fix for fastclick issue
var g_autocomplete = $("body > .pac-container").filter(":visible");
g_autocomplete.bind('DOMNodeInserted DOMNodeRemoved', function(event) {
$(".pac-item", this).addClass("needsclick");
});
//End of fix
autocomplete = new google.maps.places.Autocomplete(document.getElementById(id), options);
google.maps.event.addListener(autocomplete, 'place_changed', function() {
//Handle place selection
});
});
}
if you are using Framework 7, it has a custom implementation of FastClicks. Instead of the needsclick class, F7 has no-fastclick. The function below is how it is implemented in F7:
function targetNeedsFastClick(el) {
var $el = $(el);
if (el.nodeName.toLowerCase() === 'input' && el.type === 'file') return false;
if ($el.hasClass('no-fastclick') || $el.parents('.no-fastclick').length > 0) return false;
return true;
}
So as suggested in other comments, you will only have to add the .no-fastclick class to .pac-item and in all its children
I was having the same problem,
I realized what the problem was that probably the focusout event of pac-container happens before the tap event of the pac-item (only in phonegap built-in browser).
The only way I could solve this, is to add padding-bottom to the input when it is focused and change the top attribute of the pac-container, so that the pac-container resides within the borders of the input.
Therefore when user clicks on item in list the focusout event is not fired.
It's dirty, but it works
worked perfectly for me :
$(document).on({
'DOMNodeInserted': function() {
$('.pac-item, .pac-item span', this).addClass('needsclick');
}
}, '.pac-container');
Configuration: Cordova / iOS iphone 5

Mobile Safari: Disable scrolling pages "out of screen"

I want to block scrolling page "out of the iPhone screen" (when gray Safari's background behind the page border is visible). To do this, I'm cancelling touchmove event:
// Disables scrolling the page out of the screen.
function DisableTouchScrolling()
{
document.addEventListener("touchmove", function TouchHandler(e) { e.preventDefault(); }, true);
}
Unfortunately, this also disables mousemove event: when I tap on a button then move my finger out of it, then release the screen, the button's onclick event is triggered anyway.
I've tried mapping touch events on mouse events, as desribed here: http://ross.posterous.com/2008/08/19/iphone-touch-events-in-javascript/, but to no avail (the same behavior).
Any ideas?
From what I understand of your question, you've attempted to combine the code you've presented above with the code snippet provided by Ross Boucher on Posterous. Attempting to combine these two snippets back-to-back won't work, because in disabling touchmove, you've also disabled the shim that allows mousemove to work via his sample.
This question and its answers sketch out a workable solution to your problem. You should try these two snippets to see if they resolve your issue:
This snippet, which disables the old scrolling behavior:
elementYouWantToScroll.ontouchmove = function(e) {
e.stopPropagation();
};
Or this one, from the same:
document.ontouchmove = function(e) {
var target = e.currentTarget;
while(target) {
if(checkIfElementShouldScroll(target))
return;
target = target.parentNode;
}
e.preventDefault();
};
Then, drop in the code on Posterous:
function touchHandler(event)
{
var touches = event.changedTouches,
first = touches[0],
type = "";
switch(event.type)
{
case "touchstart": type = "mousedown"; break;
case "touchmove": type="mousemove"; break;
case "touchend": type="mouseup"; break;
default: return;
}
//initMouseEvent(type, canBubble, cancelable, view, clickCount,
// screenX, screenY, clientX, clientY, ctrlKey,
// altKey, shiftKey, metaKey, button, relatedTarget);
var simulatedEvent = document.createEvent("MouseEvent");
simulatedEvent.initMouseEvent(type, true, true, window, 1,
first.screenX, first.screenY,
first.clientX, first.clientY, false,
false, false, false, 0/*left*/, null);
first.target.dispatchEvent(simulatedEvent);
event.preventDefault();
}
And that should do it for you. If it doesn't, something else isn't working with Mobile Safari.
Unfortunately I haven't had the time to check out to above yet but was working on an identical problem and found that the nesting of elements in the DOM and which relation you apply it to affects the handler a lot (guess the above solves that, too - 'var target = e.currentTarget').
I used a slightly different approach (I'd love feedback on) by basically using a class "locked" that I assign to every element which (including all its children) i don't want the site to scroll when someone touchmoves on it.
E.g. in HTML:
<header class="locked">...</header>
<div id="content">...</div>
<footer class="locked"></div>
Then I have an event-listener running on that class (excuse my lazy jquery-selector):
$('.ubq_locked').on('touchmove', function(e) {
e.preventDefault();
});
This works pretty well for me on iOs and Android and at least gives me the control to not attach the listener to an element which I know causes problems. You do need to watch your z-index values by the way.
Plus I only attach the listener if it is a touch-device, e.g. like this:
function has_touch() {
var isTouchPad = (/hp-tablet/gi).test(navigator.appVersion);
return 'ontouchstart' in window && !isTouchPad;
}
This way non-touch devices will not be affected.
If you don't want to spam your HTML you could of course just write the selectors into an array and run through those ontouchmove, but I would expect that to be more costly in terms of performance (my knowledge there is limited though). Hope this can help.