Search code examples
javascriptgoogle-mapsgeolocationgoogle-maps-markers

Google Maps Api - when is map.panto(latlng) smooth


EDITED: I'm trying to work out when Google Maps API map.panTo(Lat, Lng) decides the trip is too far (pixels) for a "smooth" pan.

This is what the manual has to say: -

Changes the center of the map to the given LatLng. If the change is less than both the width and height of the map, the transition will be smoothly animated.

I've established that if there is only x or y vertex movement (only either Lat or Lng value changes but not both) then the check is a simple two-thirds .6666 of the map's viewport width or height. But if both Lat and Lng values change then I'm not sure of the formula.

An example of what we know: -

If we travel from Perth to somewhere up near Yeppoon: -

Perth: Lat: -31.9523 Lng: 115.8613 xPixel: 13465 yPixel: 9728 Yeppoon: Lat: -22.9523 Lng: 150.2093 xPixel 15028, yPixel: 9265

X/vertical movement: 15028 - 13465 = 1563 Y/horizontal movement: 9265 - 9728 = -463

Then, for that same trip, the following viewport sizes yield smooth pans; 1 pixel width or height less forces a hard pan: -

Viewport

Width: 1337 1435 1236 Height: 492 448 574

What is the formula for viewport pan borderline?

It should be obvious but I just can't see it

The only other information I have is: -

Google Title size at zero zoom = 256 The zoom I'm using is 6 = multiplier 64

X Pixel Formula = 256 * (0.5 + Longitude / 360) * 64

let siny = Math.sin((Latitude * Math.PI) / 180); // Truncating to 0.9999 effectively limits latitude to 89.189. This is // about a third of a tile past the edge of the world tile. siny = Math.min(Math.max(siny, -0.9999), 0.9999);

Y Pixel Formula = 256 * (0.5 - Math.log((1 + siny) / (1 - siny)) / (4 * Math.PI))

Make any sense?

EDITEND

Please copy https://richardmaher.github.io/Brotkrumen/testmap.html if it makes life easier (has to be run locally due to API key)

See console for debugging info.

Shrink browser to Width 615px and you'll smooth scrolling/panning stop.

Ready to answer any other questions

See also https://issuetracker.google.com/issues/228349799

Can someone please explain in pseudocode, or at least less ambiguous language, the API Refence Manual definition for smooth transition requirements of the panTo() method: -

panTo panTo(latLng) Parameters: latLng: LatLng|LatLngLiteral The new center latitude/longitude of the map. Return Value: None Changes the center of the map to the given LatLng. If the change is less than both the width and height of the map, the transition will be smoothly animated.

Specifically, what is "the change" in this context?

Example: -

Zoom Level = 6
LatLng1 = lat: -31.9523, lng: 115.8613 Pixel X = 13464 Pixel Y = 9728
LatLng2 = lat: -33.8688, lng: 151.2093 Pixel X = 15073 Pixel Y = 9831
Current Map Center is LatLng1 and panning to LatLng2
I make the "change" to be horizontal 1609px and vertical 103px

If the map's DIV container is at least 616px wide and 344px high the pan is smooth if not it jumps.

Can someone please help me heuristicly marry up those figures with an algoithm?


Solution

  • Ok I think the answer to the specific question is just down to rounding but I don't need to chase that one as, in the last month or so, Google has decouple its rehoming of the markers from the smooth pan test. It is now an arbitrary 100000px limit before it stops maintaing the DOM for markers.

    The complete story can be found here (remember due to the API key hard-coding you need to copy testmap.html to your local file system before trying to run it.)

    The TL;DR version and core logic is in this function: -

        function makeDestCenter(){
            console.log("Panning to new Center " + map.getZoom());
            var home = map.getCenter();
            var zoom = map.getZoom();
            var scale = 1 << zoom;  
            var proj = map.getProjection();
            
            var homePoint =  proj.fromLatLngToPoint(home);  
            var startPixelX = Math.round(homePoint.x * scale);
            var startPixelY = Math.round(homePoint.y * scale);
    
            var destPoint =  proj.fromLatLngToPoint(dest[destIndex]);
            var destPixelX = Math.round(destPoint.x * scale);
            var destPixelY = Math.round(destPoint.y * scale);
            var xTrip = Math.abs(destPixelX - startPixelX);
            var yTrip = Math.abs(destPixelY - startPixelY);
    
            console.log("sX " + startPixelX + " dX " + destPixelX + " sY " + startPixelY + " dY " + destPixelY);
    
            if ((xTrip > MAX_TRIP) || (yTrip > MAX_TRIP)) {
                google.maps.event.addListenerOnce(map, 'idle', makeDestCenter);
                map.setZoom(--zoom);
            } else {
                if (xTrip == TOO_SMALL && yTrip == TOO_SMALL) {
                    google.maps.event.addListenerOnce(map, 'idle', makeDestCenter);
                    map.setZoom(++zoom);
                } else {
                    map.panTo(dest[destIndex]);
                }
            }
        }