I have a map with cluster based markers. I want a way when the user clicks on a cluster with more than 100 markers, don't zoom in and do something else like I am opening a popup; else just decluster.
On Load Page, I am calling a service and getting list of customers with locations. I am passing the customers to the Clustering function. Now when the user clicks on the cluster I want to put some conditions. if true open a popup but do not zoom in neither decluster. I am not able to do this. This is the code
Customers.html
<div [hidden]="!MapView" style="height: 100%" #map id="map"></div>
Customers.ts
#ViewChild('map') mapElement: ElementRef;
ngOnInit() {
this.initMap();
}
initMap = () => {
this._customerservice.GetCustomersWithLocations().subscribe(res => {
if (res != null || res != undefined) {
this.CustomersLocation = res;
let latLng = new google.maps.LatLng(-31.563910, 147.154312);
let mapOptions = {
center: latLng,
zoom: 6,
mapTypeId: google.maps.MapTypeId.TERRAIN,
fullscreenControl: false
}
setTimeout(() => { // LOAD THE MAP FIRST
this.map = new google.maps.Map(this.mapElement.nativeElement, mapOptions);
}, 1000);
setTimeout(() => { //LOAD THE CLUSTER
this.mapCluster.addCluster(res, this.map);
}, 3000);
}
}, error => {
}, () => {
});
}
GoogleMapsCluster.ts
addCluster(Customers: any, map) {
//console.log(Customers);
if (google.maps) {
//Convert locations into array of markers
let markers = Customers.map((location) => {
var marker = new google.maps.Marker({
position: location.loc,
label: location.name,
});
return marker;
});
this.markerCluster = new MarkerClusterer(map, markers, { imagePath: 'assets/m' });
google.maps.event.addListener(this.markerCluster, 'clusterclick', (cluster) => {
var markers = cluster.getMarkers();
var CustomerInsideCluster = [];
for (var i = 0; i < markers.length; i++) {
CustomerInsideCluster.push({BusinessName: markers[i].label})
}
this.OpenCustomerDetails(CustomerInsideCluster);
//I OPEN A POPUP HERE. I DONT WANT TO ZOOM IN TO DECLUSTER. AT THEN END IF THE CLUSTER HAS MORE THAN 100 CUSTOMERS I DONT WANT TO ZOOM IN WHEN I CLICK ON IT.
//map.setZoom(8); GIVING ME ERROR
});
} else {
console.warn('Google maps needs to be loaded before adding a cluster');
}
}
OpenCustomerDetails(Customers: any) {
let popover = this.popoverCtrl.create(MapPopover, { Customers: Customers }, { cssClass: 'custom-popover' });
popover.present({
ev: event
});
}
This is a possible solution. Just set the markerClusterer property zoomOnClick to false.
var markerCluster = new MarkerClusterer(map, markers, {
imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m',
zoomOnClick: false
});
Within your clusterclick event, pass the counter object to the callback function and by using a conditional statement based on your requirements, open an infowindow (or else if you prefer).
markerCluster.addListener('clusterclick', function(cluster){
if (markers.length > 5){ // change #5 if you need to test different scenarions
infowindow.setPosition(cluster.getCenter());
infowindow.setContent("Simple Test");
infowindow.open(map);
}
If the markers are less or more than the required parameters, reset the zoomOnClick to true again and use the following map and clusterObject methods:
else {
markerCluster.zoomOnClick = true;
map.fitBounds(cluster.getBounds());
}
Proof of concept in this JSBIN
Related
I have looked all over but can't find an answer to this. I have a map that when I click on it adds markers to where I clicked. I am pushing those markers into an array but when I do one marker just seems to override the one that was in the array before instead of adding another index to the array. The array just always looks like this [me] no matter how many markers are on the map. Here is the code
addLatLng: function(event) {
var path = this.poly.getPath();
var markers = [];
path.push(event.latLng);
this.calcDistance(path);
var marker = new google.maps.Marker({
position: event.latLng,
title: '#' + path.getLength(),
map: this.map
});
markers.push(marker);
console.log(markers);
console.log(marker);
// debugger;
function removeMarkers(map) {
for (var i = 0; i < markers.length; i++) {
markers[i].setMap(map);
}
markers = [];
}
$('#btn-clear-map').on('click', function(event) {
event.preventDefault();
removeMarkers(null);
});
$('#btn-clear-point').on('click', function(event) {
event.preventDefault();
markers[markers.length -1].setMap(null);
});
},
this is part of a backbone view if that makes any difference. I just have no idea why when I push a marker in it seems to override the one that was already in there.
Edit: Ok I just figured out why, every time I click to make a new marker, it is resetting the marker array. Any clever ways to get around this?
The problem is that you are re-declaring the markers array on each call to addLatLng method (Also you're new event handlers and creating removeMarkers function and a closure each time)
Instead, you should keep the markers array as a property of the view as shown below:
Backbone.View.extend({
initialize: function() {
this.markers = [];
},
events: {
'click #btn-clear-map': 'removeMarkers',
'click #btn-clear-point': 'clearPoint',
},
render: function() {},
addLatLng: function(event) {
var path = this.poly.getPath();
path.push(event.latLng);
this.calcDistance(path);
var marker = new google.maps.Marker({
position: event.latLng,
title: '#' + path.getLength(),
map: this.map
});
this.markers.push(marker);
},
removeMarkers: function(event) {
event.preventDefault();
for (var i = 0; i < this.markers.length; i++) {
this.markers[i].setMap(null);
}
this.markers = [];
},
clearPoint: function(event) {
event.preventDefault();
this.markers[this.markers.length - 1].setMap(null);
}
});
I'm currently adding markers on a google map when I click on a given location. The markers are currently saved both to a Collection - Markers - and in to a more classical array. What I would like to now is to add new marker to the map as they are added by other users.
My question is: Is there any way to get notified when the collection is modified (e.g. another user add a marker) ?
Maybe this is not the best way to do it. Any other suggestion?
if (Meteor.isClient) {
Template.hello.rendered = function(){
markersArray = [];
var latlng = new google.maps.LatLng(46.123, 8.843);
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (position) {
latlng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
map.setCenter(latlng);
});
}
var opts = {
zoom: 16,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map-canvas"), opts);
// add a click event handler to the map object
google.maps.event.addListener(map, "click", function(event)
{
placeMarker(event.latLng);
});
Session.set('map', true);
function placeMarker(location) {
var marker = new google.maps.Marker({
position: location,
map: map
});
Markers.insert({"marker": location, "date": new Date()});
markersArray.push(marker);
}
}
}
As per the previous answer, you should use an observestructure, but you really need to avoid leaving the observer running forever:
Template.hello.rendered = function() {
this.markerObserve = Markers.find({}).observe({
added: function(m) {
placeMarker(m.location) // obviously depends on the structure of Markers documents
}
});
};
Template.hello.destroyed = function() {
this.markerObserve.stop();
}
You have to use observer
Template.hello.rendered = function(){
Markers.find({}).observe({
added: function (m) {
// Add marker
}
});
}
I have a map where I want to add a marker (with info window) via a checkbox. It also works quite well, but I just can not get it deleted again when I uncheck the checkbox. Can anyone help?
See also here: http://jsfiddle.net/x8D7y/
function clearOverlays() {
google.maps.event.clearListeners(marker300, 'click');
}
function showOverlays() {
var marker300 = new google.maps.Marker({
position: new google.maps.LatLng(45.0, 1.0),
map: map /*,
icon: 'img/bike5.png' */
});
var infowindow300 = new google.maps.InfoWindow({
content: '<div style="width: 200px;">Test 300 - Link</div>'
});
google.maps.event.addListener(marker300, 'click', function() {
infowindow300.open(map, marker300);
});
}
You have to use an external Array which holds the extra markers you use with the map. In your case I have added the following array:
var extraMarkers = [];
Then when I click the checkbox, I am getting the ID of that checkbox, and send it in both showOverlays() and clearOverlays() as function argument.
Then, in showOverlays(), I am using the checkbox ID as extraMarkers key and the marker as value.
Finally, in clearOverlays() I use again the checkbox ID to get the element with this ID from the extraMarkers array and I see the map to null, in order to remove the marker.
See here the working example : http://jsfiddle.net/x8D7y/1/
Here is the full code required by you:
var map;
var extraMarkers = [];
var myOptions = {
zoom: 8,
center: new google.maps.LatLng(45.0, 1.0)
};
map = new google.maps.Map($('#map')[0], myOptions);
var marker1 = new google.maps.Marker(
{
position: new google.maps.LatLng(45.5, 1.5),
map: map /*,
icon: 'img/bike5.png' */
}
);
var infowindow1 = new google.maps.InfoWindow(
{
content: '<div style="width: 200px;">Test 1 - Link</div>'
}
);
google.maps.event.addListener(
marker1,
'click',
function()
{
infowindow1.open(map, marker1);
}
);
function clearOverlays(myID)
{
google.maps.event.clearListeners(extraMarkers[myID], 'click');
extraMarkers[myID].setMap(null);
}
function showOverlays(myID)
{
var marker300 = new google.maps.Marker(
{
position: new google.maps.LatLng(45.0, 1.0),
map: map /*,
icon: 'img/bike5.png' */
}
);
extraMarkers[myID] = marker300;
var infowindow300 = new google.maps.InfoWindow(
{
content: '<div style="width: 200px;">Test 300 - Link</div>'
}
);
google.maps.event.addListener(
marker300,
'click',
function()
{
infowindow300.open(map, marker300);
}
);
}
$('#mapall').change(
function()
{
var myID = $(this).attr('id');
if($('#mapall').attr('checked'))
{
showOverlays(myID);
}
else
{
clearOverlays(myID);
}
}
);
if you have single marker on map then use marker.setMap(null);
if Multiple marker make an array for marker
markersArray.push(marker);
if (markersArray) {
for (i in markersArray) {
markersArray[i].setMap(null);
}
}
Try this:
marker300.setMap(null);
Your marker was not visible outside of showOverlays() function. Error was reported in console:
Uncaught ReferenceError: marker300 is not defined
Minimum change is to define marker300 as global:
var map;
var marker300;
and delete marker in
function clearOverlays() {
google.maps.event.clearListeners(marker300, 'click');
marker300.setMap(null);
}
and remove var in front of variable marker300 in function showOverlays()
See example in fiddle
If you want to have several markers than you will have to follow solution from user Merianos Nikos
For a school project we are having this idea of making a geospatial tag-game. You log in on our app, your location is shown on the map, and whenever you get close to another player, you tag that person. (Like children's tag but with meteor)
The issue we are having, we seem not able to auto-update our marker on the leaflet map. There's an marker showing it's just not updating.
We tried using Player.update in a time but it doesn't work.
Any suggestions?
The code
if (Meteor.isClient) {
var userLatitude;
var userLongitude;
var map;
Template.map.rendered = function () {
// Setup map
map = new L.map('map', {
dragging: false,
zoomControl: false,
scrollWheelZoom: false,
doubleClickZoom: false,
boxZoom: false,
touchZoom: false
});
map.setView([52.35873, 4.908228], 17);
//map.setView([51.9074877, 4.4550772], 17);
L.tileLayer('http://{s}.tile.cloudmade.com/9950b9eba41d491090533c541f170f3e/997#2x/256/{z}/{x}/{y}.png', {
maxZoom: 17
}).addTo(map);
// If user has location then place marker on map
if (userLatitude && userLongitude) {
var marker = L.marker([userLatitude, userLongitude]).addTo(map);
}
var playersList = players.find().fetch();
playersList.forEach(function(players) {
// Change position of all markers
var marker = L.marker([players.latitude, players.longitude], options={"id" : 666}).addTo(map);
});
};
// If the collection of players changes (location or amount of players)
Meteor.autorun(function() {
var playersList = players.find().fetch();
playersList.forEach(function(players) {
// Change position of all markers
var marker = L.marker([players.latitude, players.longitude]).addTo(map);
});
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
/*
Template.hello.events({
'click input' : function () {
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});
*/
/*
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
userLatitude = 52.35873;
userLongitude = 4.908228;
players.insert({
name: "Martijn",
latitude: userLatitude,
longitude: userLongitude
});
});
}
*/
You need to clear the existing markers, otherwise they remain on the map. The easiest / most efficient way to do this is to attach the markers to a LayerGroup when you're creating them. Then, when you want to update, clear all the markers, and then add them again.
Add layer group declaration at the top, so you have
var map, markers;
After initialising the map,
markers = new L.LayerGroup().addTo(map);
Change this line:
var marker = L.marker([userLatitude, userLongitude]).addTo(map);
to:
var marker = L.marker([userLatitude, userLongitude]).addTo(markers);
in your autorun, before the forEach,
markers.clearLayers();
then in your foreach,
var marker = L.marker([players.latitude, players.longitude]).addTo(markers);
I'm developing a web page with a Google maps application. Currently, I have a functional search bar and map that displays three KML/KMZ layers. I need to be able to toggle between each of the layers, either display one of them, two of them or all three. There is a similar function in Google Earth, but I need it in Google Maps. How can I do this?
Here is my code for the map and search bar:
<script type="text/javascript">
var geocoder;
var map;
var marker;
function initialize() {
geocoder = new google.maps.Geocoder ();
var latlng = new google.maps.LatLng (40.43, -74.00);
var myOptions = {
zoom: 5,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(document.getElementById("map_canvas"),
myOptions);
marker = new google.maps.Marker({map:map});
var ctaLayer = new google.maps.KmlLayer('http://dl.dropbox.com/u/80233620/NY_Radar_data.kmz');
ctaLayer.setMap(map);
var ctaLayer = new google.maps.KmlLayer('http://www.nyc.gov/html/dot/downloads/misc/cityracks.kml');
ctaLayer.setMap(map);
var ctaLayer = new google.maps.KmlLayer('http://dl.dropbox.com/u/80233620/OKX_Radar_data%20(1).kmz');
ctaLayer.setMap(map);
}
function codeAddress () {
var address = document.getElementById ("address").value;
geocoder.geocode ( { 'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.setCenter(results [0].geometry.location);
marker.setPosition(results [0].geometry.location);
map.setZoom(14);
}
else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
</script>
It's simply setMap(null) to hide one, setMap(map) to show. I keep a global array variable layers, to keep track of which layer to toggle:
var layers = [];
layers[0] = new google.maps.KmlLayer('http://dl.dropbox.com/u/80233620/NY_Radar_data.kmz',
{preserveViewport: true});
layers[1] = new google.maps.KmlLayer('http://www.nyc.gov/html/dot/downloads/misc/cityracks.kml',
{preserveViewport: true});
layers[2] = new google.maps.KmlLayer('http://dl.dropbox.com/u/80233620/OKX_Radar_data%20(1).kmz',
{preserveViewport: true});
The preserveViewport option stops the map from jumping around when the layers are toggled.
Here's the function to toggle:
function toggleLayer(i) {
if(layers[i].getMap() === null) {
layers[i].setMap(map);
}
else {
layers[i].setMap(null);
}
}
Note it's using the global variable. Finally the HTML, you can use checkboxes or buttons, and even a radio button by setting only one active layer at first and enabling the right one when the radio set is updated.
Large weather <input type="checkbox" id="layer0" onclick="toggleLayer(0)" checked>
<input type="button" id="layer1" onclick="toggleLayer(1)" value="Racks">
Small weather <input type="checkbox" id="layer2" onclick="toggleLayer(2)" checked>
The whole demo is here, controls on top left of map: http://jsbin.com/irahef/edit#preview
Heiter's answer is good but a little addition to the code in the jsbin example, if you want to have the layers be undisplayed on initialization is to change
layers[i].setMap(map);
to
layers[i].setMap(null);
and then make sure your checkboxes are unchecked.
I tried the code posted above by Heitor, and noticed that clicking the layers on and off changes the order that they are displayed on the map. I implemented this solution to preserve the order of the layers, but it might be somewhat inefficient. If anyone has any suggestions please share.
function toggleLayer(i) {
var j;
for (j = 0; j < layers.length ; j++ )
{
if (j != i)
{
if (layers[j].getMap() === null)
{
layers[j].setMap(null);
} else {
layers[j].setMap(map);
}
} else { //toggle the selected layer
if (layers[j].getMap() === null)
{
layers[j].setMap(map);
} else {
layers[j].setMap(null);
}
}
}
}