Skip to content

Camera

This guide shows how to read and change a map’s camera, from your code and from gestures. A mln_cameraCameraCameraCameraMaplibreNativeFfiCameraCameraCameraCameraCameraCameraCameraCamera has a center, a zoom, a bearing, and a pitch.

Read the camera from the map’s snapshot with mln_map_get_snapshotMapHandle.getSnapshotMapHandle.GetSnapshotMapHandle.Snapshotmaplibre_native_ffi_map_get_snapshotMapHandle.getSnapshotMapHandle.getSnapshotMapHandle.get_snapshotMapHandle::get_snapshotMapHandle.getSnapshotMapHandle.getSnapshotMap.getSnapshot. To read it after your pending changes apply, use mln_map_get_cameraMapHandle.getCameraMapHandle.GetCameraAsyncMapHandle.GetCameramaplibre_native_ffi_map_get_camera_asyncMapHandle.getCameraMapHandle.getCameraMapHandle.get_cameraMapHandle::get_cameraMapHandle.getCameraMapHandle.getCameraMap.getCamera instead.

main.c
static void on_camera(
void* user_data, const mln_completion_result* result
) {
if (result->status == MLN_STATUS_OK) {
const mln_camera_query_result* query = result->value;
use_camera(&query->camera);
}
on_done(user_data, result);
}
mln_map_snapshot snapshot = {.size = sizeof(mln_map_snapshot)};
mln_map_get_snapshot(map, &snapshot, NULL);
use_camera(&snapshot.view.camera);
const mln_completion read = {
.size = sizeof(mln_completion), .callback = on_camera
};
mln_map_get_camera(map, &read, NULL);
main.dart
final snapshot = map.getSnapshot();
useCamera(snapshot.view.camera);
final result = await map.getCamera();
useCamera(result.camera);
Program.cs
var snapshot = map.GetSnapshot();
UseCamera(snapshot.View.Camera);
var result = await map.GetCameraAsync();
UseCamera(result.Camera);
main.go
snapshot, err := m.Snapshot()
if err != nil {
return err
}
useCamera(snapshot.View.Camera)
query, err := m.GetCamera()
if err != nil {
return err
}
result, err := query.Await(ctx)
if err != nil {
return err
}
useCamera(result.Camera)
main.vala
var snapshot = map.get_snapshot ();
use_camera (snapshot.view.camera);
var result = yield map.get_camera_async ();
use_camera (result.camera);
Main.java
MapSnapshot snapshot = map.getSnapshot();
useCamera(snapshot.getView().getCamera());
CameraQueryResult result = map.getCamera().join();
useCamera(result.getCamera());
Main.kt
val snapshot = map.getSnapshot()
useCamera(snapshot.view.camera)
val result = map.getCamera().await()
useCamera(result.camera)
main.py
snapshot = map_handle.get_snapshot()
use_camera(snapshot.view.camera)
result = map_handle.get_camera().result()
use_camera(result.camera)
main.rs
let snapshot = map.get_snapshot()?;
use_camera(&snapshot.view.camera);
let result = map.get_camera()?.await?;
use_camera(&result.camera);
main.swift
let snapshot = try map.getSnapshot()
useCamera(snapshot.view.camera)
let result = try await map.getCamera()
useCamera(result.camera)
main.ts
const snapshot = map.getSnapshot();
useCamera(snapshot.view.camera);
const result = await map.getCamera();
useCamera(result.camera);
main.zig
const snapshot = try map.getSnapshot(null);
useCamera(snapshot.view.camera);
const result = try (try map.getCamera(null)).await(null);
useCamera(result.camera);

To move the camera, send a camera update with mln_map_update_cameraMapHandle.updateCameraMapHandle.UpdateCameraAsyncMapHandle.UpdateCameramaplibre_native_ffi_map_update_camera_asyncMapHandle.updateCameraMapHandle.updateCameraMapHandle.update_cameraMapHandle::update_cameraMapHandle.updateCameraMapHandle.updateCameraMap.updateCamera in jump mode. Set only the fields you want to change.

main.c
const mln_camera_update jump = {
.size = sizeof(mln_camera_update),
.mode = MLN_CAMERA_UPDATE_MODE_JUMP,
.camera = {
.size = sizeof(mln_camera_options),
.fields = MLN_CAMERA_OPTION_CENTER | MLN_CAMERA_OPTION_ZOOM,
.center = {.latitude = 48.8566, .longitude = 2.3522},
.zoom = 12,
},
};
mln_map_update_camera(map, &jump, NULL, NULL);
main.dart
map.discarding.updateCamera(
const CameraUpdate(
mode: CameraUpdateMode.jump,
camera: CameraOptions(
center: LatLng(latitude: 48.8566, longitude: 2.3522),
zoom: 12,
),
),
);
Program.cs
map.Discarding.UpdateCamera(
new CameraUpdate
{
Mode = CameraUpdateMode.Jump,
Camera = new CameraOptions
{
Center = new LatLng(48.8566, 2.3522),
Zoom = 12,
},
}
);
main.go
err = m.Discarding().UpdateCamera(maplibre.CameraUpdate{
Mode: maplibre.CameraUpdateModeJump,
Camera: maplibre.CameraOptions{
Center: &maplibre.LatLng{Latitude: 48.8566, Longitude: 2.3522},
Zoom: new(12.0),
},
})
if err != nil {
return err
}
main.vala
var paris = new CameraOptions () {
center = { latitude: 48.8566, longitude: 2.3522 },
zoom = 12,
};
var jump = new CameraUpdate () {
mode = CameraUpdateMode.JUMP,
camera = paris,
};
map.update_camera_async.begin (jump);
Main.java
map.getDiscarding()
.updateCamera(
CameraUpdate.of()
.withMode(CameraUpdateMode.JUMP)
.withCamera(
CameraOptions.of()
.withCenter(new LatLng(48.8566, 2.3522))
.withZoom(12.0)));
Main.kt
map.discarding.updateCamera(
CameraUpdate(
mode = CameraUpdateMode.JUMP,
camera =
CameraOptions(
center = LatLng(latitude = 48.8566, longitude = 2.3522),
zoom = 12.0,
),
),
)
main.py
map_handle.discarding.update_camera(
mln.CameraUpdate(
mode=mln.CameraUpdateMode.JUMP,
camera=mln.CameraOptions(
center=mln.LatLng(latitude=48.8566, longitude=2.3522),
zoom=12,
),
)
)
main.rs
map.discarding().update_camera(&CameraUpdate {
mode: CameraUpdateMode::Jump,
camera: CameraOptions {
center: Some(LatLng::new(48.8566, 2.3522)),
zoom: Some(12.0),
..Default::default()
},
..Default::default()
})?;
main.swift
try map.discarding.updateCamera(CameraUpdate(
mode: .jump,
camera: CameraOptions(
center: LatLng(latitude: 48.8566, longitude: 2.3522),
zoom: 12
)
))
main.ts
map.discarding.updateCamera({
mode: CameraUpdateMode.jump,
camera: {
center: { latitude: 48.8566, longitude: 2.3522 },
zoom: 12,
},
});
main.zig
try map.discarding().updateCamera(.{
.mode = .jump,
.camera = .{
.center = .{ .latitude = 48.8566, .longitude = 2.3522 },
.zoom = 12,
},
}, null);

To animate, send the update in ease or fly mode with a duration. Ease moves straight to the target, while fly zooms out, travels, and zooms back in.

To act when an animation ends, set a mln_camera_transition_handlerCameraTransitionHandlerCameraTransitionHandlerCameraTransitionHandlerMaplibreNativeFfiCameraTransitionHandlerCameraTransitionHandlerCameraTransitionHandlerCameraTransitionHandlerCameraTransitionHandlerCameraTransitionHandlerCameraTransitionHandlerCameraTransitionHandler.

To stop an animation where it is, cancel the map’s camera transitions with mln_map_cancel_camera_transitionsMapHandle.cancelCameraTransitionsMapHandle.CancelCameraTransitionsAsyncMapHandle.CancelCameraTransitionsmaplibre_native_ffi_map_cancel_camera_transitions_asyncMapHandle.cancelCameraTransitionsMapHandle.cancelCameraTransitionsMapHandle.cancel_camera_transitionsMapHandle::cancel_camera_transitionsMapHandle.cancelCameraTransitionsMapHandle.cancelCameraTransitionsMap.cancelCameraTransitions.

main.c
static void on_fly_end(
void* user_data, const mln_camera_transition_end* end
) {
(void)user_data;
// Runs on a library thread.
fly_ended(end->outcome == MLN_CAMERA_TRANSITION_OUTCOME_COMPLETED);
}
const mln_camera_update fly = {
.size = sizeof(mln_camera_update),
.mode = MLN_CAMERA_UPDATE_MODE_FLY,
.camera =
{
.size = sizeof(mln_camera_options),
.fields = MLN_CAMERA_OPTION_CENTER | MLN_CAMERA_OPTION_ZOOM,
.center = {.latitude = 40.7128, .longitude = -74.0060},
.zoom = 10,
},
.animation = {
.fields = MLN_ANIMATION_OPTION_DURATION,
.duration_ms = 3000,
.end_handler = {.callback = on_fly_end},
},
};
mln_map_update_camera(map, &fly, NULL, NULL);
main.dart
map.discarding.updateCamera(
CameraUpdate(
mode: CameraUpdateMode.fly,
camera: const CameraOptions(
center: LatLng(latitude: 40.7128, longitude: -74.006),
zoom: 10,
),
animation: AnimationOptions(
durationMs: 3000,
// Runs in this isolate, after the animation ends.
endHandler: CameraTransitionHandler(
callback: (end) {
flyEnded(
end.outcome == CameraTransitionOutcome.completed,
);
},
),
),
),
);
Program.cs
map.Discarding.UpdateCamera(
new CameraUpdate
{
Mode = CameraUpdateMode.Fly,
Camera = new CameraOptions
{
Center = new LatLng(40.7128, -74.0060),
Zoom = 10,
},
Animation = new AnimationOptions
{
DurationMs = 3000,
// Runs on a library thread.
EndHandler = new CameraTransitionHandler(end =>
FlyEnded(
end.Outcome
== CameraTransitionOutcome.Completed
)
),
},
}
);
main.go
err = m.Discarding().UpdateCamera(maplibre.CameraUpdate{
Mode: maplibre.CameraUpdateModeFly,
Camera: maplibre.CameraOptions{
Center: &maplibre.LatLng{Latitude: 40.7128, Longitude: -74.006},
Zoom: new(10.0),
},
Animation: maplibre.AnimationOptions{
DurationMs: new(3000.0),
EndHandler: maplibre.CameraTransitionHandler{
// Runs on a library thread.
Callback: func(end maplibre.CameraTransitionEnd) {
flyEnded(
end.Outcome == maplibre.CameraTransitionOutcomeCompleted,
)
},
},
},
})
if err != nil {
return err
}
main.vala
var end_handler = new CameraTransitionHandler ();
// Runs on the main context of the thread that started the
// update.
end_handler.set_callback ((end) => {
fly_ended (
end.outcome == CameraTransitionOutcome.COMPLETED
);
});
var animation = new AnimationOptions () {
duration_ms = 3000,
end_handler = end_handler,
};
var new_york = new CameraOptions () {
center = { latitude: 40.7128, longitude: -74.006 },
zoom = 10,
};
var fly = new CameraUpdate () {
mode = CameraUpdateMode.FLY,
camera = new_york,
animation = animation,
};
map.update_camera_async.begin (fly);
Main.java
// Runs after the animation ends, on a library thread.
CameraTransitionHandler onEnd =
new CameraTransitionHandler(
end -> {
CameraTransitionOutcome outcome = end.getOutcome();
flyEnded(
outcome.equals(
CameraTransitionOutcome.COMPLETED));
});
// An ID to cancel this animation by.
long flight = 1;
map.getDiscarding()
.updateCamera(
CameraUpdate.of()
.withMode(CameraUpdateMode.FLY)
.withCamera(
CameraOptions.of()
.withCenter(new LatLng(40.7128, -74.006))
.withZoom(10.0))
.withAnimation(
AnimationOptions.of()
.withDurationMs(3000.0)
.withTransitionId(flight)
.withEndHandler(onEnd)));
// Later, stop the flight where it is.
map.getDiscarding().cancelCameraTransitionsWithId(flight);
Main.kt
map.discarding.updateCamera(
CameraUpdate(
mode = CameraUpdateMode.FLY,
camera =
CameraOptions(
center = LatLng(latitude = 40.7128, longitude = -74.006),
zoom = 10.0,
),
animation =
AnimationOptions(
durationMs = 3000.0,
// Runs after the animation ends, on a library thread, or on
// the page in Kotlin/Wasm.
endHandler =
CameraTransitionHandler { end ->
flyEnded(
end.outcome == CameraTransitionOutcome.COMPLETED,
)
},
),
),
)
main.py
# Runs on the binding's dispatcher thread.
def on_fly_end(end: mln.CameraTransitionEnd) -> None:
fly_ended(
end.outcome == mln.CameraTransitionOutcome.COMPLETED
)
map_handle.discarding.update_camera(
mln.CameraUpdate(
mode=mln.CameraUpdateMode.FLY,
camera=mln.CameraOptions(
center=mln.LatLng(
latitude=40.7128, longitude=-74.006
),
zoom=10,
),
animation=mln.AnimationOptions(
duration_ms=3000,
end_handler=mln.CameraTransitionHandler(on_fly_end),
),
)
)
main.rs
map.discarding().update_camera(&CameraUpdate {
mode: CameraUpdateMode::Fly,
camera: CameraOptions {
center: Some(LatLng::new(40.7128, -74.006)),
zoom: Some(10.0),
..Default::default()
},
animation: AnimationOptions {
duration_ms: Some(3000.0),
// Runs on a library thread.
end_handler: CameraTransitionHandler::new(|end| {
fly_ended(
end.outcome == CameraTransitionOutcome::Completed,
);
}),
..Default::default()
},
..Default::default()
})?;
main.swift
try map.discarding.updateCamera(CameraUpdate(
mode: .fly,
camera: CameraOptions(
center: LatLng(latitude: 40.7128, longitude: -74.006),
zoom: 10
),
animation: AnimationOptions(
durationMs: 3000,
// Runs on a library thread, after the animation ends.
endHandler: CameraTransitionHandler { end in
flyEnded(end.outcome == .completed)
}
)
))
main.ts
map.discarding.updateCamera({
mode: CameraUpdateMode.fly,
camera: {
center: { latitude: 40.7128, longitude: -74.006 },
zoom: 10,
},
animation: {
durationMs: 3000,
// Runs on this thread, after the animation ends.
endHandler: {
callback: (end) => {
flyEnded(end.outcome === CameraTransitionOutcome.completed);
},
},
},
});
main.zig
// Runs on a library thread.
fn onFlyEnd(
_: ?*anyopaque,
end: maplibre.CameraTransitionEnd,
) anyerror!void {
flyEnded(end.outcome == .completed);
}
try map.discarding().updateCamera(.{
.mode = .fly,
.camera = .{
.center = .{ .latitude = 40.7128, .longitude = -74.0060 },
.zoom = 10,
},
.animation = .{
.duration_ms = 3000,
.end_handler = .{ .callback = onFlyEnd },
},
}, null);

To show an area, fit the camera to its bounding box with mln_map_fit_camera_to_lat_lng_boundsMapHandle.fitCameraToLatLngBoundsMapHandle.FitCameraToLatLngBoundsAsyncMapHandle.FitCameraToLatLngBoundsmaplibre_native_ffi_map_fit_camera_to_lat_lng_bounds_asyncMapHandle.fitCameraToLatLngBoundsMapHandle.fitCameraToLatLngBoundsMapHandle.fit_camera_to_lat_lng_boundsMapHandle::fit_camera_to_lat_lng_boundsMapHandle.fitCameraToLatLngBoundsMapHandle.fitCameraToLatLngBoundsMap.fitCameraToLatLngBounds, at once or with an animation. For a list of coordinates or a GeoJSON geometry, use mln_map_fit_camera_to_lat_lngsMapHandle.fitCameraToLatLngsMapHandle.FitCameraToLatLngsAsyncMapHandle.FitCameraToLatLngsmaplibre_native_ffi_map_fit_camera_to_lat_lngs_asyncMapHandle.fitCameraToLatLngsMapHandle.fitCameraToLatLngsMapHandle.fit_camera_to_lat_lngsMapHandle::fit_camera_to_lat_lngsMapHandle.fitCameraToLatLngsMapHandle.fitCameraToLatLngsMap.fitCameraToLatLngs or mln_map_fit_camera_to_geometryMapHandle.fitCameraToGeometryMapHandle.FitCameraToGeometryAsyncMapHandle.FitCameraToGeometrymaplibre_native_ffi_map_fit_camera_to_geometry_asyncMapHandle.fitCameraToGeometryMapHandle.fitCameraToGeometryMapHandle.fit_camera_to_geometryMapHandle::fit_camera_to_geometryMapHandle.fitCameraToGeometryMapHandle.fitCameraToGeometryMap.fitCameraToGeometry instead.

To get the camera without moving to it, use mln_map_camera_for_lat_lng_boundsMapHandle.cameraForLatLngBoundsMapHandle.CameraForLatLngBoundsAsyncMapHandle.CameraForLatLngBoundsmaplibre_native_ffi_map_camera_for_lat_lng_bounds_asyncMapHandle.cameraForLatLngBoundsMapHandle.cameraForLatLngBoundsMapHandle.camera_for_lat_lng_boundsMapHandle::camera_for_lat_lng_boundsMapHandle.cameraForLatLngBoundsMapHandle.cameraForLatLngBoundsMap.cameraForLatLngBounds, mln_map_camera_for_lat_lngsMapHandle.cameraForLatLngsMapHandle.CameraForLatLngsAsyncMapHandle.CameraForLatLngsmaplibre_native_ffi_map_camera_for_lat_lngs_asyncMapHandle.cameraForLatLngsMapHandle.cameraForLatLngsMapHandle.camera_for_lat_lngsMapHandle::camera_for_lat_lngsMapHandle.cameraForLatLngsMapHandle.cameraForLatLngsMap.cameraForLatLngs, or mln_map_camera_for_geometryMapHandle.cameraForGeometryMapHandle.CameraForGeometryAsyncMapHandle.CameraForGeometrymaplibre_native_ffi_map_camera_for_geometry_asyncMapHandle.cameraForGeometryMapHandle.cameraForGeometryMapHandle.camera_for_geometryMapHandle::camera_for_geometryMapHandle.cameraForGeometryMapHandle.cameraForGeometryMap.cameraForGeometry.

main.c
const mln_lat_lng_bounds bounds = {
.southwest = {.latitude = 45.8, .longitude = 5.9},
.northeast = {.latitude = 47.8, .longitude = 10.5},
};
const mln_camera_fit_update fit = {
.size = sizeof(mln_camera_fit_update),
.mode = MLN_CAMERA_UPDATE_MODE_EASE,
.animation = {
.fields = MLN_ANIMATION_OPTION_DURATION, .duration_ms = 500
},
};
mln_map_fit_camera_to_lat_lng_bounds(map, bounds, &fit, NULL, NULL);
main.dart
const bounds = LatLngBounds(
southwest: LatLng(latitude: 45.8, longitude: 5.9),
northeast: LatLng(latitude: 47.8, longitude: 10.5),
);
map.discarding.fitCameraToLatLngBounds(
bounds,
const CameraFitUpdate(
mode: CameraUpdateMode.ease,
animation: AnimationOptions(durationMs: 500),
),
);
Program.cs
var bounds = new LatLngBounds(
new LatLng(45.8, 5.9),
new LatLng(47.8, 10.5)
);
map.Discarding.FitCameraToLatLngBounds(
bounds,
new CameraFitUpdate
{
Mode = CameraUpdateMode.Ease,
Animation = new AnimationOptions { DurationMs = 500 },
}
);
main.go
bounds := maplibre.LatLngBounds{
Southwest: maplibre.LatLng{Latitude: 45.8, Longitude: 5.9},
Northeast: maplibre.LatLng{Latitude: 47.8, Longitude: 10.5},
}
err = m.Discarding().FitCameraToLatLngBounds(bounds, maplibre.CameraFitUpdate{
Mode: maplibre.CameraUpdateModeEase,
Animation: maplibre.AnimationOptions{DurationMs: new(500.0)},
})
if err != nil {
return err
}
main.vala
LatLngBounds bounds = {
{ latitude: 45.8, longitude: 5.9 },
{ latitude: 47.8, longitude: 10.5 },
};
var ease = new AnimationOptions () { duration_ms = 500 };
var fit = new CameraFitUpdate () {
mode = CameraUpdateMode.EASE,
animation = ease,
};
map.fit_camera_to_lat_lng_bounds_async.begin (bounds, fit);
Main.java
LatLngBounds bounds =
new LatLngBounds(
new LatLng(45.8, 5.9), new LatLng(47.8, 10.5));
map.getDiscarding()
.fitCameraToLatLngBounds(
bounds,
CameraFitUpdate.of()
.withMode(CameraUpdateMode.EASE)
.withAnimation(
AnimationOptions.of().withDurationMs(500.0)));
Main.kt
val bounds =
LatLngBounds(
southwest = LatLng(latitude = 45.8, longitude = 5.9),
northeast = LatLng(latitude = 47.8, longitude = 10.5),
)
map.discarding.fitCameraToLatLngBounds(
bounds,
CameraFitUpdate(
mode = CameraUpdateMode.EASE,
animation = AnimationOptions(durationMs = 500.0),
),
)
main.py
bounds = mln.LatLngBounds(
southwest=mln.LatLng(latitude=45.8, longitude=5.9),
northeast=mln.LatLng(latitude=47.8, longitude=10.5),
)
map_handle.discarding.fit_camera_to_lat_lng_bounds(
bounds,
mln.CameraFitUpdate(
mode=mln.CameraUpdateMode.EASE,
animation=mln.AnimationOptions(duration_ms=500),
),
)
main.rs
let bounds = LatLngBounds {
southwest: LatLng::new(45.8, 5.9),
northeast: LatLng::new(47.8, 10.5),
};
map.discarding().fit_camera_to_lat_lng_bounds(
bounds,
&CameraFitUpdate {
mode: CameraUpdateMode::Ease,
animation: AnimationOptions {
duration_ms: Some(500.0),
..Default::default()
},
..Default::default()
},
)?;
main.swift
let bounds = LatLngBounds(
southwest: LatLng(latitude: 45.8, longitude: 5.9),
northeast: LatLng(latitude: 47.8, longitude: 10.5)
)
try map.discarding.fitCameraToLatLngBounds(
bounds,
update: CameraFitUpdate(
mode: .ease,
animation: AnimationOptions(durationMs: 500)
)
)
main.ts
const bounds = {
southwest: { latitude: 45.8, longitude: 5.9 },
northeast: { latitude: 47.8, longitude: 10.5 },
};
map.discarding.fitCameraToLatLngBounds(bounds, {
mode: CameraUpdateMode.ease,
animation: { durationMs: 500 },
});
main.zig
const bounds = maplibre.LatLngBounds{
.southwest = .{ .latitude = 45.8, .longitude = 5.9 },
.northeast = .{ .latitude = 47.8, .longitude = 10.5 },
};
try map.discarding().fitCameraToLatLngBounds(bounds, .{
.mode = .ease,
.animation = .{ .duration_ms = 500 },
}, null);

To keep the camera within an area, set the map’s bounds with mln_map_set_boundsMapHandle.setBoundsMapHandle.SetBoundsAsyncMapHandle.SetBoundsmaplibre_native_ffi_map_set_bounds_asyncMapHandle.setBoundsMapHandle.setBoundsMapHandle.set_boundsMapHandle::set_boundsMapHandle.setBoundsMapHandle.setBoundsMap.setBounds. Bounds limit the center to an area, and the zoom and pitch to ranges.

main.c
const mln_bound_options limits = {
.size = sizeof(mln_bound_options),
.fields = MLN_BOUND_OPTION_BOUNDS | MLN_BOUND_OPTION_MIN_ZOOM |
MLN_BOUND_OPTION_MAX_ZOOM,
.bounds = bounds,
.min_zoom = 5,
.max_zoom = 16,
};
mln_map_set_bounds(map, &limits, NULL, NULL);
main.dart
map.discarding.setBounds(
const BoundOptions(bounds: bounds, minZoom: 5, maxZoom: 16),
);
Program.cs
map.Discarding.SetBounds(
new BoundOptions
{
Bounds = bounds,
MinZoom = 5,
MaxZoom = 16,
}
);
main.go
err = m.Discarding().SetBounds(maplibre.BoundOptions{
Bounds: &bounds,
MinZoom: new(5.0),
MaxZoom: new(16.0),
})
if err != nil {
return err
}
main.vala
var limits = new BoundOptions () {
bounds = bounds,
min_zoom = 5,
max_zoom = 16,
};
map.set_bounds_async.begin (limits);
Main.java
map.getDiscarding()
.setBounds(
BoundOptions.of()
.withBounds(bounds)
.withMinZoom(5.0)
.withMaxZoom(16.0));
Main.kt
map.discarding.setBounds(
BoundOptions(bounds = bounds, minZoom = 5.0, maxZoom = 16.0),
)
main.py
map_handle.discarding.set_bounds(
mln.BoundOptions(bounds=bounds, min_zoom=5, max_zoom=16)
)
main.rs
map.discarding().set_bounds(&BoundOptions {
bounds: Some(bounds),
min_zoom: Some(5.0),
max_zoom: Some(16.0),
..Default::default()
})?;
main.swift
try map.discarding.setBounds(
options: BoundOptions(bounds: bounds, minZoom: 5, maxZoom: 16)
)
main.ts
map.discarding.setBounds({ bounds, minZoom: 5, maxZoom: 16 });
main.zig
try map.discarding().setBounds(.{
.bounds = bounds,
.min_zoom = 5,
.max_zoom = 16,
}, null);

The library doesn’t handle input, so your app recognizes gestures from its own input events and applies them to the camera as deltas. Screen points are in logical pixels from the map’s top-left corner.

When a gesture starts, send a delta with mln_map_apply_camera_deltaMapHandle.applyCameraDeltaMapHandle.ApplyCameraDeltaAsyncMapHandle.ApplyCameraDeltamaplibre_native_ffi_map_apply_camera_delta_asyncMapHandle.applyCameraDeltaMapHandle.applyCameraDeltaMapHandle.apply_camera_deltaMapHandle::apply_camera_deltaMapHandle.applyCameraDeltaMapHandle.applyCameraDeltaMap.applyCameraDelta in the begin phase, which stops running camera animations. For each movement, send a delta in the update phase with the change since the last one: the movement as the offset for a drag, or the scale and the pinch’s center as the anchor for a pinch. When the gesture ends, send the end phase, or the cancel phase if the platform cancels it.

To keep the camera moving after the user flicks the map, give the end-phase delta a mln_gesture_momentumGestureMomentumGestureMomentumGestureMomentumMaplibreNativeFfiGestureMomentumGestureMomentumGestureMomentumGestureMomentumGestureMomentumGestureMomentumGestureMomentumGestureMomentum with the gesture’s release velocities. The map slows each part of the motion on its own and keeps the gesture in progress until it stops. A begin-phase delta stops it early. Its options set how quickly it slows, so it can match how your platform scrolls.

main.c
static void begin_gesture(mln_map map) {
const mln_camera_delta begin = {
.size = sizeof(mln_camera_delta),
.gesture_phase = MLN_GESTURE_PHASE_BEGIN,
};
mln_map_apply_camera_delta(map, &begin, NULL, NULL);
}
static void apply_drag(mln_map map, mln_screen_point movement) {
const mln_camera_delta drag = {
.size = sizeof(mln_camera_delta),
.fields = MLN_CAMERA_DELTA_OFFSET,
.offset = movement,
.gesture_phase = MLN_GESTURE_PHASE_UPDATE,
};
mln_map_apply_camera_delta(map, &drag, NULL, NULL);
}
static void apply_pinch(
mln_map map, double scale, mln_screen_point center
) {
const mln_camera_delta pinch = {
.size = sizeof(mln_camera_delta),
.fields = MLN_CAMERA_DELTA_SCALE | MLN_CAMERA_DELTA_ANCHOR,
.scale = scale,
.anchor = center,
.gesture_phase = MLN_GESTURE_PHASE_UPDATE,
};
mln_map_apply_camera_delta(map, &pinch, NULL, NULL);
}
static void end_gesture(mln_map map) {
const mln_camera_delta end = {
.size = sizeof(mln_camera_delta),
.gesture_phase = MLN_GESTURE_PHASE_END,
};
mln_map_apply_camera_delta(map, &end, NULL, NULL);
}
main.dart
void beginGesture(MapHandle map) {
map.discarding.applyCameraDelta(
const CameraDelta(gesturePhase: GesturePhase.begin),
);
}
void applyDrag(MapHandle map, ScreenPoint movement) {
map.discarding.applyCameraDelta(
CameraDelta(offset: movement, gesturePhase: GesturePhase.update),
);
}
void applyPinch(MapHandle map, double scale, ScreenPoint center) {
map.discarding.applyCameraDelta(
CameraDelta(
scale: scale,
anchor: center,
gesturePhase: GesturePhase.update,
),
);
}
void endGesture(MapHandle map) {
map.discarding.applyCameraDelta(
const CameraDelta(gesturePhase: GesturePhase.end),
);
}
Program.cs
private static void BeginGesture(MapHandle map)
{
map.Discarding.ApplyCameraDelta(
new CameraDelta { GesturePhase = GesturePhase.Begin }
);
}
private static void ApplyDrag(MapHandle map, ScreenPoint movement)
{
map.Discarding.ApplyCameraDelta(
new CameraDelta
{
Offset = movement,
GesturePhase = GesturePhase.Update,
}
);
}
private static void ApplyPinch(
MapHandle map,
double scale,
ScreenPoint center
)
{
map.Discarding.ApplyCameraDelta(
new CameraDelta
{
Scale = scale,
Anchor = center,
GesturePhase = GesturePhase.Update,
}
);
}
private static void EndGesture(MapHandle map)
{
map.Discarding.ApplyCameraDelta(
new CameraDelta { GesturePhase = GesturePhase.End }
);
}
main.go
func beginGesture(m *maplibre.MapHandle) error {
return m.Discarding().ApplyCameraDelta(maplibre.CameraDelta{
GesturePhase: maplibre.GesturePhaseBegin,
})
}
func applyDrag(
m *maplibre.MapHandle, movement maplibre.ScreenPoint,
) error {
return m.Discarding().ApplyCameraDelta(maplibre.CameraDelta{
Offset: &movement,
GesturePhase: maplibre.GesturePhaseUpdate,
})
}
func applyPinch(
m *maplibre.MapHandle, scale float64, center maplibre.ScreenPoint,
) error {
return m.Discarding().ApplyCameraDelta(maplibre.CameraDelta{
Scale: &scale,
Anchor: &center,
GesturePhase: maplibre.GesturePhaseUpdate,
})
}
func endGesture(m *maplibre.MapHandle) error {
return m.Discarding().ApplyCameraDelta(maplibre.CameraDelta{
GesturePhase: maplibre.GesturePhaseEnd,
})
}
main.vala
void begin_gesture (Map map) {
var delta = new CameraDelta () {
gesture_phase = GesturePhase.BEGIN,
};
map.apply_camera_delta_async.begin (delta);
}
void apply_drag (Map map, ScreenPoint movement) {
var delta = new CameraDelta () {
offset = movement,
gesture_phase = GesturePhase.UPDATE,
};
map.apply_camera_delta_async.begin (delta);
}
void apply_pinch (Map map, double scale, ScreenPoint center) {
var delta = new CameraDelta () {
scale = scale,
anchor = center,
gesture_phase = GesturePhase.UPDATE,
};
map.apply_camera_delta_async.begin (delta);
}
void end_gesture (Map map) {
var delta = new CameraDelta () {
gesture_phase = GesturePhase.END,
};
map.apply_camera_delta_async.begin (delta);
}
Main.java
private static void beginGesture(MapHandle map) {
map.getDiscarding()
.applyCameraDelta(
CameraDelta.of().withGesturePhase(GesturePhase.BEGIN));
}
private static void applyDrag(MapHandle map, ScreenPoint movement) {
map.getDiscarding()
.applyCameraDelta(
CameraDelta.of()
.withOffset(movement)
.withGesturePhase(GesturePhase.UPDATE));
}
private static void applyPinch(
MapHandle map, double scale, ScreenPoint center) {
map.getDiscarding()
.applyCameraDelta(
CameraDelta.of()
.withScale(scale)
.withAnchor(center)
.withGesturePhase(GesturePhase.UPDATE));
}
private static void endGesture(MapHandle map) {
map.getDiscarding()
.applyCameraDelta(
CameraDelta.of().withGesturePhase(GesturePhase.END));
}
Main.kt
private fun beginGesture(map: MapHandle) {
map.discarding.applyCameraDelta(
CameraDelta(gesturePhase = GesturePhase.BEGIN),
)
}
private fun applyDrag(map: MapHandle, movement: ScreenPoint) {
map.discarding.applyCameraDelta(
CameraDelta(
offset = movement,
gesturePhase = GesturePhase.UPDATE,
),
)
}
private fun applyPinch(
map: MapHandle,
scale: Double,
center: ScreenPoint,
) {
map.discarding.applyCameraDelta(
CameraDelta(
scale = scale,
anchor = center,
gesturePhase = GesturePhase.UPDATE,
),
)
}
private fun endGesture(map: MapHandle) {
map.discarding.applyCameraDelta(
CameraDelta(gesturePhase = GesturePhase.END),
)
}
main.py
def begin_gesture(map_handle: mln.MapHandle) -> None:
map_handle.discarding.apply_camera_delta(
mln.CameraDelta(gesture_phase=mln.GesturePhase.BEGIN)
)
def apply_drag(
map_handle: mln.MapHandle, movement: mln.ScreenPoint
) -> None:
map_handle.discarding.apply_camera_delta(
mln.CameraDelta(
offset=movement, gesture_phase=mln.GesturePhase.UPDATE
)
)
def apply_pinch(
map_handle: mln.MapHandle, scale: float, center: mln.ScreenPoint
) -> None:
map_handle.discarding.apply_camera_delta(
mln.CameraDelta(
scale=scale,
anchor=center,
gesture_phase=mln.GesturePhase.UPDATE,
)
)
def end_gesture(map_handle: mln.MapHandle) -> None:
map_handle.discarding.apply_camera_delta(
mln.CameraDelta(gesture_phase=mln.GesturePhase.END)
)
main.rs
fn begin_gesture(map: &MapHandle) -> Result<()> {
map.discarding().apply_camera_delta(&CameraDelta {
gesture_phase: GesturePhase::Begin,
..Default::default()
})
}
fn apply_drag(map: &MapHandle, movement: ScreenPoint) -> Result<()> {
map.discarding().apply_camera_delta(&CameraDelta {
offset: Some(movement),
gesture_phase: GesturePhase::Update,
..Default::default()
})
}
fn apply_pinch(
map: &MapHandle,
scale: f64,
center: ScreenPoint,
) -> Result<()> {
map.discarding().apply_camera_delta(&CameraDelta {
scale: Some(scale),
anchor: Some(center),
gesture_phase: GesturePhase::Update,
..Default::default()
})
}
fn end_gesture(map: &MapHandle) -> Result<()> {
map.discarding().apply_camera_delta(&CameraDelta {
gesture_phase: GesturePhase::End,
..Default::default()
})
}
main.swift
private func beginGesture(_ map: MapHandle) throws {
try map.discarding
.applyCameraDelta(CameraDelta(gesturePhase: .begin))
}
private func applyDrag(
_ map: MapHandle,
movement: ScreenPoint
) throws {
try map.discarding.applyCameraDelta(
CameraDelta(offset: movement, gesturePhase: .update)
)
}
private func applyPinch(
_ map: MapHandle,
scale: Double,
center: ScreenPoint
) throws {
try map.discarding.applyCameraDelta(
CameraDelta(scale: scale, anchor: center, gesturePhase: .update)
)
}
private func endGesture(_ map: MapHandle) throws {
try map.discarding.applyCameraDelta(CameraDelta(gesturePhase: .end))
}
main.ts
function beginGesture(map: MapHandle): void {
map.discarding.applyCameraDelta({
gesturePhase: GesturePhase.begin,
});
}
function applyDrag(map: MapHandle, movement: ScreenPoint): void {
map.discarding.applyCameraDelta({
offset: movement,
gesturePhase: GesturePhase.update,
});
}
function applyPinch(
map: MapHandle,
scale: number,
center: ScreenPoint,
): void {
map.discarding.applyCameraDelta({
scale,
anchor: center,
gesturePhase: GesturePhase.update,
});
}
function endGesture(map: MapHandle): void {
map.discarding.applyCameraDelta({ gesturePhase: GesturePhase.end });
}
main.zig
fn beginGesture(map: maplibre.Map) !void {
try map.discarding().applyCameraDelta(
.{ .gesture_phase = .begin },
null,
);
}
fn applyDrag(
map: maplibre.Map,
movement: maplibre.ScreenPoint,
) !void {
try map.discarding().applyCameraDelta(.{
.offset = movement,
.gesture_phase = .update,
}, null);
}
fn applyPinch(
map: maplibre.Map,
scale: f64,
center: maplibre.ScreenPoint,
) !void {
try map.discarding().applyCameraDelta(.{
.scale = scale,
.anchor = center,
.gesture_phase = .update,
}, null);
}
fn endGesture(map: maplibre.Map) !void {
try map.discarding().applyCameraDelta(
.{ .gesture_phase = .end },
null,
);
}

A mln_map_projectionMapProjectionHandleMapProjectionHandleMapProjectionHandleMaplibreNativeFfiMapProjectionMapProjectionHandleMapProjectionHandleMapProjectionHandleMapProjectionHandleMapProjectionHandleMapProjectionHandleMapProjection converts between screen points and coordinates for one view of the map. Create it from the snapshot’s view, and create a new one after the camera moves. Release it when you’re done.

main.c
mln_map_snapshot current = {.size = sizeof(mln_map_snapshot)};
mln_map_get_snapshot(map, &current, NULL);
mln_map_projection projection = MLN_HANDLE_NULL;
mln_map_projection_create(&current.view, &projection, NULL);
mln_lat_lng coordinate;
mln_map_projection_lat_lng_for_pixel(
projection, (mln_screen_point){.x = 100, .y = 200}, &coordinate,
NULL
);
mln_screen_point point;
mln_map_projection_pixel_for_lat_lng(
projection, coordinate, &point, NULL
);
mln_map_projection_release(projection);
main.dart
final projection = MapProjectionHandle.create(
map.getSnapshot().view,
);
final coordinate = projection.latLngForPixel(
const ScreenPoint(x: 100, y: 200),
);
final point = projection.pixelForLatLng(coordinate);
projection.release();
Program.cs
using (
var projection = MapProjectionHandle.Create(
map.GetSnapshot().View
)
)
{
var coordinate = projection.LatLngForPixel(
new ScreenPoint(100, 200)
);
var point = projection.PixelForLatLng(coordinate);
}
main.go
current, err := m.Snapshot()
if err != nil {
return err
}
projection, err := maplibre.NewMapProjection(current.View)
if err != nil {
return err
}
defer projection.Release()
coordinate, err := projection.LatLngForPixel(
maplibre.ScreenPoint{X: 100, Y: 200},
)
if err != nil {
return err
}
point, err := projection.PixelForLatLng(coordinate)
if err != nil {
return err
}
main.vala
var projection = new MapProjection.create (
map.get_snapshot ().view
);
var coordinate = projection.lat_lng_for_pixel ({ x: 100, y: 200 });
var point = projection.pixel_for_lat_lng (coordinate);
Main.java
ScreenPoint point;
try (MapProjectionHandle projection =
MapProjectionHandle.create(map.getSnapshot().getView())) {
LatLng coordinate =
projection.latLngForPixel(new ScreenPoint(100.0, 200.0));
point = projection.pixelForLatLng(coordinate);
}
Main.kt
val projection = MapProjectionHandle.create(map.getSnapshot().view)
val coordinate =
projection.latLngForPixel(ScreenPoint(x = 100.0, y = 200.0))
val point = projection.pixelForLatLng(coordinate)
projection.release()
main.py
with mln.MapProjectionHandle.create(
map_handle.get_snapshot().view
) as projection:
coordinate = projection.lat_lng_for_pixel(
mln.ScreenPoint(x=100, y=200)
)
point = projection.pixel_for_lat_lng(coordinate)
main.rs
let projection =
MapProjectionHandle::create(&map.get_snapshot()?.view)?;
let coordinate = projection
.lat_lng_for_pixel(ScreenPoint::new(100.0, 200.0))?;
let point = projection.pixel_for_lat_lng(coordinate)?;
drop(projection);
main.swift
let projection = try MapProjectionHandle.create(
view: map.getSnapshot().view
)
let coordinate = try projection.latLngForPixel(
point: ScreenPoint(x: 100, y: 200)
)
let point = try projection.pixelForLatLng(coordinate: coordinate)
projection.release()
main.ts
const projection = MapProjectionHandle.create(
map.getSnapshot().view,
);
const coordinate = projection.latLngForPixel({ x: 100, y: 200 });
const point = projection.pixelForLatLng(coordinate);
projection.release();
main.zig
const view = (try map.getSnapshot(null)).view;
var projection = try maplibre.MapProjection.create(view, null);
defer projection.release();
const coordinate =
try projection.latLngForPixel(.{ .x = 100, .y = 200 }, null);
const point = try projection.pixelForLatLng(coordinate, null);