Query
This guide shows how to find the features the map draws, or the features a source has loaded.
Queries run through the map’s render session. A rendered query reads the last frame, so after a camera or style change, render a frame before you query.
Query rendered features
Section titled “Query rendered features”To find what’s drawn at a screen point or in a box, use
mln_render_session_query_rendered_featuresRenderSessionHandle.queryRenderedFeaturesRenderSessionHandle.QueryRenderedFeaturesAsyncRenderSessionHandle.QueryRenderedFeaturesmaplibre_native_ffi_render_session_query_rendered_features_asyncRenderSessionHandle.queryRenderedFeaturesRenderSessionHandle.queryRenderedFeaturesRenderSessionHandle.query_rendered_featuresRenderSessionHandle::query_rendered_featuresRenderSessionHandle.queryRenderedFeaturesRenderSessionHandle.queryRenderedFeaturesRenderSession.queryRenderedFeatures. Name the layers to query, or
leave them out to query every layer.
static void on_rendered( void* user_data, const mln_completion_result* result) { if (result->status == MLN_STATUS_OK) { for (size_t i = 0; i < result->value_count; i++) { const mln_rendered_feature* hit = MLN_STRIDED_AT( mln_rendered_feature, result->value, result->value_size, i ); // The views are valid only during this callback. copy_feature(hit->feature); } } on_done(user_data, result);}
const mln_rendered_query_geometry box = { .size = sizeof(mln_rendered_query_geometry), .type = MLN_RENDERED_QUERY_GEOMETRY_TYPE_BOX, .data.box = { .min = {.x = point.x - 5, .y = point.y - 5}, .max = {.x = point.x + 5, .y = point.y + 5}, },};const mln_buffer_view layers[] = {mln_buffer_view_from_cstr("poi")};const mln_rendered_feature_query_options options = { .size = sizeof(mln_rendered_feature_query_options), .fields = MLN_RENDERED_FEATURE_QUERY_OPTION_LAYER_IDS, .layer_ids = layers, .layer_id_count = 1,};const mln_completion rendered = { .size = sizeof(mln_completion), .callback = on_rendered};mln_render_session_query_rendered_features( session, &box, &options, &rendered, NULL);final hits = await session.queryRenderedFeatures( RenderedQueryGeometry.box( ScreenBox( min: ScreenPoint(x: point.x - 5, y: point.y - 5), max: ScreenPoint(x: point.x + 5, y: point.y + 5), ), ), options: RenderedFeatureQueryOptions(layerIds: ['poi']),);for (final hit in hits) { // hit.feature is the decoded GeoJSON feature. useFeature(hit.feature);}var box = new ScreenBox( new ScreenPoint(point.X - 5, point.Y - 5), new ScreenPoint(point.X + 5, point.Y + 5));var hits = await session.QueryRenderedFeaturesAsync( new RenderedQueryGeometry.Box(box), new RenderedFeatureQueryOptions { LayerIds = ["poi"] });foreach (var hit in hits){ // hit.Feature is GeoJSON text. UseFeature(hit.Feature);}box := maplibre.RenderedQueryGeometryBoxVariant{ Value: maplibre.ScreenBox{ Min: maplibre.ScreenPoint{X: point.X - 5, Y: point.Y - 5}, Max: maplibre.ScreenPoint{X: point.X + 5, Y: point.Y + 5}, },}rendered, err := session.QueryRenderedFeatures( box, &maplibre.RenderedFeatureQueryOptions{ LayerIDs: []string{"poi"}, },)if err != nil { return err}hits, err := rendered.Await(ctx)if err != nil { return err}for _, hit := range hits { // hit.Feature is the feature's GeoJSON. useFeature(hit.Feature)}ScreenBox box = { { point.x - 5, point.y - 5 }, { point.x + 5, point.y + 5 }};var options = new RenderedFeatureQueryOptions () { layer_ids = { "poi" },};var hits = yield session.query_rendered_features_async ( new RenderedQueryGeometry.box (box), options);foreach (var hit in hits) { // The feature is GeoJSON text. use_feature (hit.get_feature ());}List<RenderedFeature> hits = session .queryRenderedFeatures( new RenderedQueryGeometry.Box( new ScreenBox( new ScreenPoint( point.getX() - 5, point.getY() - 5), new ScreenPoint( point.getX() + 5, point.getY() + 5))), RenderedFeatureQueryOptions.of() .withLayerIds(List.of("poi"))) .join();for (RenderedFeature hit : hits) { // hit.getFeature() is the GeoJSON feature, as JSON. useFeature(hit.getFeature());}val hits = session .queryRenderedFeatures( RenderedQueryGeometry.Box( ScreenBox( min = ScreenPoint(x = point.x - 5, y = point.y - 5), max = ScreenPoint(x = point.x + 5, y = point.y + 5), ), ), RenderedFeatureQueryOptions(layerIds = listOf("poi")), ) .await()for (hit in hits) { // hit.feature is the GeoJSON feature, as JSON. useFeature(hit.feature)}hits = session.query_rendered_features( mln.RenderedQueryGeometryBoxVariant( mln.ScreenBox( min=mln.ScreenPoint(x=point.x - 5, y=point.y - 5), max=mln.ScreenPoint(x=point.x + 5, y=point.y + 5), ) ), mln.RenderedFeatureQueryOptions(layer_ids=["poi"]),).result()for hit in hits: # hit.feature is the parsed GeoJSON feature. use_feature(hit.feature)let area = RenderedQueryGeometry::Box(ScreenBox { min: ScreenPoint::new(point.x - 5.0, point.y - 5.0), max: ScreenPoint::new(point.x + 5.0, point.y + 5.0),});let options = RenderedFeatureQueryOptions { layer_ids: Some(&["poi"]), ..Default::default()};
let hits = session .query_rendered_features(&area, Some(&options))? .await?;for hit in hits { // hit.feature is the GeoJSON feature, as JSON. use_feature(&hit.feature);}let hits = try await session.queryRenderedFeatures( geometry: .box(ScreenBox( min: ScreenPoint(x: point.x - 5, y: point.y - 5), max: ScreenPoint(x: point.x + 5, y: point.y + 5) )), options: RenderedFeatureQueryOptions(layerIds: ["poi"]))for hit in hits { // hit.feature is the GeoJSON feature, as JSON. useFeature(hit.feature)}const hits = await session.queryRenderedFeatures( { kind: "box", value: { min: { x: point.x - 5, y: point.y - 5 }, max: { x: point.x + 5, y: point.y + 5 }, }, }, { layerIds: ["poi"] },);for (const hit of hits) { // hit.feature is the parsed GeoJSON feature. useFeature(hit.feature);}const rendered = try session.queryRenderedFeatures( allocator, .{ .box = .{ .min = .{ .x = point.x - 5, .y = point.y - 5 }, .max = .{ .x = point.x + 5, .y = point.y + 5 }, } }, .{ .layer_ids = &.{"poi"} }, null,);var hits = try rendered.await(null);defer hits.deinit();
for (hits.value) |hit| { // hit.feature is the GeoJSON feature, as JSON. useFeature(hit.feature);}Query source features
Section titled “Query source features”To search a source’s data instead of what’s drawn, query the source by its ID
with mln_render_session_query_source_featuresRenderSessionHandle.querySourceFeaturesRenderSessionHandle.QuerySourceFeaturesAsyncRenderSessionHandle.QuerySourceFeaturesmaplibre_native_ffi_render_session_query_source_features_asyncRenderSessionHandle.querySourceFeaturesRenderSessionHandle.querySourceFeaturesRenderSessionHandle.query_source_featuresRenderSessionHandle::query_source_featuresRenderSessionHandle.querySourceFeaturesRenderSessionHandle.querySourceFeaturesRenderSession.querySourceFeatures, and for a vector source,
name the source layers. To keep only some features, pass a
filter.
A source query only sees the tiles that the source has loaded for the current view, and a source loads tiles only while a visible layer uses it.
static void on_source( void* user_data, const mln_completion_result* result) { if (result->status == MLN_STATUS_OK) { for (size_t i = 0; i < result->value_count; i++) { const mln_source_feature* hit = MLN_STRIDED_AT( mln_source_feature, result->value, result->value_size, i ); copy_feature(hit->feature); } } on_done(user_data, result);}
const mln_buffer_view source_layers[] = { mln_buffer_view_from_cstr("transportation")};const mln_source_feature_query_options source_options = { .size = sizeof(mln_source_feature_query_options), .fields = MLN_SOURCE_FEATURE_QUERY_OPTION_SOURCE_LAYER_IDS, .source_layer_ids = source_layers, .source_layer_id_count = 1, // A filter from the MapLibre style spec, as JSON. .filter = mln_buffer_view_from_cstr( "[\"==\", [\"get\", \"class\"], \"motorway\"]" ),};const mln_completion source = { .size = sizeof(mln_completion), .callback = on_source};mln_render_session_query_source_features( session, mln_buffer_view_from_cstr("openmaptiles"), &source_options, &source, NULL);final features = await session.querySourceFeatures( 'openmaptiles', options: SourceFeatureQueryOptions( sourceLayerIds: ['transportation'], // A filter from the MapLibre style spec. filter: [ '==', ['get', 'class'], 'motorway', ], ),);for (final feature in features) { useFeature(feature.feature);}var features = await session.QuerySourceFeaturesAsync( "openmaptiles", new SourceFeatureQueryOptions { SourceLayerIds = ["transportation"], // A filter from the MapLibre style spec, as JSON. Filter = """["==", ["get", "class"], "motorway"]""", });foreach (var feature in features){ UseFeature(feature.Feature);}source, err := session.QuerySourceFeatures( "openmaptiles", &maplibre.SourceFeatureQueryOptions{ SourceLayerIDs: []string{"transportation"}, // A filter from the MapLibre style spec, as JSON. Filter: `["==", ["get", "class"], "motorway"]`, },)if err != nil { return err}features, err := source.Await(ctx)if err != nil { return err}for _, feature := range features { useFeature(feature.Feature)}var filter = new SourceFeatureQueryOptions () { source_layer_ids = { "transportation" }, // A filter from the MapLibre style spec. filter = "[\"==\", [\"get\", \"class\"], \"motorway\"]",};var features = yield session.query_source_features_async ( "openmaptiles", filter);foreach (var feature in features) { use_feature (feature.get_feature ());}List<SourceFeature> features = session .querySourceFeatures( "openmaptiles", SourceFeatureQueryOptions.of() .withSourceLayerIds(List.of("transportation")) // A filter from the MapLibre style spec. .withFilter( """ ["==", ["get", "class"], "motorway"] """)) .join();for (SourceFeature feature : features) { useFeature(feature.getFeature());}val features = session .querySourceFeatures( "openmaptiles", SourceFeatureQueryOptions( sourceLayerIds = listOf("transportation"), // A filter from the MapLibre style spec. filter = """["==", ["get", "class"], "motorway"]""", ), ) .await()for (feature in features) { useFeature(feature.feature)}features = session.query_source_features( "openmaptiles", mln.SourceFeatureQueryOptions( source_layer_ids=["transportation"], # A filter from the MapLibre style spec. filter=["==", ["get", "class"], "motorway"], ),).result()for feature in features: use_feature(feature.feature)let options = SourceFeatureQueryOptions { source_layer_ids: Some(&["transportation"]), // A filter from the MapLibre style spec, as JSON. filter: r#"["==", ["get", "class"], "motorway"]"#,};
let features = session .query_source_features("openmaptiles", Some(&options))? .await?;for feature in features { use_feature(&feature.feature);}let features = try await session.querySourceFeatures( "openmaptiles", options: SourceFeatureQueryOptions( sourceLayerIds: ["transportation"], // A filter from the MapLibre style spec, as JSON. filter: #"["==", ["get", "class"], "motorway"]"# ))for feature in features { useFeature(feature.feature)}const features = await session.querySourceFeatures("openmaptiles", { sourceLayerIds: ["transportation"], // A filter from the MapLibre style spec. filter: ["==", ["get", "class"], "motorway"],});for (const feature of features) { useFeature(feature.feature);}const source = try session.querySourceFeatures( allocator, "openmaptiles", .{ .source_layer_ids = &.{"transportation"}, // A filter from the MapLibre style spec, as JSON. .filter = "[\"==\", [\"get\", \"class\"], \"motorway\"]", }, null,);var features = try source.await(null);defer features.deinit();
for (features.value) |feature| { useFeature(feature.feature);}