Skip to content

Query a map

A rendered feature query finds features in the last frame at a screen position. Use it for hit testing. To read source features that no layer draws, use Query a source.

A rendered query belongs to the render session and runs on the session’s owner thread. It reads the last frame that the session drew. A query before the first render update reports an invalid-state status.

The query uses the last frame that the session drew. Render after a camera move or style change when the result must match the new state. A query finds features after the relevant tiles load and render.

Screen coordinates are logical map pixels measured from the top-left of the viewport. The map’s coordinate-to-pixel query uses the same space. A host that tracks positions in physical device pixels divides them by the render session’s scale factor.

The geometry states where to search. A point matches the exact position, and a box matches a rectangle around it. A hit test uses a box with a few logical pixels of tolerance around the position, because symbols and thin lines are small targets. Box corners come in any order, and MapLibre clips the box to the viewport.

query-rendered-features.c
const mln_rendered_query_geometry geometry =
mln_rendered_query_geometry_box((mln_screen_box){
.min = {.x = at.x - 6.0, .y = at.y - 6.0},
.max = {.x = at.x + 6.0, .y = at.y + 6.0},
});

Naming the layer IDs that your host treats as selectable keeps background and land fills out of the result.

query-rendered-features.c
const mln_buffer_view layer_ids[] = {
view("poi-labels"), view("building-fill")
};
mln_rendered_feature_query_options options =
mln_rendered_feature_query_options_default();
options.fields = MLN_RENDERED_FEATURE_QUERY_OPTION_LAYER_IDS;
options.layer_ids = layer_ids;
options.layer_id_count = sizeof(layer_ids) / sizeof(layer_ids[0]);

On success, the query returns queried features. Copy every value that the host keeps.

query-rendered-features.c
mln_queried_feature_list result = MLN_HANDLE_NULL;
const mln_status queried = mln_render_session_query_rendered_features(
session, &geometry, &options, &result
);
if (queried != MLN_STATUS_OK) return queried;
read_query_result(result);
mln_queried_feature_list_destroy(result);
return MLN_STATUS_OK;

Each hit is a GeoJSON Feature plus optional source ID, source-layer ID, and feature state.

query-rendered-features.c
size_t count = 0;
if (mln_queried_feature_list_count(result, &count) != MLN_STATUS_OK) return;
if (count == 0) return;
mln_queried_feature hit = mln_queried_feature_default();
if (mln_queried_feature_list_get(result, 0, &hit) != MLN_STATUS_OK) return;
// Copy hit.feature and any identifier or state view you keep.