Work offline
One database stores everything that a runtime caches. The cache path in the
runtime options selects its location. Set a writable filesystem path to preserve
resources across host runs; the default :memory: database lasts only for the
runtime’s lifetime.
Two storage classes share the database. The ambient cache contains fetched resources and evicts them to stay within a byte budget. An offline region contains every resource that a defined area needs and remains outside the ambient budget. Download a region when the host must draw a known area without a network.
Database operations are asynchronous. A start call validates its arguments, returns an operation ID, and queues the work. The runtime later queues one operation-completed event with the ID and result status while its subscription selects that type, as Handle events describes. This result is separate from the start call’s status. Take the result value when an operation produces one. Take or discard every operation to release its runtime state.
Download a region
Section titled “Download a region”A region definition names a style and an area. MapLibre resolves the definition into every tile, glyph, and sprite that the style needs across the zoom range, and stores them under one region ID.
mln_offline_tile_pyramid_region_definition pyramid = { .size = sizeof(pyramid), .style_url = "https://tiles.openfreemap.org/styles/bright", .bounds = bounds, .min_zoom = 10.0, .max_zoom = 15.0, .pixel_ratio = 1.0f, .include_ideographs = false,};The create call takes a tagged definition. A tile-pyramid definition covers a bounding box, and a geometry definition covers a GeoJSON shape.
mln_offline_region_definition definition = { .size = sizeof(definition), .type = MLN_OFFLINE_REGION_DEFINITION_TILE_PYRAMID, .data.tile_pyramid = pyramid,};Creation also takes opaque metadata bytes, and the database stores them with the region. A host records what the region holds there, such as a name or a version. MapLibre copies the definition, the style URL, and the metadata bytes before the call returns.
mln_offline_operation_id create_id = 0;if ( mln_runtime_offline_region_create_start( runtime, &definition, (const uint8_t*)metadata, strlen(metadata), &create_id ) != MLN_STATUS_OK) { return 0;}Taking the result produces a snapshot handle that contains the region record. A failed take leaves the operation live. Discard the operation after the host stops retrying.
mln_offline_region_snapshot region = MLN_HANDLE_NULL;if (await_operation(runtime, create_id) == MLN_STATUS_OK) { mln_runtime_offline_region_create_take_result(runtime, create_id, ®ion);}if (region == MLN_HANDLE_NULL) { mln_runtime_offline_operation_discard(runtime, create_id); return 0;}The region ID is the part of the record that the remaining calls need.
// Copy metadata before destroying the snapshot; info.id remains valid.mln_offline_region_info info = {.size = sizeof(info)};mln_offline_region_snapshot_get(region, &info);mln_offline_region_snapshot_destroy(region);Mark the region as observed to receive progress and error events. Then set its download state to active. MapLibre fetches the resources while the host pumps the runtime.
mln_offline_operation_id download_id = 0;if ( mln_runtime_offline_region_set_download_state_start( runtime, info.id, MLN_OFFLINE_REGION_DOWNLOAD_ACTIVE, &download_id ) != MLN_STATUS_OK || finish_operation(runtime, download_id) != MLN_STATUS_OK) { return 0;}A status-changed event contains resource counts, tile counts, and stored bytes. A response-error event reports one failed resource while the remaining download continues. Drain both from the runtime that owns the region, as described in Concepts.
const mln_runtime_event_offline_region_status* progress = &event->payload.offline_region_status;if (progress->region_id != region_id) return false;
// Treat the fraction as an estimate until the required count is precise.*out_fraction = progress->status.required_resource_count == 0 ? 0.0 : (double)progress->status.completed_resource_count / (double)progress->status.required_resource_count;return progress->status.complete;Set the download state to inactive to pause. Set it to active to resume from the resources that MapLibre has stored.
Read what the database holds
Section titled “Read what the database holds”List the regions in the database to learn what an earlier run of the host stored, because region records outlive the runtime that wrote them. One list operation covers the whole database.
mln_offline_operation_id list_id = 0;if ( mln_runtime_offline_regions_list_start(runtime, &list_id) != MLN_STATUS_OK) { return 0;}Taking the result produces a list handle that the host owns. Destroy that handle after reading the entries.
mln_offline_region_list list = MLN_HANDLE_NULL;if (await_operation(runtime, list_id) == MLN_STATUS_OK) { mln_runtime_offline_regions_list_take_result(runtime, list_id, &list);}if (list == MLN_HANDLE_NULL) { mln_runtime_offline_operation_discard(runtime, list_id); return 0;}Each entry carries the region ID, the definition that created the region, and the metadata that the host passed at creation.
size_t count = 0;mln_offline_region_list_count(list, &count);
for (size_t index = 0; index < count; index++) { // Copy the definition and metadata before destroying the list. mln_offline_region_info info = {.size = sizeof(info)}; if (mln_offline_region_list_get(list, index, &info) != MLN_STATUS_OK) { continue; }Deleting a region removes its record and evicts the resources that no other region requires.
mln_offline_operation_id delete_id = 0;if ( mln_runtime_offline_region_delete_start(runtime, info.id, &delete_id) == MLN_STATUS_OK && finish_operation(runtime, delete_id) == MLN_STATUS_OK) { deleted++;}