I'm wondering how I get a smooth zoom in animation with the Google Maps API.
I have 2 points, one in, let say China, and one in France. When I'm zoomed in on China, and click the button France. I want it to gradually zoom out smooth, one zoom level at the time. When it's zoomed out it should pan to the new location, and then zoom in on the new location one zoom level at the time.
How can I do this?
You need the zoomOut method with the continuous zoom parameter set to do the zoom and the panTo method to do the smooth panning to the new centerpoint.
You can listen to the zoomEnd and moveEnd events on the map object to chain together your zoomOut, panTo and zoomIn methods.
EDIT:
So in the course of implementing a sample for this problem, I discovered that the doContinuousZoom param on ZoomIn and ZoomOut (or just EnableContinuousZoom on the map) doesn't quite work as expected. It works ok when zooming out, if the tiles are in the cache (this is an important point, if the tiles aren't cached then it is not really possible to get the smooth animation you are after) then it does some nice scaling on the tiles to simulate a smooth zoom animation and introduces a ~500 ms delay on each zoom step so you can do it asynchronously (unlike panTo, which you will see in my example I use a setTimeout to call async).
Unfortunately the same is not true for the zoomIn method, which just jumps to the target zoom level without the scaling animation for each zoom level. I haven't tried explicitly setting the version for the google maps code, so this might be something that is fixed in later versions. Anyway, here is the sample code which is mostly just javascript hoop jumping and not so much with the Google Maps API:
http://www.cannonade.net/geo.php?test=geo2
Because this approach seems a bit unreliable, I think it would make more sense to do the async processing for setZoom explicitly (Same as the panning stuff).
EDIT2:
So I do the async zooming explicitly now (using setTimeout with a single zoom at a time). I also have to fire events when each zoom happens so that my events chain correctly. It seems like the zoomEnd and panEnd events are being called synchronously.
Setting enableContinuousZoom on the map doesn't seem to work, so I guess calling zoomOut, zoomIn with the param is the only way to get that to work.
Here's my approach.
var point = markers[id].getPosition(); // Get marker position
map.setZoom(9); // Back to default zoom
map.panTo(point); // Pan map to that position
setTimeout("map.setZoom(14)",1000); // Zoom in after 1 sec
var zoomFluid, zoomCoords; //shared variables
function plotMarker(pos, name){
var marker = new google.maps.Marker({
map: map,
title:name,
animation: google.maps.Animation.DROP,
position: pos
});
google.maps.event.addListener(marker, 'click', function(){
zoomCoords = marker.getPosition(); //Updates shared position var
zoomFluid = map.getZoom(); //Updates shared zoom var;
map.panTo(zoomCoords); //initial pan
zoomTo(); //recursive call
});
}
// increases zoomFluid value at 1/2 second intervals
function zoomTo(){
//console.log(zoomFluid);
if(zoomFluid==10) return 0;
else {
zoomFluid ++;
map.setZoom(zoomFluid);
setTimeout("zoomTo()", 500);
}
}
For the zoom this one worked for me nicely:
function animateMapZoomTo(map, targetZoom) {
var currentZoom = arguments[2] || map.getZoom();
if (currentZoom != targetZoom) {
google.maps.event.addListenerOnce(map, 'zoom_changed', function (event) {
animateMapZoomTo(map, targetZoom, currentZoom + (targetZoom > currentZoom ? 1 : -1));
});
setTimeout(function(){ map.setZoom(currentZoom) }, 80);
}
}
Related
Problem
My segment works like this. When I drag the map around (Google Maps Javascript API), I want a circle rendered at map centre to disappear. When the dragging is done, I want the circle to move to the new centre BEFORE becoming visible again.
What ends up happening is that the circle becomes visible and then jerks over to the new position, even though I have them one before the other (set position, then change the visibility.)
Attempt
Here is the code that I was using for this segment:
control.getCircle().setCenter(latLng(latitude, longitude));
control.getCircle().setVisible(true);
Where latLng is a wrapper function I made to get LatLng object and control is the control object from angular-google-maps.
Even though setCenter is first, the circle still becomes visible before it is moved. Is there anything I can do about this? I have tried to wrap it in a promise, callbacks, etc. but nothing seems to do the trick.
Thanks!
Try with listeners on event center_changed , event idle or event dragstart
google.maps.event.addListener(map, 'center_changed', function() {
control.getCircle().setCenter(latLng(latitude, longitude));
});
google.maps.event.addListener(map, 'dragstart', function() {
control.getCircle().setVisible(false);
});
google.maps.event.addListener(map, 'idle', function() {
control.getCircle().setVisible(true);
});
I am trying to set up OpenLayers to not display the vector layer just before a zoom starts and make it reappear after a zoom ends. I have the zoom ends part already established like this:
map = new OpenLayers.Map('map_element', { eventListeners: { "zoomend": mapEvent}});
function mapEvent(event) {
if(event.type == "zoomend") {
hide_vector_layer();
}
}
But I don't see any kind of event listener for the start of a zoom in the documentation. There is a "movestart" which covers moving, panning, and zoom. Unfortunately, I can't use the "movestart" one, because I don't want the layer to disappear during a pan. You would think there would be a "zoomstart", as there is a "zoomend".
The reason I am trying to do this, is because I don't like how the vector layer zooms at a different rate when using Google Maps as a base layer. It looks wrong, looks like all the features are inaccurate, even though they land in the right place after the zoom is complete.
Any suggestions?
Here is a easy to add the 'BeforeZoom' event to the OpenLayers . Just add the code below to where you created your map object.
map.zoomToProxy = map.zoomTo;
map.zoomTo = function (zoom,xy){
//Your Before Zoom Actions
//If you want zoom to go through call
map.zoomToProxy(zoom,xy);
//else do nothing and map wont zoom
};
How this works:
For any kind of zooming activity, OpenLayers API ultimately calls the function called zoomTo. So before overriding it, we copy that function to a new function called 'zoomToProxy'. The we override it and add our conditional zoom logic. If we want the zoom to happen we just call new proxy function :)
For this purpose you should override moveTo and moveByPx methods of OpenLayers.Map for eliminate movestart event triggering for any actions except zooming.
I had the same problem that OP had, and I tried to solve it with drnextgis's solution. But unfortunately it didn't completely work:: the zoomChanged property in OpenLayers.Map.moveTo evaluates to true not only when the zoom level has changed, but also when the map has been resized.
My map was 100% of the user's browser window, so if they resized the window, the event would be triggered. This was undesirable for me, as I only wanted to trigger the event if the zoom level had actually changed. My solution was to create an new event, called "zoomstart", which I inserted at the top of OpenLayers.Map.moveTo. Here's the code:
var getZoom = this.getZoom();
if ( !!getZoom && !!zoom && this.isValidZoomLevel(zoom) && getZoom != zoom )
this.events.triggerEvent("zoomstart", zoom);
This code will pass the new zoom level to an event listener that is registered to zoomstart, and in my case I determine the map's restrictedExtent and do other stuff based upon the new zoom level.
Peace be with ye.
"movestart" handles "zoomstart". To detect if the zoomstart, try:
map.events.register("movestart",map, function(e) {
if(e.zoomChanged)
{
//zoom start code here
}
});
Solution of "Shaunak" is worked very well for me.
I want to restrict zooming below 11 so edited his code as
if (zoom > 11) {
map.zoomToProxy(zoom, xy);
}
I am trying to get my custom markers to show up on my map after i have used the fitBounds() method to fit the boundaries of the map to the markers themselves.
I have done this by looping over the markers array and then extending the boundaries of the map to incorporate the marker co-ordinates.
This works fine with the stock google maps markers. However, my client wants to use quite large (36px by 57px) marker images for their site. How do i compensate for this when fitting the boundaries of the map?
At the moment when using the custom marker images they do not all fit inside the boundaries set.
Since you already have calculated the bounds, you may just need to extend the bounds to add enough buffer area to include the large images. The formula you can use to calculate or extend a bounds this way is called a convex hull; the Computational Geometry Algorithms Library has a section on 2D Convex Hull Algorithms or there is a JavaScript Quickhull Article that also includes a nifty online example near the bottom of the page. Hope this is helpful -
The cheap answer is to always zoom out one level after fitBounds(). But we can do a bit better.
I like writing hacks. Here I am making the assumption that the size of your marker will never be larger than 36x57. I tested a while back to find that fitBounds() leaves a margin of around 42 px between the edge and the closest marker (maybe not on mobiles), and I'm also assuming you are not repositioning the marker, that is, it will always be displayed above the given coordinate position. If icons run off to the other sides, adjustments are needed.
My hack takes advantage of a function that measures the pixel position of a LatLng (using the container version, I read here that the div version is not reliable with bounds changes).
Since we know the height of the icon, and where the topmost marker is, we can pan the map south a bit if it's determined to be offscreen. In case there's not enough margin below, the only option is to zoom out. My only concern is it will be jerky because it calls for two events: fitBounds and the custom panning/zooming. The only answer then would be to rewrite a custom fitBounds. When I tested manually the events ran smoothly.
http://jsfiddle.net/sZJjY/
Click to add cat icons, right-click to trigger the resize/panning.
Example: place 3-4 kitties, right-click, then purposely place another that goes off the top, right-click again.
function fitIcons() {
var left = 180.0;
var right = -180.0;
var top = -90.0;
var bottom = 90.0;
for (var i = 0; i < markers.length; i++) {
curlat = markers[i].getPosition().lat();
curlng = markers[i].getPosition().lng();
if(curlat > top) { top = curlat; }
if(curlat < bottom) { bottom = curlat; }
if(curlng > right) { right = curlng; }
if(curlng < left) { left = curlng; }
}
var overlay = new google.maps.OverlayView();
overlay.draw = function() {};
overlay.setMap(map);
map.fitBounds(new google.maps.LatLngBounds(
new google.maps.LatLng(bottom, left),
new google.maps.LatLng(top, right)));
topPixels = overlay.getProjection().fromLatLngToContainerPixel(
new google.maps.LatLng(top, right));
bottomPixels = overlay.getProjection().fromLatLngToContainerPixel(
new google.maps.LatLng(bottom, left));
topGap = topPixels.y;
bottomGap = $("#map_canvas").height() - bottomPixels.y;
if(topGap < iconHeight) {
if(bottomGap > iconHeight) {
map.panBy(0, topGap);
}
else {
map.setZoom(map.getZoom() - 1);
}
}
}
I'm using Polymaps.js library for making maps.
It would be very useful to be able to observe zoom level changes on layers. I'm trying to keep a layer of points scaled by values in meters, so I need to recalculate the radii whenever the zoom level changes.
I'm using the on "move" function, which I think is triggered on zooms, too. There may be on on "zoom" function, too. I think that there is.
For example:
// whenever the map moves...
map.on("move", function() {
// get the current zoom
var z = map.zoom();
// show/hide parcels
parcels.visible(z >= 16);
});
Is it possible to add an image overlay to a google map that scales as the user zooms?
My current code works like this:
var map = new GMap2(document.getElementById("gMap"));
var customIcon = new GIcon();
customIcon.iconSize = new GSize(100, 100);
customIcon.image = "/images/image.png";
map.addOverlay(new GMarker(new GLatLng(50, 50), { icon:customIcon }));
However, this adds an overlay that maintains the same size as the user zooms in and out (it is acts as a UI element like the sidebar zoom control).
There is a zoomend event, fired when the map reaches a new zoom level. The event handler receives the previous and the new zoom level as arguments.
http://code.google.com/apis/maps/documentation/reference.html#Events_GMap
You might want to check out openlayers
It's a very capable Javascript API - it supports a bunch of back ends, allowing you to transparently switch between, say, Google Map tiles and Yahoo Map tiles.
Well after messing around trying to scale it myself for a little bit I found a helper called EInserts which I'm going to check out.
Addition:
Okay EInserts is about the coolest thing ever.
It even has a method to allow you to drag the image and place it in development mode for easy lining up.