Skip to main content

2D, 3D and everything between

A map with terrain can be drawn flat or in 3D, and the SDK has several ways to be flat. They look the same and cost differently. This page lists every state, how to get into it, how to move between two of them, and what each is good for.

Numbers are from a Crosscall HLTE556N (Adreno 610) with the Massif style over Rhône-Alpes data and extruded buildings, three runs per state. The device speed drifts, so read them as ratios.

The switches​

Three settings decide the state. You set the first three; the SDK derives the rest.

SettingValuesDecides
Enabledtrue / falseWhether terrain exists at all. Off means no elevation data and no terrain code anywhere.
Flattenedtrue / falseWhether the terrain is drawn right now. The map eases between the two over AutoFlattenDuration. The auto-flatten rule (see the terrain page) writes this same setting.
FlattenModeRENDER (default) / FULLWhat a flat map keeps. RENDER keeps everything 3D needs, so switching back is free. FULL throws it away and the flat map is a plain 2D one.
AlwaysDecodefalse (default) / trueWhether a disabled terrain still keeps its tiles ready, so Enabled can be switched without re-decoding.
Exaggeration0 …Height scale. 0 is a flat ground that is still drawn as 3D.

"Decoded for terrain" below means the tiles were built with the terrain tile set and the geometry carries the terrain subdivision, with the elevation data warmed while they decode. A tile decoded for terrain draws fine flat. A tile decoded plain does not draw right in 3D (a road chords straight between its ends and floats over a valley), so the SDK re-decodes whenever it has to go from plain to terrain. That re-decode is what every cost below is about.

The states​

StateYou setLooksTilesBuilding shadowsCost against Terrain off
Terrain offEnabled false (or no TerrainOptions)2Dplainwith the light onthe baseline
Flat, readyEnabled true, Flattened true, RENDER2Dfor terrainwith the light onnone measured: same tiles, bytes and frame time
Flat, plainEnabled true, Flattened true, FULL2Dplainwith the light onnot measured; the tiles are the baseline's by design
3DEnabled true, Flattened falseterrain, drapefor terrainwith the light on27 to 47% more tiles, up to 35% more bytes, frame time +30 to +37%
3D, zero reliefas 3D, Exaggeration 0flat ground, drawn as 3Dfor terrainwith the light onabout +5 ms a frame at tilt 90 (+17%)
Terrain off, readyEnabled false, AlwaysDecode true2Dfor terrainwith the light onabout +2% frame time, plus the elevation fetches

A few things the table hides:

  • Shadows follow the light, not the terrain. Buildings and models cast and receive shadows in every state once TerrainLightingEnabled is on and ShadowStrength is above 0, flat or 3D, with no terrain attached or with one. Flat states pay about 3 ms a frame for them; 3D, zero relief pays 5 to 15 ms.
  • Auto-flatten writes Flattened. At tilt 88° and above the SDK flattens by itself, so with the default RENDER you land in Flat, ready, and the shadows stay. Set AutoFlattenTilt to 0 to keep 3D at any tilt.
  • AlwaysDecode does not touch FULL. In FULL a flat map is plain by definition; the option only covers a disabled terrain.

Moving between states​

From → toRe-decodesWhat you see
Flat, ready ↔ 3D (Flattened)nothingone ramp, AutoFlattenDuration long. Rising, only about 8 to 10 new tiles load, in about 2 s
Flat, plain ↔ 3D (Flattened, FULL)the visible tiles, the first time each waythe map stays flat until the 3D tiles are ready, then ramps. A repeat switch at the same view reuses the tiles it kept aside
Terrain off → 3D (Enabled)every visible tile3D draws at once on tiles that are not ready for it for about 5 s (31 tiles at tilt 50)
Terrain off, ready → 3D (Enabled)nothing but the tiles the wider 3D view addsabout 2 s, the same as flat-ready
3D → Terrain offevery visible tileinvisible if you flatten first, because the re-decode happens while the map is already flat

TerrainOptions.isSwitching() is true while a rise waits for its tiles.

Which one to use​

  • A 2D map that never goes 3D: no TerrainOptions at all. Nothing else comes cheaper.
  • A map with a 2D/3D button, or one that flattens itself far out and tilts back in: Flat, ready. It costs what Terrain off costs and switches for free. This is the default, and the reason RENDER is the default.
  • A map that is flat most of the time and 3D rarely, where the flat state should be as cheap as possible: Flat, plain (FULL). You pay one re-decode, hidden behind the wait, each time you go to 3D for the first time at a view. The gallery's 2D/3D switch example uses it.
  • An app that turns terrain on and off with Enabled (a settings toggle, a layer switcher): set AlwaysDecode. Without it the first Enabled turns 3D on over tiles that are not ready for it. It is the only state that pays in 2D for something you may never use, so leave it off if the toggle is rare.
  • Buildings, models and their shadows on a map that looks flat: any flat state, with the light on. 3D, zero relief gets there through the full terrain pipeline and costs more.
  • Straight-down maps with no shadows wanted: leave auto-flatten on.

Code​

TerrainOptions terrain = new TerrainOptions(demSource);
MassifInterop.getOptionsByHandle(mapView.getMapHandle()).setTerrainOptions(terrain);

// Flat, ready: attached and enabled, drawn flat until you ask for 3D.
terrain.setFlattened(true); // before the first layer decodes: nothing is built for a 3D not yet shown
terrain.setFlattened(false); // the button: ramps up, no re-decode

// Flat, plain: the flat map is a plain 2D one.
terrain.setFlattenMode(TerrainFlattenMode.TERRAIN_FLATTEN_MODE_FULL);

// An Enabled toggle that never re-decodes.
terrain.setAlwaysDecode(true);
terrain.setEnabled(false); // later: setEnabled(true)

// 3D at zero relief: flat ground, drawn by the full terrain pipeline.
terrain.setExaggeration(0f);

// Building and model shadows, in any state: the sun and the strength, with or without terrain.
LightOptions light = new LightOptions();
light.setTerrainLightingEnabled(true);
light.setShadowStrength(1f);
MassifInterop.getOptionsByHandle(mapView.getMapHandle()).setLightOptions(light);
const terrain = map.terrain();
terrain.set('flattened', true);
terrain.set('flattenMode', 'TERRAIN_FLATTEN_MODE_FULL');
terrain.set('alwaysDecode', true);
terrain.set('exaggeration', 0);

Measured​

Grenoble z16.2, tilt 26, Chamonix z14.2, tilt 40 and Grenoble z16.3, tilt 90, panning; frame time in ms; the same run of each pair side by side because the device drifts between runs.

Terrain offFlat, ready3D3D, zero relief
Grenoble tilt 26, tiles decoded189190240not measured
Chamonix tilt 40, tiles decoded494972not measured
Grenoble tilt 90, tiles decoded66678985
Grenoble tilt 90, frame30.831.042.236.0

The full table, with the method, is in the terrain internals.