All examplesbasicsfitbounds-flyto
§ examples · basics

fitBounds & flyTo

Three cities arc in via `flyTo` (zoom-out → pan → zoom-in) and one bbox frames the world with padded `fitBounds`. Toggle 2D/3D to see the same call honor pitch/bearing on the globe.

slugfitbounds-flyto
source98 lines
statuslive
tsexamples-src/fitbounds-flyto.ts
1/**
2 * fitBounds & flyTo — camera navigation in both 2D and 3D. Each city
3 * knows its ideal zoom + pitch + bearing; in 3D those angles are
4 * honored, in 2D they collapse to a flat top-down flyTo. The 2D/3D
5 * toggle feeds `view.switchTo(...)` so you can compare how the same
6 * arc renders across projections.
7 */
8 
9import { MapView } from "mapgpu";
10import { RasterTileLayer } from "mapgpu/layers";
11import { RenderEngine } from "mapgpu/render";
12 
13import type { RunResultObject } from "@/components/examples/ExampleCanvas";
14 
15type City = {
16 label: string;
17 center: [number, number];
18 zoom: number;
19 /** Only applied in 3D. Degrees from top-down. */
20 pitch: number;
21 /** Only applied in 3D. Heading in degrees (0 = north). */
22 bearing: number;
23};
24 
25const CITIES: Record<string, City> = {
26 istanbul: { label: "Istanbul", center: [29.02, 41.01], zoom: 13, pitch: 55, bearing: -15 },
27 london: { label: "London", center: [-0.127, 51.507], zoom: 13, pitch: 55, bearing: 10 },
28 tokyo: { label: "Tokyo", center: [139.767, 35.681], zoom: 13, pitch: 55, bearing: -25 },
29};
30 
31// World bbox. fitBounds honors this identically in both modes — in 3D
32// the globe tilts to frame it.
33const WORLD_BOUNDS: [number, number, number, number] = [-180, -60, 180, 75];
34 
35export async function run(container: HTMLElement): Promise<RunResultObject> {
36 const view = new MapView({
37 container,
38 renderEngine: new RenderEngine(),
39 mode: "2d",
40 center: [0, 20],
41 zoom: 2,
42 minZoom: 1,
43 maxZoom: 18,
44 backgroundColor: "transparent",
45 });
46 
47 view.map.add(
48 new RasterTileLayer({
49 id: "osm",
50 urlTemplate: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
51 maxZoom: 19,
52 attribution: "© OpenStreetMap contributors",
53 }),
54 );
55 
56 await view.when();
57 
58 // pitch/bearing live in GoToTarget and are silently ignored by Mode2D,
59 // so the same flyTo call works in both projections.
60 const fly = (city: City) =>
61 view.flyTo(
62 {
63 center: city.center,
64 zoom: city.zoom,
65 pitch: city.pitch,
66 bearing: city.bearing,
67 },
68 { duration: 1800 },
69 );
70 
71 return {
72 dispose: () => view.destroy(),
73 controls: [
74 {
75 kind: "toggle",
76 id: "mode",
77 labels: ["2D", "3D"],
78 initial: false,
79 onChange: async (on) => {
80 await view.switchTo(on ? "3d" : "2d");
81 },
82 },
83 ...Object.entries(CITIES).map(([id, city]) => ({
84 kind: "button" as const,
85 id,
86 label: city.label,
87 onClick: () => fly(city),
88 })),
89 {
90 kind: "button",
91 id: "fit-world",
92 label: "Fit world",
93 onClick: () => view.fitBounds(WORLD_BOUNDS, { padding: 40, duration: 1200 }),
94 },
95 ],
96 };
97}