How to add a markercluster to Mapbox StoreLocator Template - leaflet

i tried different ways to synchronize the cluster with the store locator. It is easy to add the cluster, but than i have some problems with the list on the left side.
My problem is, that i am not really sure how to integrate this function, that at the end the store locator works with the custom markers and the list is synchronize in the right way.
Store Locator:
https://jsfiddle.net/sd5vt439/
L.mapbox.featureLayer('examples.map-h61e8o8e').on('ready', function(e) {
var clusterGroup = new L.MarkerClusterGroup();
e.target.eachLayer(function(layer) {
clusterGroup.addLayer(layer);
});
map.addLayer(clusterGroup);
});

ok, i tried a little bit and it works now with nested functions:
locations.on('ready', function(e) {
var clusterGroup = new L.MarkerClusterGroup();
e.target.eachLayer(function(layer) {
clusterGroup.addLayer(layer);
});
map.addLayer(clusterGroup);
locations.eachLayer(function(locale) {
....
}

Related

Leaflet-geoman remove button not working after bind a new layer click function

I need to bind a custom click function on the drawn shapes. I'm using the following code for that:
map.on('pm:create', function(e) {
e.layer.on('click', function(e) {
document.getElementById('info-pane').style.display = 'block';
});
});
When I bind this new click function, I'm not able to remove the shape anymore. When I am in the remove mode, the click is triggering the show info-pane instead of remove the shape.
How can I bind a custom click function to the shapes without "deactivate" any leaflet-geoman functionality such as the Remove ?
Well,
Including this L.DomEvent.stopPropagation(e); seems to be working now.
map.on('pm:create', function(e) {
e.layer.on('click', function(e) {
document.getElementById('info-pane').style.display = 'block';
});
L.DomEvent.stopPropagation(e);
});

Mapquest routes using custom icons

I'm using Mapquest's Javascript API for leaflet.
My code looks like this:
dir = MQ.routing.directions()
.on('success',
function (data) {
console.log(data);
var legs = data.route.legs;
var maneuvers;
if (legs && legs.length) {
maneuvers = $.map(legs[0].maneuvers,
function(m) {
return new Maneuvers(m);
});
self.maneuvers(maneuvers);
}
});
dir.route({
locations: [
self.from(),
self.to()
]
});
map.addLayer(MQ.routing.routeLayer({
directions: dir,
fitBounds: true
}));
The results I get looks like this:
Although, this looks good, the icons don't look anything like the Get Direction module on mapquest.com
How can I change the icons so that they look more modern?
Our new static map v5 is now in beta, this includes a new customizable icon service. check it out:
https://developer.mapquest.com/documentation/static-map-api/v5/getting-started/
There is a new icon service coming in the not too distant future with more modern icons. It will be a companion to a new static map version. Sorry, no eta yet.

Using search feature in Ionic framework

I am a UI person and very new to ionic framework.. I wanted to add search feature in my android app built using Ionic framework. After a research i found that I will need to use this plugin https://github.com/djett41/ionic-filter-bar. but there is no detail documentation available. Can anyone please guide how to use this plugin working. I have made all setup but stuck with actual code.
First of all you must install the plugin. You can use bower for that:
bower install ionic-filter-bar --save
and it will copy all the javascript and css needed in the lib folder inside www.
Then you must add the references to the css to your index.html:
<link href="lib/ionic-filter-bar/dist/ionic.filter.bar.css" rel="stylesheet">
same thing for the javascript:
<script src="lib/ionic-filter-bar/dist/ionic.filter.bar.js"></script>
You have to inject the module jett.ionic.filter.bar you your main module:
var app = angular.module('app', [
'ionic',
'jett.ionic.filter.bar'
]);
and you must reference the service $ionicFilterBar in your controller:
angular.module('app')
.controller('home', function($scope, $ionicFilterBar){
});
Now you can start using it.
In my sample I want to trigger the search-box when the user clicks/taps on a icon in the header. I would add this HTML to the view:
<ion-nav-buttons side="secondary">
<button class="button button-icon icon ion-ios-search-strong" ng-click="showFilterBar()">
</button>
</ion-nav-buttons>
The action trigger an event in my controller showFilterBar:
$scope.showFilterBar = function () {
var filterBarInstance = $ionicFilterBar.show({
cancelText: "<i class='ion-ios-close-outline'></i>",
items: $scope.places,
update: function (filteredItems, filterText) {
$scope.places = filteredItems;
}
});
};
which creates the $ionicFilterBar and shows it.
As you can see here I am using an array of objects $scope.places
$scope.places = [{name:'New York'}, {name: 'London'}, {name: 'Milan'}, {name:'Paris'}];
which I have linked to the items member of my $ionicFilterBar. The update method will give me in filteredItems the items (places) filtered.
You can play with this plunker.
Another option is to use the plugin to actually fetch some data remotely through $http.
If we want to achieve this we can use the update function again.
Now we don't need to bind the items to our array of objects cause we won't need the filtered elements.
We will use the filterText to perform some action:
$scope.showFilterBar = function () {
var filterBarInstance = $ionicFilterBar.show({
cancelText: "<i class='ion-ios-close-outline'></i>",
// items: $scope.places,
update: function (filteredItems, filterText) {
if (filterText) {
console.log(filterText);
$scope.fetchPlaces(filterText);
}
}
});
};
We will call another function which will, maybe, call $http and return some data which we can bind to our array of objects:
$scope.fetchPlaces = function(searchText)
{
$scope.places = <result of $http call>;
}
Another plunker here.
PS:
If you want to configure it using some sort of customization you must do it in your configuration using the provider $ionicFilterBarConfigProvider:
angular.module('app')
.config(function($ionicFilterBarConfigProvider){
$ionicFilterBarConfigProvider.clear('ion-ios-close-empty');
})
PPS:
In my plunker I've included the css and the script directly copying it from the source.
UPDATE:
Someone asked not to replace the list with the updated one.
My cheap and dirty solution is to check if the filterText contains some values. If it's empty (no searches) we go throught each element an set a property found = false otherwise we compare the places array we the filteredItems array.
Matching elements will be marked as found.
function allNotFound(filteredItems) {
angular.forEach($scope.places, function(item){
item.found = false;
});
}
function matchingItems(filteredItems) {
angular.forEach($scope.places, function(item){
var found = $filter('filter')(filteredItems, {name: item.name});
if (found && found.length > 0) {
console.log('found', item.name);
item.found = true;
} else {
item.found = false;
console.log('not found', item.name);
}
});
and now we can integrate the filter bar this way:
$scope.showFilterBar = function () {
var filterBarInstance = $ionicFilterBar.show({
cancelText: "<i class='ion-ios-close-outline'></i>",
items: $scope.places,
update: function (filteredItems, filterText) {
if (!filterText) {
allNotFound();
} else {
matchingItems(filteredItems);
}
}
});
};
We can use the found attribute of the object to change the style of the element.
As always, a Plunker to show how it works.
Ionic uses Angular, and Angular include an atributte filter very useful. Look this: https://docs.angularjs.org/api/ng/filter/filter and the example there. Regards

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

duplicate layer/ show permanantly on click

I'm starting to learn how to use leaflet. I'm trying to create a map with markers. If you hover them they should display a route. if the mouse leaves the marker the route should be deleted. (This part works)
When you click on the marker the route should stay on the map even when the mouse leaves the marker.
Therefore I would need to duplicate the route layer so that it doesn't get deleted when the mouse leaves the marker. Or there is a better method that I don't know.
function Route() {
DirectionsLayerLong = omnivore.gpx('GPX/ Route_long.gpx');
DirectionsLayerLong.on('ready', function() {
this.setStyle(style_long);
});
DirectionsLayerShort = omnivore.gpx('GPX/Route_short.gpx');
DirectionsLayerShort.on('ready', function() {
this.setStyle(style_short);
});
return DirectionsLayer = L.featureGroup([DirectionsLayerLong, DirectionsLayerShort]);
};
var Marker = L.marker([50, -100], {
icon: iconfu
}).addTo(map);
Marker.on('mouseover', function(e) {
Route();
DirectionsLayer.addTo(map);
});
Marker.on('mouseout', function(e) {
DirectionsLayer.remove()
});
Marker.on('click', function(e) {
DirectionsPermaLayer.remove();
Route();
DirectionsPermaLayer = DirectionsLayer;
DirectionsPermaLayer.addTo(map);
});
I could simply use omnivore with another variable but I'd like to reuse the function.
The simplest solution is just to remove the mouseout event listener when you click on the marker:
Marker.on('click', function(e) {
Marker.off('mouseout');
});
Cloning your route layer would be a little more complicated (not to mention unnecessary, if removing the event listener solves your problem), but it's worth exploring how one might do that. First of all, a concise explanation of why you can't just create a copy using DirectionsPermaLayer = DirectionsLayer can be found on w3schools:
Objects are mutable: They are addressed by reference, not by value.
If y is an object, the following statement will not create a copy of
y:
var x = y; // This will not create a copy of y.
The object x is not a copy of y. It is y. Both x and y points to the
same object.
Any changes to y will also change x, because x and y are the same
object.
There are many ways to go about cloning an object in Javascript, but I suspect that most of these will not work for cloning leaflet layers, as all Leaflet's internal ids will be copied along with the object, causing unpredictable behavior. The best strategy would probably be to convert DirectionsLayerShort and DirectionsLayerLong to GeoJSON using the .toGeoJSON method, then read them back in using L.geoJson:
var Short2 = L.geoJson(DirectionsLayerShort.toGeoJSON()).setStyle(style_short);
var Long2 = L.geoJson(DirectionsLayerLong.toGeoJSON()).setStyle(style_long);
var Directions2 = L.featureGroup([Long2, Short2]).addTo(map);
This could require a little refactoring of your code, but it should do the job.