I'm using the latest version of MarkerClusterer with Google Maps API v3 and I think I've found a bug!
My google map minZoom is set to 1. Zooming from level 1 down to any level and back up to 1 is just fine. The bug is seen when I try to zoom out from level 1 to level 0.
When I click to zoom out from level 1 to level 0, the gMap UI doesn't allow the zoom, as intended, but all my markerClusters disappear, they re-appear when I go down to zoom level 2 and come back to level 1.
I've posted this on the Google Group page for Google Maps API v3, but no response so far (been over a week as of today).
Any help is much appreciated!
It's more a bug in the Maps-API than a bug in markerClusterer, but you can fix it in markerClusterer.js
I'm not sure where you click on when you(try to) set the zoom to 0(the issue for me doesn't occur when I use the zoom-control), but it happens when I set the zoom using map.setZoom(0)
The issue: the API reports a zoom of 0, but this is incorrect, because the zoom will be set to 1(the minZoom).
Fix:
Replace this part of marcerclusterer.js:
// Add the map event listeners
var that = this;
google.maps.event.addListener(this.map_, 'zoom_changed', function() {
var zoom = that.map_.getZoom();
if (that.prevZoom_ != zoom) {
that.prevZoom_ = zoom;
that.resetViewport();
}
});
...with that:
// Add the map event listeners
var that = this;
google.maps.event.addListener(this.map_, 'zoom_changed', function() {
var zoom = that.map_.getZoom(),
minZoom=that.map_.minZoom||0,
maxZoom=Math.min(that.map_.maxZoom||100,
that.map_.mapTypes[that.map_.getMapTypeId()].maxZoom);
zoom=Math.min(Math.max(zoom,minZoom),maxZoom);
if (that.prevZoom_ != zoom) {
that.prevZoom_ = zoom;
that.resetViewport();
}
});
Related
I'm currently building a Phonegap app along with Google's Map JS API.
I have a map that you can drag to select your dropoff location. This is accomplished by collecting the map's center geolocation on the "dragend" event. I then set a pin on top of the map (outside of the JS map) so that you know where the center point is.
Everything works fine until you pinch to zoom and the pin is then displaced. This is due to you zooming and dragging the map at the same time when pinching the screen.
Is there any way to prevent dragging on zoom in order to hold the map at its center point?
Please view the JSfiddle here.
var map;
google.maps.event.addDomListener(window, 'load', onMapReady);
function onMapReady() {
var center = new google.maps.LatLng(30.267153, -97.743061);
var mapOptions = {
center: center,
zoom: 13,
styles: [{"featureType": "poi", "stylers": [{ "visibility": "off" }]},{"featureType": "transit","stylers": [{ "visibility": "off" }]}],
disableDefaultUI: true,
};
map = new google.maps.Map(document.getElementById('map'), mapOptions);
google.maps.event.addListener(map, 'dragend', function() {
center = map.getCenter();
$('#log').html('Lat: ' + center.lat() + ' Lng: ' + center.lng());
});
}
There are two possible solutions for this issue , from what i have read that you have tried using native marker but it is lagging , which itself is another issue that i have a suggestion for :
use crosswalk for building your app , this will extremely boost your app performance , i have experienced it myself , and was really satisified with results ( iam using intel xdk but i believe this will work for phonegap as well ).
use the UX solution : you feel the marker is lagging because after all it is visible !! i would suggest hiding the marker on drag event start , then showin it back on drag event end.
Third solution which makes sense , is using a pinch detector library like this , this , or even solutions mentioned here , and here , pick whatever works best for you , as performance is a point of concern , and previous solution have to be tried , however once you have detected the pinch gesture , you set the map drag to false , set it back again to true after pinch ends .
I dont usually provide much coding in my solution but rather the correct algorithm to help solving the specified issue.
EDIT: ProllyGeek is right, the zoom_changed event fires after the drag has already happened. You can detect if the touch event was near the center of the map (over your marker) and re-center the map after zooming:
google.maps.event.addListener(map, 'dragend', function() {
if (center && center != map.getCenter()) {
map.setCenter(center);
center = null;
}
});
//we should recenter the map if the click/mousedown was within the centerRadius of the center of the map
google.maps.event.addListener(map, 'mousedown', function(clickMouseEvent) {
var mapDiv = document.getElementById('map');
if (pointInCircle(clickMouseEvent.pixel.x,
clickMouseEvent.pixel.y,
mapDiv.offsetWidth/2,
mapDiv.offsetHeight/2,
centerRadius)) {
//map.setOptions({draggable: false}); //disables zoom and dragging
center = map.getCenter(); //save the current center point so we can recenter later.
}
});
//handy function to see if a x,y coordinate is within z radius of another x,y coordinate
//from https://stackoverflow.com/a/16819053/1861459
function pointInCircle(x, y, cx, cy, radius) {
var distancesquared = (x - cx) * (x - cx) + (y - cy) * (y - cy);
return distancesquared <= radius * radius;
}
http://jsfiddle.net/bxfn499f/11/
Original Answer:
Did you try setting draggable to false on the map when the zoom event is fired?
google.maps.event.addListener(map, 'zoom_changed', function() {
map.setOptions({draggable: false});
//heavy handed re-enabling of draggable
//setTimeout(function() { map.setOptions({draggable: true}); }, 5000); //cant drag for five seconds
});
You can programmatically re-enable dragging with the mouseup event (which should fire in lieu of the touchend event) or whatever makes sense in your use case (map.setOptions({draggable: true});). For example:
google.maps.event.addListener(map, 'mouseup', function() {
map.setOptions({draggable: true});
});
http://jsfiddle.net/bxfn499f/6/ I tested from a desktop, so I tweaked the fiddle slightly as the map wasn't loading for me - assuming this was due to the window.load not being fired after $(document).ready(function() { ... }. You'll have to see how this behaves if the drag stars before the zoom event.
I have a solution which might work. Assuming you want the map to center back to the pin.
Add custom control div in your map object. The example is here. Instead of the "center map" div in the example, make your pin the controlling div. Listen to dragend event and set the center at pin's position.
Haven't really tested the solution but seems like it will do the trick.
Using Leaflet 0.7.3 and last version of markercluster (https://github.com/Leaflet/Leaflet.markercluster), I would like to reproduce a behavior I have been able to do using Google API and associated libraries (Google Marker Cluster V3 & Overlapping Marker Spiderfier V3).
When you have a map with your clusters and you click on one cluster, the API will zoom to bound the markers contained in this cluster. But when all your markers are exactly on the same position, the zoom will be set to its maximum value which is often ugly (no context around the markers or just one street). I would like to limit the zoom level when user click on one cluster with a code like this :
markers.on('clusterclick', function (a) {
map.fitBounds(a.layer.getBounds());
if (map.getZoom() > 14) {
map.setZoom(14);
}
});
With such code the zoom is correctly limited to 14 but the spiderfy is not done (even if I explicitly call a.layer.spiderfy(). Is there something I am missing ?
Here is the fiddle : http://jsfiddle.net/953u41ax/ (click for exemple on 36 then on 12)
Eric, you can achieve this by preventing zoom on click and then manually calling spiderfy on the layer.
Map Settings:
var stationsCluster = new L.MarkerClusterGroup({
maxClusterRadius: 60,
iconCreateFunction: null,
spiderfyOnMaxZoom: true,
showCoverageOnHover: true,
zoomToBoundsOnClick: false
Spidery Listener:
stationsCluster.on('clusterclick', function (a) {
a.layer.spiderfy();
});
You can set the maximum zoom level on the map:
map._layersMaxZoom=8
and then disable the clustering at the same zoom level:
var markers = L.markerClusterGroup({
disableClusteringAtZoom: 8
});
You can play around with this to get the desired effect, this is just an example. JSFiddle is here http://jsfiddle.net/953u41ax/2/
I'm making a project for the school and I need to resize the marker icons depending on zoom level in a leaflet map, Is there an easy way to accomplish this? Any tutorial on the web? Thanks in advance for the help!!!
In order to change the size of the markers when you zoom in/out, you'll need to handle the event.
map.on('zoomend', function() { });
The zoomend event will be called whenever the map has finished zooming in or out. See the API here.
Now, inside this function, you can call your custom code in order to change the size of the markers. For example, let's say you wanted to take a simple approach and set the size of a circle marker equal to the size of the maps zoom level. See the API for a CircleMarker here
// Create some marker that will be resized on the map zooming
var myMarker = new L.CircleMarker([10,10], { /* Options */ });
map.on('zoomend', function() {
var currentZoom = map.getZoom();
myMarker.setRadius(currentZoom);
});
Now whenever the map zooms in or out, the size of the marker will change.
I'm not sure what Stophace is referring to regarding circleMarkers not changing size, but, adding to the approved answer... if you want to resize circleMakers or change any other styling options (I find it helpful to change the weight along with radius), you can use the following approach:
map.on('zoomend', function() {
var currentZoom = map.getZoom();
var myRadius = currentZoom*(1/2); //or whatever ratio you prefer
var myWeight = currentZoom*(1/5); //or whatever ratio you prefer
layername.setStyle({radius: myRadius, weight: setWeight});
});
layername will be replaced with the name of whatever layer you have which contains circleMarkers... and of course you can change the fractions to your liking to suit your needs.
I'm guessing the OP's school project is finished, but I hope this helps others who have the same question!
The Google Maps API for a Polygon does not offer a drag method.
What would be an efficient way of implementing such a feature (i.e., sufficiently optimised so that it would not kill a four year old laptop)?
Thank you!
I found the Google Maps V2 Polygon Implementation to be very limiting for the needs I have had and solved it by creating a custom overlay. My group is currently stuck on IE6 so I have yet to migrate over to Google Maps V3 - but taking a quick look at the API shows that you could probably do a similar thing that I did in V2 with V3.
Essentially the idea is:
Create a Custom Overlay
Populate it with your own SVG/VML Polygons and attach a drag event to this custom polygon object
Custom Overlays:
Here is some information to get you started on making your own custom overlay:
http://code.google.com/apis/maps/documentation/javascript/overlays.html#CustomOverlays
Creating your own "Dragable" Polygon Object:
Once you get that down you'll want to add your own polygons to the custom overlay instead of using GPolygons. I went through the painful process of learning SVG/VML and writing a library to bridge SVG/VML together - you could do that, but I would recommend starting by trying to use another library such as Raphaël.
http://raphaeljs.com/
Using Raphaël will save you a whole lot of time trying to figure out how to get cross-browser Vector Graphic (Polygon) functionality - and best of all it supports drag events already, here is an example from their library:
http://raphaeljs.com/graffle.html
Once you have a custom overlay and you are able to throw some Raphaël objects onto it the last step is to translate the coordinates you want from a Lat/Lng value to a Pixel value. This is available in the MapCanvasProjection of V3:
http://code.google.com/apis/maps/documentation/javascript/reference.html#MapCanvasProjection
You can use fromLatLngToDivPixel to figure out what the actual pixel values are for the points on your Raphael polygon, draw it, then add it to the overlay with a drag event.
Since version 3.11 (dated Jan 22, 2013) it's possible to just set the draggable property onto the google.maps.Polygon instance; see this example.
If you want to programmatically move a polygon, you'll need a custom Google Maps Extension which I wrote, as the API does not provide such a method.
Here's how I do it. Find the approximate center of the polygon, and add a marker, then add a drag listener to the marker. On lat/lng change, subtract the difference from the original marker lat/lng, subtract the difference to each of the paths, then, set the original position to the new position. Make sure that in your javascript api call that you have library=geometry,drawing
google.maps.event.addListener(draw, 'overlaycomplete', function(shape) {
// POLYGON
if (shape.type == 'polygon') {
var bounds = new google.maps.LatLngBounds(); var i;
var path = shape.overlay.getPath();
for (i = 0; i < path.length; i++) { bounds.extend(path.getAt(i)); }
shape.latLng = bounds.getCenter();
marker = getMarker(map,shape);
shape.overlay.marker = marker;
markers.push(marker);
}
google.maps.event.addListener(marker, 'drag', function(event) {
shape.overlay.move(event.latLng, shape, path);
});
google.maps.event.addListener(shape.overlay, 'rightclick', function() {
this.setMap(null);
this.marker.setMap(null);
draw.setDrawingMode('polygon');
});
});
}
google.maps.Polygon.prototype.move = function(latLng, shape, p) {
var lat = latLng.lat();
var lng = latLng.lng();
latDiff = shape.latLng.lat()-lat;
lngDiff = shape.latLng.lng()-lng;
for (i = 0; i < p.length; i++) {
pLat = p.getAt(i).lat();
pLng = p.getAt(i).lng();
p.setAt(i,new google.maps.LatLng(pLat-latDiff,pLng-lngDiff));
}
shape.latLng = latLng;
}
function getMarker(map,shape){
var infowindow = new google.maps.InfoWindow();
if(shape.type=='polygon'){ latLng = shape.latLng; }
marker = new google.maps.Marker({
position: latLng,
map:map,
draggable:true,
clickable: true,
animation: google.maps.Animation.DROP
});
shape.overlay.marker = marker;
shape.overlay.bindTo('center',marker,'position');
google.maps.event.addListener(marker, 'click', (function(marker) {
return function() {
infowindow.setContent('polygon');
infowindow.open(map, marker);
toggleBounce(marker);
}
})(marker));
google.maps.event.addListener(infowindow,'closeclick', (function(marker) {
return function() {
marker.setAnimation(null);
}
})(marker));
return marker;
}
If you have any questions, feel free to contact me.
You could have markers for each point on the polygon, these markers could have drag and at the end of each drag, the polygon could be redrawn.
You could also have a marker in the center of the polygon representing the polygon as a whole, when you move that marker, every marker could be moved by the same amount to maintain the shape.
Okay - so after seeing the website you are trying to implement I started to feel like Raphael may not be necessary because it is a pretty heavy JS Library - and if you are only trying to draw a rectangle polygon I thought, why not just do it with a single lightweight DIV instead?
However I think the Raphael solution would still hold water for many other cases - so I think I'll just post another possible answer.
Here is a working example I threw together:
http://www.johnmick.net/drag-div-v3/
Feel free to take a look at the source:
http://www.johnmick.net/drag-div-v3/js/main.js
Essentially we do the following
Create the Custom Overlay
Create the draggable div polygon and, using jQuery UI, make it draggable
Tie an event that listens to when the dragging has stopped that updates the LatLng position of the rectangle
Add the object to the Custom Overlay
Implement the draw function to redraw the rectangle during zooms and pans
Currently I am only storing one LatLng value for the Rectangle (being the top left corner) - you could easily extend this example to store all 4 points of the rectangle and have the shape dynamically resize itself on zooms. You may want to do that, otherwise as users zoom out they will get a climate report for a larger and larger area.
I have a webpage that finds a store by postcode or name.
I have just released an update to it so that contact details display in an info window coming from the marker. Due to the small size of the info window, after centering to the marker, the map pans down until it can fit the marker and info window in leaving the marker near the bottom.
Wondering if there is an easy way to set this offset immediately so that the marker appears at the bottom of the map window and it doesn't have to pan?
Thanks.
You could center the map appropriately before you add the marker:
var someZoom = 13;
var center = new GLatLng(37.4419, -122.1419);
map.setCenter(center, someZoom);
The zoom is optional too. You can just leave the zoom on whatever it is:
map.setCenter(center);
If you would like to center on a particular pixel, instead of a lat/lng, then you can use this function to convert:
fromContainerPixelToLatLng(pixel:GPoint)
I feel like you should spend half an hour and review the docs: http://code.google.com/apis/maps/documentation/reference.html. I read the documentation extensively while working on my website: www.trailbehind.com
Perhaps the auto-panning is caused by an internal addoverlay event handler. Have you tried handling the addoverlay event and returning false from it?
GEvent.addListener(map, "addoverlay", function() {
return false;
});
where 'map' is the name of your GMap2 object.