WORKFLOWS

Exporting

Every way to get data out: the section and globe export modals, what each layer contains, resolutions, sidecars and the Blender handoff.

An export is a background job. You pick a destination, tick layers, and Dayside writes every one of them at a single matched resolution into a folder on your machine. Nothing is uploaded and nothing is packed into a proprietary container: you get PNGs, JSON sidecars and a text sheet.

There are two export modals. A section export re-renders the section at full resolution. A globe export re-composes the whole planet from the tile crops the colour pass retained. They ship different layer sets and they cap at different resolutions.

Two ways out#

Where you areButtonWhat ships
Section editorExport in the top barOne section, up to ten layer ids
Globe workspaceExport globe in the top bar or the panel footerThe whole planet, five layer ids

Both queue a job and drop it into the Acquisitions drawer, which you open from the inbox button in the top bar. One export runs per world at a time; a second request while one is in flight returns 409.

The Export button is disabled while painted changes are still committing, while palette preferences are saving, and when the palette catalogue failed to load. Export globe is disabled until the world has an active mosaic.

Exporting a section#

Destinations preset the layer set#

The section export dialog
The section export dialog.

Pick the destination first. It replaces your ticks with a preset, so change it before you start ticking rather than after.

DestinationHintLayers it ticks
Blenderdisplace + texturesatellite, heightmap, land_heightmap
Unreallandscape + maskssatellite, heightmap, biome_mask, biome_class_masks, sea_mask
Tabletopmap image onlysatellite
Fileseverything availablesatellite, heightmap, land_heightmap, biome_mask, biome_class_masks, sea_mask

Files is the default. The destination only chooses the starting ticks; every layer stays available under every destination.

Four layers are in no preset at all, so you tick them by hand: Satellite colour · analytic, Satellite albedo · unlit, Water mask and Heightmap · water surface. The Blender preset deliberately leaves out Biome class masks, because a displacement-plus-colour workflow feeds a colour ramp from the single indexed Biome mask and one file per class is a folder of unused images.

Resolution comes from the workspace header#

The export modal shows the resolution read-only. You change it in the Output select in the section editor's top bar, which offers Native, 2K, 4K, 8K, 16K and 32K. The choice is remembered per world and section in local storage.

Native writes the section's own signed-heightmap grid. A numeric choice is an exact final long edge, and the short edge is rounded to preserve the aspect ratio:

w_out = round(w_native * long_edge / max(w_native, h_native))
h_out = round(h_native * long_edge / max(w_native, h_native))

The modal prints the resulting W × H px, and appends · final upscale when the target is larger than the section's own grid. That label is literal. The colour engine never enlarges a section to render it: the render runs at the section's native pitch and the result is upscaled with a Lanczos filter to the target. A 16K export of an 8K section is an honest upscale of an 8K render, and the modal says so in a warning when you pick 16K, which also notes that the 16K path is high-memory and not tiled.

Section exports stop at 16K#

The header offers 32K, but the server refuses a section export above 16 384 px with a 422 and the fix Use 'native' or a long edge no larger than 16384 px. Only a globe export reaches 32 768 px, because it re-composes from retained per-tile crops and streams to bounded memory rather than holding a full-resolution array.

If you want 32K of one region, export the globe at 32K instead, or leave the header on Native or 16K for section work.

The export runs its own render#

The request carries your current render state: params, the sea object, palettes or palette_preferences, seed, hillshade_exag, analysis_long_side and rivers. The satellite layer is a fresh full-resolution pass with those settings. Move a slider and export without re-rendering and the file gets the new look, even though the canvas still shows the old frame.

Every layer of one export writes at the same target size, computed with the same rounding the renderer uses, so the masks register with the colour pixel for pixel.

Cost is dominated by the render. In the progress bar a satellite layer carries weight 8.0 against 1.0 for every mask or heightmap re-encode. Satellite albedo · unlit and Water mask carry 8.0 as well, because on their own each runs its own analytic render. Requested alongside a satellite layer they ride that render's single traversal and finish almost immediately, so the pair costs far less than two renders.

Cancelling is safe for your output folder. Layers are generated into a server-owned scratch area and committed to the destination in one step at the end, so a cancelled export leaves no half-written folder behind.

Output folder#

Leave Output folder empty and the export lands in <world folder>\loresat_exports\<timestamp>_<section>_<destination>. Type a path and it is used as given, created if missing. The finished acquisition shows the full path with a copy button.

Section layers#

Ten layer ids ship today. The export writes them in this canonical order regardless of the order you tick them.

LayerLayer idFilesFormatWhat it is
Satellite coloursatellitesatellite.pngRGB 8-bit PNGThe full render with your current settings; the Atmospheric haze option adds physically-derived aerial perspective here (and only here — the analytic layer always stays clear)
Satellite colour · analyticsatellite_analyticsatellite_analytic.pngRGB 8-bit PNGThe analytic render on its own, section-only — on the globe the composed colour already is the analytic render of its tiles
Satellite albedo · unlitsatellite_albedosatellite_albedo.pngRGB 8-bit PNGThe same composite with the hillshade multiply removed and the seabed ghost unlit
Water maskwater_maskwater_mask.png8-bit greyscale PNGSea and solved lakes at 255, rivers at their anti-aliased coverage, dry land at 0
Heightmap · water surfacewater_surfacewater_surface_16bit.png, water_surface_16bit.json16-bit greyscale PNG plus JSONThe hydro-flattened heightmap on the same decode contract: standing water raised to its solved surface, max(z, level) over lakes and max(z, 0) over the sea
Heightmapheightmapheightmap_16bit.png, heightmap_16bit.json16-bit greyscale PNG plus JSONSigned elevation, ocean depth encoded, with the metres decode contract
Land heightmapland_heightmapland_heightmap_16bit.png, land_heightmap_16bit.json16-bit greyscale PNG plus JSONThe same encode with the ocean clamped to sea level, so black is exactly 0 m
Biome maskbiome_maskbiome_mask.pngIndexed 8-bit PNG, RGB fallbackThe authored Köppen crop, nearest-resampled to the target size
Biome class masksbiome_class_masksbiome_class_<code>.png per present class, biome_classes.json8-bit greyscale PNG set plus JSONOne hard black-and-white mask per Köppen class present in the crop
Sea / land masksea_masksea_mask.png8-bit greyscale PNGSea white, land black, cut at exactly 0 m

An export that includes either heightmap layer also writes handoff.txt beside the sidecars.

Satellite albedo#

satellite_albedo is the base colour for a renderer that will light the surface itself. The hillshade multiply is gone and the shallow-seabed ghost is present but unlit. Pair it with satellite_analytic if you want the lit and unlit halves of the same render.

The layer fails loudly rather than shipping a shaded image under its name. If the section has no recorded pixel geometry and the render degrades to a non-satellite style, the export errors and tells you to fix the geometry or drop the layer.

Water mask and sea mask#

These two answer different questions and you usually want both.

sea_mask states the decode contract: every sample at or below 0 m is sea and encodes as white. It comes from the heightmap alone and knows nothing about hydrology.

water_mask states where water actually stands in the render. Ocean and solved lakes are 255. River channels carry their anti-aliased coverage, so a channel narrower than one output pixel keeps its honest partial value instead of saturating. Dry land is 0, and a playa pan counts as dry. Standing water wins wherever the two overlap.

Two consequences worth holding on to. The water mask is only as good as the hydrology in the render, so with Rivers off it reports standing water only, and with river_blend at 0 no channels are drawn. The globe pass routes no flow at all, so water_mask is a section layer with no globe equivalent.

Heightmap and land heightmap#

Both are 16-bit greyscale PNGs with a JSON sidecar. The decode is stated in the sidecar and is the same for both:

elev_m = png_value / 65535 * scale_m - depth_m

heightmap fits scale_m and depth_m to the signed range, so ocean depth is encoded and sea level sits at sea_level_brightness, which the sidecar gives you as depth_m / scale_m.

land_heightmap clamps the ocean to sea level, so depth_m is exactly 0, black is exactly 0 m and the whole 65 536-code range covers land alone. On an Earth-like world that is roughly twice the vertical resolution of the full-range encode, which matters most on low-relief ground where displacement terracing shows first. scale_m is fitted to the land peak and floored at 1 m, so a flat or all-ocean section still yields a valid all-black encode. Ocean depth is not represented in this layer at all: pair it with sea_mask if you need the waterline, or use heightmap for bathymetry.

The design calls for 32-bit EXR. The engine's EXR path needs a host codec that is not shipped, so 16-bit PNG plus the JSON decode contract is what you get, and the UI labels it PNG 16-bit.

Biome mask and biome class masks#

biome_mask is your authored Köppen crop re-emitted at the target size. The resample is nearest-neighbour, because a class map is categorical and interpolating it would invent classes at every boundary. It writes as an exact indexed-colour PNG when the raster holds 256 unique colours or fewer, and falls back to plain RGB when an anti-aliased authored crop exceeds that.

biome_class_masks splits the same crop into one hard 8-bit mask per class actually present, white for in-class. The crop is resized first and classified second, which is what makes every boundary bit-identical to biome_mask's at the same size, so a project file can bind both without a pixel of drift.

Only classes with at least one pixel get a file. Where two Köppen codes share a palette colour, the nearest-match tie goes to the first in code order and the loser emits nothing, so two masks can never claim the same pixels or the same filename. Files are named biome_class_<code>.png when the crop's palette sidecar resolved standard Köppen codes; without that they degrade to class_<index>.png and the JSON carries the matched code and colour.

Both layers need a Köppen layer in the world. Without one the rows grey out, and the server refuses the request with a fix that names whichever of the two you asked for.

Layers that are not built yet#

River mask, Snow mask and Normal map appear as greyed rows with a soon badge in both modals, and a request for one returns 422. The snow and channel fields live inside the render and are not cheaply exposable yet.

water_mask is the shipped superset of river_mask for the water case, so use it instead. normal_map is the other half of the physically-based pair with satellite_albedo; for now, generate normals from land_heightmap in your renderer.

Exporting the globe#

Export globe opens from the globe workspace top bar or from the side panel footer. The modal header carries a status chip: UP TO DATE, NEEDS UPDATE or UPDATING.

Source#

There is no source picker. The modal states what it will ship: the single coloured globe with every region and tile edit folded in, or, when the world's map came from a focus-area rebuild rather than a globe colour pass, the world map stitched from your regions.

A stale mosaic blocks the export. When the globe needs updating the footer offers Re-compose first, and a request against a stale mosaic returns 409 telling you to rebuild or restore a coherent revision.

Resolution and retained tiles#

The ladder is Native · as displayed, 2K · 2048 × 1024, 4K, 8K, 16K and 32K. Globe output is always 2:1, so a 32K export is 32 768 × 16 384 px.

Every numeric option is disabled until the globe has retained tiles. Without them the modal explains that you need to run Re-compose globe first, because that pass produces the high-resolution globe the export ships.

This matters more than it looks. The on-globe display composite is capped at a 4096 px long edge to stay cheap to build and serve. Native ships that composite. The retained per-tile crops hold the full captured detail, and a numeric resolution re-composes from them, so 8K, 16K and 32K are genuinely more detail rather than an upscale of the 4K display map.

A scale readout under the buttons gives Pixels, Detail in px/cell, Ground in m/px and an approximate Est. size.

Globe layers#

Five layer ids compose from a coloured globe: satellite, heightmap, land_heightmap, biome_mask and sea_mask. The labels read Satellite colour, Heightmap · 16-bit, Land heightmap · sea at 0 m, Biome mask · Köppen and Sea mask.

Rows come from the server and fall into four kinds:

  • selectable, a normal tick;
  • locked, always ships and cannot be dropped, which is what a verbatim published world map's pinned layers do;
  • blocked, with the reason printed, for example that some sections have no signed terrain and need generating first, or that some sections have no Köppen crop;
  • soon, for the three deferred masks.

The footer counts exactly the layers the request will yield, and the primary button disables at zero with a tooltip saying nothing would ship and to select at least one producible layer.

Two section layers are deliberately absent from the globe set. satellite_analytic is missing because the globe's composed colour already is the analytic render of its tiles, so the explicit layer would duplicate satellite. biome_class_masks is missing because the globe's biome mask is composed from per-section crops and the present-class list that keeps the matcher honest is per section.

Colour is fixed when you colour the globe#

What lands in the folder#

The colour layer ships as a float32 .npy array plus an 8-bit satellite.png you can actually open, and a mosaic_manifest.json recording the immutable mosaic snapshot. At Native the pinned assets are copied verbatim and hash-verified on the way out. At a numeric resolution the colour is re-composed and the manifest records reassembled_resolution and the new output_size.

The height and mask layers write the same filenames as a section export: heightmap_16bit.png and its sidecar, land_heightmap_16bit.png and its sidecar, biome_mask.png, sea_mask.png. All three height-derived layers compose one shared metres field, so ticking all of them costs one compose rather than three.

There is no handoff.txt on the globe path, because Gaea's map size is a section-scoped idea and a whole planet has no single one. The globe heightmap sidecars still carry a handoff block, computed against the planet's circumference, and it will correctly report that the map far exceeds Gaea's 2400 km cap.

Acquisitions and the receipt#

The Acquisitions drawer lists every export, running and finished. Each entry shows the destination and resolution, a status pill (QUEUED · POSITION n, EXPORTING N%, DONE, or a failure), the time, the layer and file counts, the priority, a read-only path field with a copy button, and Prioritise and Cancel while it is in flight.

The receipt#

A completed export renders a receipt headed Export complete with the number of layers shipped and the wall-clock duration, then one row per file that was actually written. Each row gives the filename, the real pixel dimensions read back from the file header, the byte size and a Copy file path button. A file that went missing or unreadable says so in place of its dimensions.

The count is what shipped rather than what you ticked. Warnings recorded during the run, such as a globe layer that could not be composed, ride along on the export record.

Set in Gaea#

When the export wrote a heightmap layer, the receipt adds a Set in Gaea panel with the numbers to type into Gaea's terrain definition, one block per heightmap layer, each with a copy button:

ReadingWhere it comes from
Map size in kmThe section's long edge, or Gaea's 2400 km cap when it exceeds it
Height in mscale_m, already multiplied by the width scale when the section exceeds the cap
Sea level, a brightness on 0 to 1, 4 decimalsdepth_m / scale_m, never a depth in metres
Terrain width scale, 3 decimalsShown only over the cap, to keep the height-to-width ratio

The layer is always named, because heightmap and land_heightmap differ precisely in their sea level and that is the pair most easily mixed up: the land layer's sea level is exactly 0.0000.

If the record carries no numbers, the panel points at handoff.txt instead. That happens for an export recovered after a server restart: the handoff numbers are deliberately not part of the durable job schema, so a recovered record reports none, and the sheet on disk is the surviving copy.

Scale metadata in the export#

The heightmap sidecar#

heightmap_16bit.json and land_heightmap_16bit.json carry, alongside the honesty and Blender blocks:

FieldMeaning
encoding, decodegrayscale PNG, 16-bit and the decode expression in words
scale_m, depth_mThe two constants the decode needs
sea_level_m, sea_level_brightnessAlways 0.0, and the brightness 0 m lands at
size_px, native_size_pxThe exported grid and the section's own grid
pixel_size_mGround sample distance of the exported grid, in m/px
extent_kmThe persisted raster's ground size, in km
sectionThe section id
ocean_policy, ocean_policy_notesigned_full_range or clamped_to_sea_level, spelled out
handoffThe extent, km per pixel, and the Gaea and Wilbur blocks

One subtlety about extent_km. It measures the persisted raster, which includes the feather margin a focus area is cut with, rather than the polygon you drew on the globe. That is the right number here, because the raster is the file Gaea receives. The Regions panel and the editor footer quote the drawn selection instead, so the two readings differ by roughly the feather.

handoff.txt#

An export including either heightmap layer writes handoff.txt beside the sidecars. It opens with the section id and its pixel size in m/px, then per layer: the extent in km, the resolution in km/pixel, a For Gaea block with map size, suggested height, the shown calculation when the section is over the cap, sea level and terrain width, and a For Wilbur block with the erosion preset, the scale in km/px and px/km, and the affine mapping between metres and Wilbur's unitless 500 to 3500 span.

The Wilbur recipe follows: six steps in order with every parameter filled in for this section's size, plus a table of pre-blur radii recomputed for 4096 and 8192 px re-exports, because those radii are in pixels and the same km extent at a bigger resolution would otherwise under-blur. The recipe is derived from extent and pixel pitch rather than from any layer's heights, so when both heightmap layers agree it is printed once at the end.

The preset comes from the section width: LOCAL under 500 km, REGIONAL under 1500 km, CONTINENTAL under 4000 km, SUPERCONTINENTAL above that.

The other sidecars#

biome_classes.json records target_px, unmatched_pixels, the source, the colour match_distance of 60.0 in RGB euclidean terms, and one entry per emitted class with its Köppen code, filename, canonical palette RGB, pixel count and share of the whole raster. Pixels that matched nothing, such as white background or an off-palette colour, belong to no class and are counted in unmatched_pixels instead of being folded into a neighbour.

mosaic_manifest.json ships inside a globe export folder and carries the immutable mosaic snapshot: its revision, recipe, output size and content-addressed layer entries with their hashes.

There is no coordinate reference system#

Exports carry physical scale rather than georeferencing. There is no GeoTIFF writer, no world file and no CRS or EPSG code anywhere in the export path. What travels with a file is extent_km, pixel_size_m and, for a section, its id. Treat the rasters as images with a known ground sample distance and set the scale by hand in whatever consumes them.

Taking an export into Blender#

The heightmap sidecars carry a blender block so the numbers are stated rather than discovered.

Set the colour space first#

image_colorspace is Non-Color, and it is the first field in the block for a reason. A 16-bit heightmap read as sRGB gets a transfer curve applied, and the terrain comes out wrong in a way that still looks plausible. It is the most common heightmap-in-Blender mistake. Set the image datablock's colour space to Non-Color before you do anything else.

Displace numbers#

The sidecar resolves the Displace modifier's own maths against that file's numbers:

displaced = original + normal * (value - midlevel) * strength
LayerMidlevelStrength
land_heightmap0scale_m, the land peak in metres
heightmapsea_level_brightness, that is depth_m / scale_mscale_m, the full signed range in metres

Both are in the sidecar as displace_midlevel and displace_strength_m. land_heightmap is the ergonomic choice for displacement work: midlevel 0 needs no lookup, and the whole code range covers land.

The globe is equirectangular 2:1#

A world export is equirectangular at exactly 2:1, which matches a UV sphere's default UVs in Blender. The sidecar states this too, along with the two consequences: the poles are oversampled, and the seam at ±180° wraps. Displace a subdivided UV sphere with land_heightmap, feed the satellite PNG to base colour, and the mapping needs no adjustment.

Subdivide enough that the mesh can carry the map. A 16K globe export holds detail a low-density sphere will simply not sample.

Albedo and water mask#

For a lit render, use satellite_albedo as base colour rather than satellite: the satellite layer has the hillshade baked in, and lighting an already-shaded image gives you two suns.

water_mask is the roughness driver of that pair. Invert it into a roughness input, or use it directly as the mix factor between a ground shader and a water shader. It is pixel-registered with the colour layers when both come from the same export, so no alignment work is needed.

TO CONFIRMa Blender add-on that brings tiles in as a displaced sphere with matched colour has been described as a deliverable. The only add-on in this repo is blender_addons/project_r, which is the Project-R sibling product (its own id, name and manifest) and has no Displace or import-a-Dayside-export operator anywhere in it. Confirm what actually ships before this section claims an add-on workflow, and I will write it up.
TO CONFIRMper-tier export caps have been floated as part of the licence model. Nothing in the export path enforces a licence tier today; the only caps in code are 16 384 px for a section and 32 768 px for a globe. Confirm whether licence gating lands before v1.0, so this page can state one set of limits.

Round-tripping through Gaea or Wilbur#

The handoff sheet exists so a section can leave and come back. Export heightmap or land_heightmap, type the numbers from the receipt or handoff.txt into Gaea, do the erosion work there, then bring the file back with Import in the section editor.

The import dialog asks which export you round-tripped, prefills the Gaea Height you were told to use, and accepts the exported heightmap's JSON sidecar directly when the section changed since the export. The file can be any resolution: it is fitted to the section's grid rather than cropped. Picking the land-only variant keeps this world's bathymetry and takes the coastline from the file's own 0 m.

One warning worth repeating: changing the heightmap invalidates a refined coastline, so plan to re-refine after a round trip.