Skip to content

Zoom and planet size relation on globe

Explanation of zoom and planet size relation under globe projection and how to account for it when changing the map center and zoom by some delta.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Zoom and planet size relation on globe</title>
    <meta property="og:description" content="Explanation of zoom and planet size relation under globe projection and how to account for it when changing the map center and zoom by some delta." />
    <meta charset='utf-8'>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel='stylesheet' href='https://unpkg.com/maplibre-gl@5.4.0/dist/maplibre-gl.css' />
    <script src='https://unpkg.com/maplibre-gl@5.4.0/dist/maplibre-gl.js'></script>
    <style>
        body { margin: 0; padding: 0; }
        html, body, #map { height: 99%; }
    </style>
</head>
<body>
<style>
    #fly {
        display: block;
        position: absolute;
        top: 20px;
        left: 50%;
        transform: translate(-50%);
        width: 50%;
        height: 40px;
        padding: 10px;
        border: none;
        border-radius: 3px;
        font-size: 12px;
        text-align: center;
        color: #fff;
        background: #ee8a65;
    }
</style>
<div id="map"></div>
<br />
<button id="fly">Go to pole or equator</button>
<script>
    const map = new maplibregl.Map({
        container: 'map',
        style: 'https://demotiles.maplibre.org/style.json',
        center: [0, 0],
        zoom: 2,
    });

    map.on('style.load', () => {
        map.setProjection({
            type: 'globe', // Set projection to globe
        });
    });

    // To stay consistent with web mercator maps, globe is automatically enlarged when map center is nearing the poles.
    // This keeps the map center visually similar to a mercator map with the same x,y and zoom.
    // However, sometimes we want to negate this effect and keep the globe size consistent even when changing latitudes.
    // This function computes what we need to add the the target zoom level when changing latitude.
    function getZoomAdjustment(oldLatitude, newLatitude) {
        return Math.log2(Math.cos(newLatitude / 180 * Math.PI) / Math.cos(oldLatitude / 180 * Math.PI));
    }

    // Switch back and forth between zooming in and out.
    let zoomIn = false;
    const zoomDelta = 1.5;

    document.getElementById('fly').addEventListener('click', () => {
        // Fly to a random location by offsetting the point -74.50, 40
        // by up to 5 degrees.
        const center = [
            map.getCenter().lng,
            zoomIn ? 0 : 80,
        ];
        const mapZoom = map.getZoom();
        const delta = (zoomIn ? zoomDelta : -zoomDelta);
        // We want to change the map's globe size by some delta and change the center latitude at the same time,
        // thus we need to compensate for the globe enlarging effect described earlier.
        const zoom = map.getZoom() + delta + getZoomAdjustment(map.getCenter().lat, center[1]);
        map.flyTo({
            center,
            zoom,
            essential: true // this animation is considered essential with respect to prefers-reduced-motion
        });
        zoomIn = !zoomIn;
    });
</script>
</body>
</html>