Update data
A GeoJSON source accepts prepared inline data or a new URL. Both updates replace the whole dataset. A host that tracks individual features keeps its own collection and publishes the collection after each change.
Initialize the source with an empty feature collection. Every later update uses the same source ID, preserving the layers that draw it.
mln_geojson_source_data empty = MLN_HANDLE_NULL;mln_status status = mln_geojson_source_data_create( view("{\"type\":\"FeatureCollection\",\"features\":[]}"), NULL, &empty);if (status != MLN_STATUS_OK) return status;
status = mln_map_add_geojson_source_data(map, view("vehicles"), empty);mln_geojson_source_data_destroy(empty);return status;For each change, the host serializes its collection as one UTF-8 GeoJSON FeatureCollection. The snippet writes each position as a point feature into a caller-provided buffer.
size_t used = 0;int written = snprintf( json, json_capacity, "{\"type\":\"FeatureCollection\",\"features\":[");if (written < 0 || (size_t)written >= json_capacity) { return MLN_STATUS_INVALID_ARGUMENT;}used = (size_t)written;for (size_t i = 0; i < count; i++) { written = snprintf( json + used, json_capacity - used, "%s{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\"," "\"coordinates\":[%.8f,%.8f]},\"properties\":{}}", i == 0 ? "" : ",", positions[i].longitude, positions[i].latitude ); if (written < 0 || (size_t)written >= json_capacity - used) { return MLN_STATUS_INVALID_ARGUMENT; } used += (size_t)written;}if (json_capacity - used < 3) return MLN_STATUS_INVALID_ARGUMENT;memcpy(json + used, "]}", 3);An update has two steps. Preparation parses the bytes and tiles them into the index that the source consumes, which is the expensive part of the update, and it runs on any thread with no map in hand. Prepare the data with the same options that the source was added with; an install with different options reports an invalid-argument status.
// Preparation parses and tiles the collection, and runs on any thread. The// data carries the same default options the source was added with.mln_geojson_source_data prepared = MLN_HANDLE_NULL;mln_status status = mln_geojson_source_data_create(view(json), NULL, &prepared);if (status != MLN_STATUS_OK) return status;The install runs on the map’s owner thread and swaps the prepared index into the source, which is cheap at any data size. Destroy the prepared handle after the install, or keep it to install the same data on other matching sources. MapLibre copies what it keeps, so the serialization buffer is free for reuse once preparation returns.
// The install is cheap and runs on the map owner thread.status = mln_map_set_geojson_source_data(map, view("vehicles"), prepared);mln_geojson_source_data_destroy(prepared);return status;A host with a worker thread prepares there and installs from the owner thread, so a large dataset never blocks the pump. When a feed delivers faster than the display refreshes, collect the messages that arrive between frames and publish once per frame.
Synchronous tiling
Section titled “Synchronous tiling”MapLibre slices the tiles that the viewport requests out of the installed index on a worker thread. New features appear in a later frame, and the install call does not extend the current frame. Use this default for most feeds.
Set the synchronous-tiling option when the next frame must include the update. Examples include a simulation that advances with rendering and a still image captured immediately after an update. Tile slicing then runs during the render call on the render session’s owner thread, which extends that frame by the slicing work.
The option chosen when adding the source is the baseline, and a runtime override raises it temporarily. Enable the override around a burst of high-frequency small updates, such as a tracked position, and disable it to restore the source’s own option.
return mln_map_set_geojson_source_synchronous_tiling( map, view("vehicles"), tracking);