Network
The library downloads styles, tiles, fonts, and icons from the URLs in the style. This guide shows how to change those requests, serve resources from your own code, and tell the library when the device is offline. Transforms and resource providers apply to every map on the runtime.
Add headers
Section titled “Add headers”To add headers to requests, such as an Authorization header, set a header
transform with mln_runtime_set_http_header_transformRuntimeHandle.setHttpHeaderTransformRuntimeHandle.SetHttpHeaderTransformAsyncRuntimeHandle.SetHTTPHeaderTransformmaplibre_native_ffi_runtime_set_http_header_transform_asyncRuntimeHandle.setHttpHeaderTransformRuntimeHandle.setHttpHeaderTransformRuntimeHandle.set_http_header_transformRuntimeHandle::set_http_header_transformRuntimeHandle.setHttpHeaderTransformRuntimeHandle.setHttpHeaderTransformRuntime.setHttpHeaderTransform.
For fixed values, give the transform rules. Each rule matches a URL pattern and lists its headers. Match only URLs on your own server, so your token doesn’t go to other servers in the style.
For values that change, such as a token that expires, give the transform a callback instead. It sets the headers of each request.
In a browser and on OpenHarmony, serve the requests with a resource provider to add headers instead.
const mln_http_header header = { .name = mln_buffer_view_from_cstr("Authorization"), .value = mln_buffer_view_from_cstr("Bearer my-token"),};const mln_http_header_rule rule = { .size = sizeof(mln_http_header_rule), .flags = MLN_URL_MATCH_GLOB, .url = mln_buffer_view_from_cstr("https://tiles.example.com/**"), .headers = &header, .header_count = 1,};const mln_http_header_transform transform = { .size = sizeof(mln_http_header_transform), .rules = &rule, .rule_count = 1,};
mln_runtime_set_http_header_transform( runtime, &transform, NULL, NULL);// This binding takes rules only, because// it can't answer on a library thread.runtime.discarding.setHttpHeaderTransform( HttpHeaderTransform( rules: [ HttpHeaderRule( flags: UrlMatchFlag.glob, url: 'https://tiles.example.com/**', headers: [ HttpHeader( name: 'Authorization', value: 'Bearer my-token', ), ], ), ], ),);runtime.Discarding.SetHttpHeaderTransform( new HttpHeaderTransform { Rules = [ new HttpHeaderRule { Flags = UrlMatchFlag.Glob, Url = "https://tiles.example.com/**", Headers = [ new HttpHeader( "Authorization", "Bearer my-token" ), ], }, ], });headers := maplibre.HTTPHeaderTransform{ Rules: []maplibre.HTTPHeaderRule{{ Flags: maplibre.URLMatchFlagGlob, URL: "https://tiles.example.com/**", Headers: []maplibre.HTTPHeader{{ Name: "Authorization", Value: "Bearer my-token", }}, }},}err := runtime.Discarding().SetHTTPHeaderTransform(headers)if err != nil { return err}// This library takes rules only, because it can't answer// on a library thread.var header = new HttpHeader () { name = "Authorization", value = "Bearer my-token",};var header_rule = new HttpHeaderRule () { flags = UrlMatchFlag.GLOB, url = "https://tiles.example.com/**",};header_rule.set_headers ({ header });var headers = new HttpHeaderTransform ();headers.set_rules ({ header_rule });runtime.set_http_header_transform_async.begin (headers);HttpHeaderRule authorization = HttpHeaderRule.of() .withFlags(UrlMatchFlag.GLOB) .withUrl("https://tiles.example.com/**") .withHeaders( List.of( HttpHeader.of("Authorization") .withValue("Bearer my-token")));MaplibreNative.setHttpHeaderTransform( runtime.getDiscarding(), HttpHeaderTransform.of().withRules(List.of(authorization)));runtime.discarding.setHttpHeaderTransform( HttpHeaderTransform( rules = listOf( HttpHeaderRule( flags = UrlMatchFlag.GLOB, url = "https://tiles.example.com/**", headers = listOf( HttpHeader( name = "Authorization", value = "Bearer my-token", ), ), ), ), ),)# This binding takes rules only, because# it can't answer on a library thread.runtime.discarding.set_http_header_transform( mln.HttpHeaderTransform( rules=[ mln.HttpHeaderRule( flags=mln.UrlMatchFlag.GLOB, url="https://tiles.example.com/**", headers=[ mln.HttpHeader( name="Authorization", value="Bearer my-token", ) ], ) ] ))let rule = HttpHeaderRule { flags: UrlMatchFlag::GLOB, url: "https://tiles.example.com/**".to_owned(), headers: vec![HttpHeader { name: "Authorization".to_owned(), value: "Bearer my-token".to_owned(), }], ..Default::default()};
runtime.discarding().set_http_header_transform( HttpHeaderTransform { rules: vec![rule], ..Default::default() },)?;try runtime.discarding .setHttpHeaderTransform(HttpHeaderTransform(rules: [ HttpHeaderRule( flags: .glob, url: "https://tiles.example.com/**", headers: [HttpHeader( name: "Authorization", value: "Bearer my-token" )] ), ]))// This binding takes rules only, because// it can't answer on a library thread.runtime.discarding.setHttpHeaderTransform({ rules: [ { flags: UrlMatchFlag.glob, url: "https://tiles.example.com/**", headers: [ { name: "Authorization", value: "Bearer my-token" }, ], }, ],});try runtime.discarding().setHttpHeaderTransform(allocator, .{ .rules = &.{.{ .flags = .{ .glob = true }, .url = "https://tiles.example.com/**", .headers = &.{.{ .name = "Authorization", .value = "Bearer my-token", }}, }},}, null);Rewrite URLs
Section titled “Rewrite URLs”To change a request’s URL, such as to add an API key or to use another server,
set a resource transform with mln_runtime_set_resource_transformRuntimeHandle.setResourceTransformRuntimeHandle.SetResourceTransformAsyncRuntimeHandle.SetResourceTransformmaplibre_native_ffi_runtime_set_resource_transform_asyncRuntimeHandle.setResourceTransformRuntimeHandle.setResourceTransformRuntimeHandle.set_resource_transformRuntimeHandle::set_resource_transformRuntimeHandle.setResourceTransformRuntimeHandle.setResourceTransformRuntime.setResourceTransform.
To add query parameters, such as an API key, give the transform rules. Each rule matches a URL pattern and lists the parameters to add. As with headers, match only URLs on your own server.
const mln_url_query_parameter key = { .name = mln_buffer_view_from_cstr("key"), .value = mln_buffer_view_from_cstr("my-api-key"),};const mln_resource_transform_rule rule = { .size = sizeof(mln_resource_transform_rule), .flags = MLN_URL_MATCH_GLOB, .url = mln_buffer_view_from_cstr("https://tiles.example.com/**"), .parameters = &key, .parameter_count = 1,};const mln_resource_transform transform = { .size = sizeof(mln_resource_transform), .rules = &rule, .rule_count = 1,};
mln_runtime_set_resource_transform( runtime, &transform, NULL, NULL);runtime.discarding.setResourceTransform( ResourceTransform( rules: [ ResourceTransformRule( flags: UrlMatchFlag.glob, url: 'https://tiles.example.com/**', parameters: [ UrlQueryParameter(name: 'key', value: 'my-api-key'), ], ), ], ),);runtime.Discarding.SetResourceTransform( new ResourceTransform { Rules = [ new ResourceTransformRule { Flags = UrlMatchFlag.Glob, Url = "https://tiles.example.com/**", Parameters = [ new UrlQueryParameter( "key", "my-api-key" ), ], }, ], });keys := maplibre.ResourceTransform{ Rules: []maplibre.ResourceTransformRule{{ Flags: maplibre.URLMatchFlagGlob, URL: "https://tiles.example.com/**", Parameters: []maplibre.URLQueryParameter{{ Name: "key", Value: "my-api-key", }}, }},}err = runtime.Discarding().SetResourceTransform(keys)if err != nil { return err}var parameter = new UrlQueryParameter () { name = "key", value = "my-api-key",};var query_rule = new ResourceTransformRule () { flags = UrlMatchFlag.GLOB, url = "https://tiles.example.com/**",};query_rule.set_parameters ({ parameter });var query = new ResourceTransform ();query.set_rules ({ query_rule });runtime.set_resource_transform_async.begin (query);ResourceTransformRule apiKey = ResourceTransformRule.of() .withFlags(UrlMatchFlag.GLOB) .withUrl("https://tiles.example.com/**") .withParameters( List.of( UrlQueryParameter.of("key") .withValue("my-api-key")));runtime .getDiscarding() .setResourceTransform( ResourceTransform.of().withRules(List.of(apiKey)));runtime.discarding.setResourceTransform( ResourceTransform( rules = listOf( ResourceTransformRule( flags = UrlMatchFlag.GLOB, url = "https://tiles.example.com/**", parameters = listOf( UrlQueryParameter( name = "key", value = "my-api-key", ), ), ), ), ),)runtime.discarding.set_resource_transform( mln.ResourceTransform( rules=[ mln.ResourceTransformRule( flags=mln.UrlMatchFlag.GLOB, url="https://tiles.example.com/**", parameters=[ mln.UrlQueryParameter( name="key", value="my-api-key", ) ], ) ] ))let rule = ResourceTransformRule { flags: UrlMatchFlag::GLOB, url: "https://tiles.example.com/**".to_owned(), parameters: vec![UrlQueryParameter { name: "key".to_owned(), value: "my-api-key".to_owned(), }], ..Default::default()};
runtime.discarding().set_resource_transform( ResourceTransform { rules: vec![rule], ..Default::default() },)?;try runtime.discarding .setResourceTransform(ResourceTransform(rules: [ ResourceTransformRule( flags: .glob, url: "https://tiles.example.com/**", parameters: [UrlQueryParameter( name: "key", value: "my-api-key" )] ), ]))runtime.discarding.setResourceTransform({ rules: [ { flags: UrlMatchFlag.glob, url: "https://tiles.example.com/**", parameters: [{ name: "key", value: "my-api-key" }], }, ],});try runtime.discarding().setResourceTransform(allocator, .{ .rules = &.{.{ .flags = .{ .glob = true }, .url = "https://tiles.example.com/**", .parameters = &.{.{ .name = "key", .value = "my-api-key", }}, }},}, null);For any other change, give the transform a callback instead. It answers each
request with its new URL through
mln_resource_transform_request_completeResourceTransformRequestHandle.completeResourceTransformRequestHandle.CompleteResourceTransformRequestHandle.Completemaplibre_native_ffi_resource_transform_request_completeResourceTransformRequestHandle.completeResourceTransformRequestHandle.completeResourceTransformRequestHandle.completeResourceTransformRequestHandle::completeResourceTransformRequestHandle.completeResourceTransformRequestHandle.completeResourceTransformRequest.complete.
static void add_api_key( void* user_data, mln_resource_kind kind, mln_buffer_view url, mln_resource_transform_request request) { (void)user_data; (void)kind; char buffer[4096]; mln_buffer_view new_url = with_api_key(url, buffer, sizeof(buffer)); // The answer ends the request whatever it returns. A failed one // fails it. mln_resource_transform_request_complete(request, new_url, NULL);}
const mln_resource_transform transform = { .size = sizeof(mln_resource_transform), .callback = add_api_key,};
mln_runtime_set_resource_transform( runtime, &transform, NULL, NULL);runtime.discarding.setResourceTransform( ResourceTransform( callback: (_, url, request) { request.complete(withApiKey(url)); }, ),);// Runs on a library thread.runtime.Discarding.SetResourceTransform( new ResourceTransform { Callback = (_, url, request) => request.Complete(WithApiKey(url)), });rewrite := maplibre.ResourceTransform{ Callback: func( _ maplibre.ResourceKind, url string, request *maplibre.ResourceTransformRequestHandle, ) { // The answer ends the request whatever it returns. A failed // one fails the request. if err := request.Complete(withAPIKey(url)); err != nil { log.Print(err) } },}err = runtime.Discarding().SetResourceTransform(rewrite)if err != nil { return err}var rewrite = new ResourceTransform ();rewrite.set_callback ((kind, url, request) => { try { request.complete (with_api_key (url)); } catch (GLib.Error error) { warning ("%s", error.message); }});runtime.set_resource_transform_async.begin (rewrite);runtime .getDiscarding() .setResourceTransform( ResourceTransform.of() .withCallback( (kind, url, request) -> request.complete(withApiKey(url))));runtime.discarding.setResourceTransform( ResourceTransform { _, url, request -> request.complete(withApiKey(url)) },)def add_api_key( kind: mln.ResourceKind, url: str, request: mln.ResourceTransformRequestHandle,) -> None: request.complete(with_api_key(url))
runtime.discarding.set_resource_transform( mln.ResourceTransform(add_api_key))runtime.discarding().set_resource_transform( ResourceTransform::new(|_kind, url, request| { // Dropping the request without // an answer fails it. let _ = request.complete(&with_api_key(url)); }),)?;let transform = ResourceTransform { _, url, request in // The answer ends the request whatever it returns. A failed one // fails // the request. try? request.complete(url: withApiKey(url))}
try runtime.discarding.setResourceTransform(transform)runtime.discarding.setResourceTransform({ callback: (_kind, url, request) => { request.complete(withApiKey(url)); },});fn addApiKey( _: ?*anyopaque, _: maplibre.ResourceKind, url: []const u8, request: maplibre.ResourceTransformRequest,) anyerror!void { var owned = request; var buffer: [4096]u8 = undefined; // Returning an error releases the // request, which fails it. try owned.complete(try withApiKey(url, &buffer), null);}
try runtime.discarding().setResourceTransform( allocator, .{ .callback = addApiKey }, null,);To stop a request instead, such as when the callback can’t get a credential,
fail it with mln_resource_transform_request_failResourceTransformRequestHandle.failResourceTransformRequestHandle.FailResourceTransformRequestHandle.Failmaplibre_native_ffi_resource_transform_request_failResourceTransformRequestHandle.failResourceTransformRequestHandle.failResourceTransformRequestHandle.failResourceTransformRequestHandle::failResourceTransformRequestHandle.failResourceTransformRequestHandle.failResourceTransformRequest.fail. A request that the
callback releases without an answer also fails. An answer ends the request, even
when the answer fails, so don’t release the request after you answer it.
Serve resources
Section titled “Serve resources”You might not need code for this. The library already reads file:// URLs,
bundled files through asset:// URLs and the runtime’s asset path, and tile
archives through pmtiles:// and mbtiles:// URLs, so you can point the style
at those.
For anything else, set a resource provider with
mln_runtime_set_resource_providerRuntimeHandle.setResourceProviderRuntimeHandle.SetResourceProviderAsyncRuntimeHandle.SetResourceProvidermaplibre_native_ffi_runtime_set_resource_provider_asyncRuntimeHandle.setResourceProviderRuntimeHandle.setResourceProviderRuntimeHandle.set_resource_providerRuntimeHandle::set_resource_providerRuntimeHandle.setResourceProviderRuntimeHandle.setResourceProviderRuntime.setResourceProvider. Give your URLs a custom scheme in the
style, such as app://tiles/{z}/{x}/{y}.pbf, and give the provider a route that
matches it.
Answer each request with its bytes through
mln_resource_request_completeResourceRequestHandle.completeResourceRequestHandle.CompleteResourceRequestHandle.Completemaplibre_native_ffi_resource_request_completeResourceRequestHandle.completeResourceRequestHandle.completeResourceRequestHandle.completeResourceRequestHandle::completeResourceRequestHandle.completeResourceRequestHandle.completeResourceRequest.complete, or pass it through with
mln_resource_request_pass_throughResourceRequestHandle.passThroughResourceRequestHandle.PassThroughResourceRequestHandle.PassThroughmaplibre_native_ffi_resource_request_pass_throughResourceRequestHandle.passThroughResourceRequestHandle.passThroughResourceRequestHandle.pass_throughResourceRequestHandle::pass_throughResourceRequestHandle.passThroughResourceRequestHandle.passThroughResourceRequest.passThrough to let the library download it.
static void serve_tile( void* user_data, const mln_resource_request_info* request, mln_resource_request handle) { (void)user_data; mln_buffer_view tile = find_tile(request->requested_url);
mln_resource_response response = { .size = sizeof(mln_resource_response) }; if (tile.size > 0) { response.status = MLN_RESOURCE_RESPONSE_STATUS_OK; response.bytes = tile.data; response.byte_count = tile.size; } else { response.status = MLN_RESOURCE_RESPONSE_STATUS_NO_CONTENT; }
mln_resource_request_complete(handle, &response, NULL);}
const mln_resource_route route = { .size = sizeof(mln_resource_route), .kinds = MLN_RESOURCE_KIND_FLAG_TILE, .flags = MLN_URL_MATCH_GLOB, .url = mln_buffer_view_from_cstr("app://tiles/**"),};const mln_resource_provider provider = { .size = sizeof(mln_resource_provider), .callback = serve_tile, .routes = &route, .route_count = 1,};
mln_runtime_set_resource_provider(runtime, &provider, NULL, NULL);runtime.discarding.setResourceProvider( ResourceProvider( routes: [ ResourceRoute( kinds: ResourceKindFlag.tile, flags: UrlMatchFlag.glob, url: 'app://tiles/**', ), ], callback: (request, handle) { final tile = findTile(request.requestedUrl); handle.complete( tile == null ? ResourceResponse( status: ResourceResponseStatus.noContent, ) : ResourceResponse( status: ResourceResponseStatus.ok, bytes: tile, ), ); }, ),);runtime.Discarding.SetResourceProvider( new ResourceProvider { Routes = [ new ResourceRoute { Kinds = ResourceKindFlag.Tile, Flags = UrlMatchFlag.Glob, Url = "app://tiles/**", }, ], // Runs on a library thread. Callback = (request, handle) => handle.Complete( FindTile(request.RequestedUrl) is { } tile ? new ResourceResponse { Status = ResourceResponseStatus.Ok, Bytes = tile, } : new ResourceResponse { Status = ResourceResponseStatus.NoContent, } ), });provider := maplibre.ResourceProvider{ Routes: []maplibre.ResourceRoute{{ Kinds: maplibre.ResourceKindFlagTile, Flags: maplibre.URLMatchFlagGlob, URL: "app://tiles/**", }}, Callback: func( request maplibre.ResourceRequestInfo, handle *maplibre.ResourceRequestHandle, ) { response := maplibre.ResourceResponse{ Status: maplibre.ResourceResponseStatusNoContent, } if tile := findTile(request.RequestedURL); tile != nil { response = maplibre.ResourceResponse{ Status: maplibre.ResourceResponseStatusOk, Bytes: tile, } }
if err := handle.Complete(response); err != nil { log.Print(err) } },}err = runtime.Discarding().SetResourceProvider(provider)if err != nil { return err}var route = new ResourceRoute () { kinds = ResourceKindFlag.TILE, flags = UrlMatchFlag.GLOB, url = "app://tiles/**",};var provider = new ResourceProvider ();provider.set_routes ({ route });provider.set_callback ((request, handle) => { var tile = find_tile (request.get_requested_url ()); var response = new ResourceResponse () { status = ResourceResponseStatus.NO_CONTENT, }; if (tile != null) { response.status = ResourceResponseStatus.OK; response.bytes = tile; } try { handle.complete (response); } catch (GLib.Error error) { warning ("%s", error.message); }});runtime.set_resource_provider_async.begin (provider);// Runs on a library thread.private void serveTile( ResourceRequestInfo request, ResourceRequestHandle handle) { byte[] tile = findTile(request.getRequestedUrl()); if (tile == null) { handle.complete( ResourceResponse.of() .withStatus(ResourceResponseStatus.NO_CONTENT)); } else { handle.complete( ResourceResponse.of() .withStatus(ResourceResponseStatus.OK) .withBytes(tile)); }}
ResourceRoute tiles = ResourceRoute.of() .withKinds(ResourceKindFlag.TILE) .withFlags(UrlMatchFlag.GLOB) .withUrl("app://tiles/**");runtime .getDiscarding() .setResourceProvider( ResourceProvider.of(this::serveTile) .withRoutes(List.of(tiles)));runtime.discarding.setResourceProvider( ResourceProvider( routes = listOf( ResourceRoute( kinds = ResourceKindFlag.TILE, flags = UrlMatchFlag.GLOB, url = "app://tiles/**", ), ), ) { request, handle -> val tile = findTile(request.requestedUrl) handle.complete( if (tile == null) { ResourceResponse(status = ResourceResponseStatus.NO_CONTENT) } else { ResourceResponse( status = ResourceResponseStatus.OK, bytes = tile, ) }, ) },)def serve_tile( request: mln.ResourceRequestInfo, handle: mln.ResourceRequestHandle,) -> None: tile = find_tile(request.requested_url) if tile is None: handle.complete( mln.ResourceResponse( status=mln.ResourceResponseStatus.NO_CONTENT ) ) else: handle.complete( mln.ResourceResponse( status=mln.ResourceResponseStatus.OK, bytes=tile ) )
runtime.discarding.set_resource_provider( mln.ResourceProvider( serve_tile, routes=[ mln.ResourceRoute( kinds=mln.ResourceKindFlag.TILE, flags=mln.UrlMatchFlag.GLOB, url="app://tiles/**", ) ], ))let provider = ResourceProvider { routes: vec![ResourceRoute { kinds: ResourceKindFlag::TILE, flags: UrlMatchFlag::GLOB, url: "app://tiles/**".to_owned(), }], ..ResourceProvider::new(|request, handle| { let tile = find_tile(&request.requested_url); let response = match &tile { Some(bytes) => ResourceResponse { status: ResourceResponseStatus::Ok, bytes, ..Default::default() }, None => ResourceResponse { status: ResourceResponseStatus::NoContent, ..Default::default() }, }; // Dropping the request without an answer fails it. let _ = handle.complete(&response); })};
runtime.discarding().set_resource_provider(provider)?;try runtime.discarding.setResourceProvider(ResourceProvider( callback: { request, handle in let response = if let tile = findTile(request.requestedUrl) { ResourceResponse(status: .ok, bytes: tile) } else { ResourceResponse(status: .noContent) } try? handle.complete(response: response) }, routes: [ResourceRoute( kinds: .tile, flags: .glob, url: "app://tiles/**" )]))runtime.discarding.setResourceProvider({ routes: [ { kinds: ResourceKindFlag.tile, flags: UrlMatchFlag.glob, url: "app://tiles/**", }, ], callback: (request, handle) => { const tile = findTile(request.requestedUrl); handle.complete( tile === undefined ? { status: ResourceResponseStatus.noContent } : { status: ResourceResponseStatus.ok, bytes: tile }, ); },});fn serveTile( _: ?*anyopaque, request: maplibre.ResourceRequestInfo, handle: maplibre.ResourceRequest,) anyerror!void { var owned = handle; if (findTile(request.requested_url)) |tile| { try owned.complete(.{ .status = .ok, .bytes = tile }, null); } else { try owned.complete(.{ .status = .no_content }, null); }}
try runtime.discarding().setResourceProvider(allocator, .{ .callback = serveTile, .routes = &.{.{ .kinds = .{ .tile = true }, .flags = .{ .glob = true }, .url = "app://tiles/**", }},}, null);Add a custom source
Section titled “Add a custom source”For tiles that don’t come from a URL, such as data your code computes, add a
custom source. The map asks your code for each tile it needs, by zoom, x, and y.
A source added with mln_map_add_custom_geometry_sourceMapHandle.addCustomGeometrySourceMapHandle.AddCustomGeometrySourceAsyncMapHandle.AddCustomGeometrySourcemaplibre_native_ffi_map_add_custom_geometry_source_asyncMapHandle.addCustomGeometrySourceMapHandle.addCustomGeometrySourceMapHandle.add_custom_geometry_sourceMapHandle::add_custom_geometry_sourceMapHandle.addCustomGeometrySourceMapHandle.addCustomGeometrySourceMap.addCustomGeometrySource takes GeoJSON for
each tile. For encoded vector tiles, add a custom vector source with
mln_map_add_custom_vector_sourceMapHandle.addCustomVectorSourceMapHandle.AddCustomVectorSourceAsyncMapHandle.AddCustomVectorSourcemaplibre_native_ffi_map_add_custom_vector_source_asyncMapHandle.addCustomVectorSourceMapHandle.addCustomVectorSourceMapHandle.add_custom_vector_sourceMapHandle::add_custom_vector_sourceMapHandle.addCustomVectorSourceMapHandle.addCustomVectorSourceMap.addCustomVectorSource instead.
Add the source after the map’s style loads, then add a layer that draws it, as in Style.
Each request names its map and source. Answer it with the tile’s data through
mln_map_set_custom_geometry_source_tile_dataMapHandle.setCustomGeometrySourceTileDataMapHandle.SetCustomGeometrySourceTileDataAsyncMapHandle.SetCustomGeometrySourceTileDatamaplibre_native_ffi_map_set_custom_geometry_source_tile_data_asyncMapHandle.setCustomGeometrySourceTileDataMapHandle.setCustomGeometrySourceTileDataMapHandle.set_custom_geometry_source_tile_dataMapHandle::set_custom_geometry_source_tile_dataMapHandle.setCustomGeometrySourceTileDataMapHandle.setCustomGeometrySourceTileDataMap.setCustomGeometrySourceTileData. When your data for a tile
changes, invalidate the tile with
mln_map_invalidate_custom_geometry_source_tileMapHandle.invalidateCustomGeometrySourceTileMapHandle.InvalidateCustomGeometrySourceTileAsyncMapHandle.InvalidateCustomGeometrySourceTilemaplibre_native_ffi_map_invalidate_custom_geometry_source_tile_asyncMapHandle.invalidateCustomGeometrySourceTileMapHandle.invalidateCustomGeometrySourceTileMapHandle.invalidate_custom_geometry_source_tileMapHandle::invalidate_custom_geometry_source_tileMapHandle.invalidateCustomGeometrySourceTileMapHandle.invalidateCustomGeometrySourceTileMap.invalidateCustomGeometrySourceTile, and the map asks for it
again.
static void fetch_tile( void* user_data, mln_map map, mln_buffer_view source_id, mln_canonical_tile_id tile) { (void)user_data; mln_map_set_custom_geometry_source_tile_data( map, source_id, tile, mln_buffer_view_from_cstr(geojson_for_tile(tile)), NULL, NULL );}
const mln_custom_geometry_source_options options = { .size = sizeof(mln_custom_geometry_source_options), .fetch_tile = fetch_tile,};
mln_map_add_custom_geometry_source( map, mln_buffer_view_from_cstr("app-data"), &options, NULL, NULL);map.discarding.addCustomGeometrySource( 'app-data', CustomGeometrySourceOptions( fetchTile: (sourceMap, sourceId, tile) { sourceMap.discarding.setCustomGeometrySourceTileData( sourceId, tile, geojsonForTile(tile), ); }, ),);map.Discarding.AddCustomGeometrySource( "app-data", new CustomGeometrySourceOptions { // Runs on a library thread. FetchTile = (sourceMap, sourceId, tile) => sourceMap.Discarding.SetCustomGeometrySourceTileData( sourceId, tile, GeoJsonForTile(tile) ), });source := maplibre.CustomGeometrySourceOptions{ FetchTile: func( m *maplibre.MapHandle, sourceID string, tile maplibre.CanonicalTileID, ) { err := m.Discarding().SetCustomGeometrySourceTileData( sourceID, tile, geojsonForTile(tile), ) if err != nil { log.Print(err) } },}err = m.Discarding().AddCustomGeometrySource("app-data", source)if err != nil { return err}var source = new CustomGeometrySourceOptions ();source.set_fetch_tile ((source_map, source_id, tile) => { source_map.set_custom_geometry_source_tile_data_async.begin ( source_id, tile, geojson_for_tile (tile) );});map.add_custom_geometry_source_async.begin ("app-data", source);map.getDiscarding() .addCustomGeometrySource( "app-data", CustomGeometrySourceOptions.of( (sourceMap, sourceId, tile) -> sourceMap .getDiscarding() .setCustomGeometrySourceTileData( sourceId, tile, geojsonForTile(tile))));map.discarding.addCustomGeometrySource( "app-data", CustomGeometrySourceOptions( fetchTile = { sourceMap, sourceId, tile -> sourceMap.discarding.setCustomGeometrySourceTileData( sourceId, tile, geojsonForTile(tile), ) }, ),)def fetch_tile( map_handle: mln.MapHandle, source_id: str, tile: mln.CanonicalTileId,) -> None: map_handle.discarding.set_custom_geometry_source_tile_data( source_id, tile, geojson_for_tile(tile) )
map_handle.discarding.add_custom_geometry_source( "app-data", mln.CustomGeometrySourceOptions(fetch_tile=fetch_tile),)let options = CustomGeometrySourceOptions::new(|map, source_id, tile| { let data = geojson_for_tile(tile); let _ = map .discarding() .set_custom_geometry_source_tile_data( source_id, tile, &data, ); });
map.discarding() .add_custom_geometry_source("app-data", options)?;try map.discarding.addCustomGeometrySource( "app-data", options: CustomGeometrySourceOptions( fetchTile: { sourceMap, sourceId, tile in try? sourceMap.discarding.setCustomGeometrySourceTileData( sourceId, tileId: tile, data: geojsonForTile(tile) ) } ))map.discarding.addCustomGeometrySource("app-data", { fetchTile: (map, sourceId, tile) => { map.discarding.setCustomGeometrySourceTileData( sourceId, tile, geojsonForTile(tile), ); },});fn fetchTile( _: ?*anyopaque, map: *const maplibre.Map, source_id: []const u8, tile: maplibre.CanonicalTileId,) anyerror!void { try map.discarding().setCustomGeometrySourceTileData( source_id, tile, geojsonForTile(tile), null, );}
try map.discarding().addCustomGeometrySource( "app-data", .{ .fetch_tile = fetchTile }, null,);Set the network status
Section titled “Set the network status”When the device loses its connection, set the network status to offline with
mln_network_set_statussetNetworkStatusMaplibreNative.SetNetworkStatusSetNetworkStatusmaplibre_native_ffi_set_network_statussetNetworkStatussetNetworkStatusset_network_statusset_network_statussetNetworkStatussetNetworkStatussetNetworkStatus. The library then stops starting network requests
and loads what it can from its cache.
Set it back to online when the connection returns. The status covers every
runtime in the process.
mln_network_set_status(MLN_NETWORK_STATUS_OFFLINE, NULL);
// Later, when the connection returns:mln_network_set_status(MLN_NETWORK_STATUS_ONLINE, NULL);setNetworkStatus(NetworkStatus.offline);
// Later, when the connection returns:setNetworkStatus(NetworkStatus.online);MaplibreNative.SetNetworkStatus(NetworkStatus.Offline);
// Later, when the connection returns:MaplibreNative.SetNetworkStatus(NetworkStatus.Online);err = maplibre.SetNetworkStatus(maplibre.NetworkStatusOffline)if err != nil { return err}
// Later, when the connection returns:err = maplibre.SetNetworkStatus(maplibre.NetworkStatusOnline)if err != nil { return err}set_network_status (NetworkStatus.OFFLINE);
// Later, when the connection returns:set_network_status (NetworkStatus.ONLINE);MaplibreNative.setNetworkStatus(NetworkStatus.OFFLINE);
// Later, when the connection returns:MaplibreNative.setNetworkStatus(NetworkStatus.ONLINE);setNetworkStatus(NetworkStatus.OFFLINE)
// Later, when the connection returns:setNetworkStatus(NetworkStatus.ONLINE)mln.set_network_status(mln.NetworkStatus.OFFLINE)
# Later, when the connection returns:mln.set_network_status(mln.NetworkStatus.ONLINE)set_network_status(NetworkStatus::Offline)?;
// Later, when the connection returns:set_network_status(NetworkStatus::Online)?;try setNetworkStatus(.offline)
// Later, when the connection returns:try setNetworkStatus(.online)setNetworkStatus(NetworkStatus.offline);
// Later, when the connection returns:setNetworkStatus(NetworkStatus.online);try maplibre.setNetworkStatus(.offline, null);
// Later, when the connection returns:try maplibre.setNetworkStatus(.online, null);