Docs / Core / Projection & zoom

Projection and zoom

Projection flattens a round Earth onto a flat screen; zoom says how close you are. Providers disagree on both, so MapConductor shares one set of constants and formulas across all three platforms and normalises every value it exposes. The camera page covers how to drive the camera — this one covers the numbers underneath.

ELLIPSOID
WGS84
PROJECTIONS
WebMercator · WGS84
ZOOM RANGE
0 – 22
Platform

01 · The Earth constants

Projection, geodesy and tile maths all read from Earth.

Constant
Value
Description
RADIUS_METERS
6 378 137.0
WGS84 semi-major axis (equatorial radius).
CIRCUMFERENCE_METERS
2πa
Equatorial circumference; used for tile resolution maths.
FLATTENING
1 / 298.257223563
Flattening; used by the ellipsoidal geodesic model.
SEMI_MINOR_AXIS_METERS
a(1 − f)
Semi-minor axis (polar radius).
ECCENTRICITY_SQUARED
f(2 − f)
First eccentricity squared.
WEB_MERCATOR_MAX_EXTENT_METERS
πa ≈ 20 037 508.34
The largest value Web Mercator coordinates take; projected x and y stay within ±this.

02 · Projections

A projection has exactly two operations: project and unproject — flatten a coordinate to a planar Offset and take it back again. Two implementations ship with the SDK.

WebMercator

Web Mercator, in metres

Projects onto the ±20,037,508.34 m plane, the same as EPSG:3857. Used for tile-boundary maths and for converting screen distances into metres.

WGS84

256-pixel tile space

Projects into the coordinate space of the single zoom-0 tile (256×256). This is the space the tile renderers draw in.

com.mapconductor.core.projection
interface ProjectionInterface {
    fun project(position: GeoPointInterface): Offset
    fun unproject(point: Offset): GeoPointInterface
}

val meters = WebMercator.project(tokyo)   // x, y はメートル
val tile = WGS84.project(tokyo)           // 0..256 のタイル座標

When you need pixels rather than a plane, use toScreenOffset / fromScreenOffset on the MapViewHolder instead of a projection — those account for the tilt and bearing actually on screen.

03 · Zoom and altitude

2D tile providers express scale as a zoom level; 3D ones as camera altitude or range. MapConductor converts between them so your app always sees one zoom scale. Each provider implements one converter class extending the shared abstract base.

Latitude and tilt are part of the conversion: Web Mercator magnifies towards the poles, so the same zoom level needs a several-fold different camera altitude at the equator and at high latitude.

Figure · camera altitude for one zoom level, relative to the equator
0° · 赤道
×1.00
30° · カイロ
×0.87
35° · 東京
×0.82
At the same zoom 14 the camera sits about 20% lower than it would at the equator.
60° · オスロ
×0.50
70° · トロムソ
×0.34
Roughly three times the equator. Match altitudes while ignoring latitude and the visible area drifts this far apart.
AbstractZoomAltitudeConverter · Kotlin
abstract class AbstractZoomAltitudeConverter(
    protected val zoom0Altitude: Double,
) {
    abstract fun zoomLevelToAltitude(zoomLevel: Double, latitude: Double, tilt: Double): Double

    abstract fun altitudeToZoomLevel(altitude: Double, latitude: Double, tilt: Double): Double
}

Each provider package ships its own ZoomAltitudeConverter. Your app does not normally call it, but it is available if you are building your own 3D presentation.

What the video shows · Android + MapLibreKotlin · Jetpack Compose
// The same camera position handed to both states
val shared = MapCameraPosition(
    position = GeoPoint.fromLatLong(35.6812, 139.7671),
    zoom = 14.0,
)
val maplibre = rememberMapLibreMapViewState(cameraPosition = shared)
val googlemaps = rememberGoogleMapViewState(cameraPosition = shared)

// Move one and the same value goes to the other
Row {
    MapLibreMapView(
        state = maplibre,
        modifier = Modifier.weight(1f),
        onCameraMoveEnd = { googlemaps.moveCameraTo(it) },
    )
    GoogleMapView(
        state = googlemaps,
        modifier = Modifier.weight(1f),
        onCameraMoveEnd = { maplibre.moveCameraTo(it) },
    )
}
Sample video · the same zoom value across providers
Video not shot yetHolding one zoom value while the provider is swapped, showing how far the actual scale drifts.

04 · Limits

These are the constants the conversion works with. Lower bounds on the cosines keep the formulas from blowing up near the poles or looking straight down.

DEFAULT_ZOOM0_ALTITUDE
171,319,879 m
ZOOM_FACTOR
2.0
MIN / MAX_ZOOM_LEVEL
0.0 / 22.0
MIN / MAX_ALTITUDE
100 / 50,000,000 m
MIN_COS_LAT
0.01
MIN_COS_TILT
0.05
WEB_MERCATOR_INITIAL_MPP_256
156,543.033928
TILE SIZE
256 px

The zoom-0 reference altitude was not derived on paper: it is the result of measuring the area each engine actually shows at a given zoom and tuning until they matched. The only constants are that relation and one step of zoom halving the altitude (ZOOM_FACTOR = 2).

Related pages