All examples3dgeometry-3d
§ examples · 3d

3D Primitives

Four Mesh3DSymbol primitives (box · cylinder · sphere · cone) placed as point features over a satellite tilt view. A CallbackRenderer dispatches on the feature's `kind` attribute.

sluggeometry-3d
source127 lines
statuslive
tsexamples-src/geometry-3d.ts
1/**
2 * 3D Primitives — four geometry flavors side-by-side over Ankara:
3 * - Box + Cylinder → fill-extrusion polygon footprints (buildings)
4 * - Sphere + Cone → Mesh3DSymbol on point features
5 *
6 * This matches the landing-page scene-3 pattern so the example mirrors
7 * MapGPU's production drawing workflow.
8 */
9 
10import { MapView } from "mapgpu";
11import { GraphicsLayer, RasterTileLayer } from "mapgpu/layers";
12import { RenderEngine } from "mapgpu/render";
13import { CallbackRenderer, SimpleRenderer } from "mapgpu/core";
14import { makeCircleFootprint, makeRectFootprint } from "mapgpu/tools";
15 
16import type { RunResultObject } from "@/components/examples/ExampleCanvas";
17 
18const ANKARA: [number, number] = [32.86, 39.93];
19 
20const BOX = { id: "box", lon: ANKARA[0] - 0.0020, lat: ANKARA[1], scale: [40, 10, 40] as [number, number, number], color: [255, 109, 58, 235] as [number, number, number, number] };
21const CYL = { id: "cyl", lon: ANKARA[0] - 0.0007, lat: ANKARA[1], scale: [40, 10, 40] as [number, number, number], color: [88, 166, 255, 235] as [number, number, number, number] };
22const SPH = { id: "sph", lon: ANKARA[0] + 0.0007, lat: ANKARA[1], scale: [20, 20, 20] as [number, number, number], color: [188, 140, 255, 235] as [number, number, number, number] };
23const CONE = { id: "cone", lon: ANKARA[0] + 0.0020, lat: ANKARA[1], scale: [30, 10, 30] as [number, number, number], color: [63, 185, 80, 235] as [number, number, number, number] };
24 
25export async function run(container: HTMLElement): Promise<RunResultObject> {
26 const view = new MapView({
27 container,
28 renderEngine: new RenderEngine(),
29 mode: "3d",
30 center: ANKARA,
31 zoom: 17,
32 pitch: 55,
33 bearing: -15,
34 backgroundColor: "transparent",
35 globeEffects: {
36 atmosphere: { enabled: true, strength: 1.0, falloff: 3 },
37 },
38 });
39 
40 view.map.add(
41 new RasterTileLayer({
42 id: "esri-imagery",
43 urlTemplate:
44 "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
45 maxZoom: 19,
46 attribution: "© Esri",
47 }),
48 );
49 
50 // Box + Cylinder as extruded polygon footprints.
51 const extrusion = new GraphicsLayer({ id: "extrusion" });
52 extrusion.renderer = new CallbackRenderer((feature) => {
53 const color = (feature.attributes.color ?? [255, 109, 58, 235]) as [number, number, number, number];
54 return {
55 type: "fill-extrusion",
56 color,
57 heightField: "height",
58 minHeightField: "minHeight",
59 ambient: 0.4,
60 shininess: 24,
61 specularStrength: 0.18,
62 };
63 });
64 extrusion.addMany([
65 {
66 id: BOX.id,
67 geometry: {
68 type: "Polygon" as const,
69 coordinates: [makeRectFootprint(BOX.lon, BOX.lat, BOX.scale[0] / 2, BOX.scale[0] / 2)],
70 },
71 attributes: { height: BOX.scale[1], minHeight: 0, color: BOX.color },
72 },
73 {
74 id: CYL.id,
75 geometry: {
76 type: "Polygon" as const,
77 coordinates: [makeCircleFootprint(CYL.lon, CYL.lat, CYL.scale[0] / 2, 48)],
78 },
79 attributes: { height: CYL.scale[1], minHeight: 0, color: CYL.color },
80 },
81 ]);
82 view.map.add(extrusion);
83 
84 // Sphere as its own layer + SimpleRenderer with mesh-3d.
85 const sphere = new GraphicsLayer({ id: "sphere" });
86 sphere.renderer = new SimpleRenderer({
87 type: "mesh-3d",
88 meshType: "sphere",
89 color: SPH.color,
90 scale: SPH.scale,
91 ambient: 0.35,
92 shininess: 32,
93 specularStrength: 0.15,
94 });
95 sphere.add({
96 id: SPH.id,
97 geometry: { type: "Point" as const, coordinates: [SPH.lon, SPH.lat] },
98 attributes: {},
99 });
100 view.map.add(sphere);
101 
102 // Cone likewise.
103 const cone = new GraphicsLayer({ id: "cone" });
104 cone.renderer = new SimpleRenderer({
105 type: "mesh-3d",
106 meshType: "cone",
107 color: CONE.color,
108 scale: CONE.scale,
109 ambient: 0.35,
110 shininess: 32,
111 specularStrength: 0.15,
112 });
113 cone.add({
114 id: CONE.id,
115 geometry: { type: "Point" as const, coordinates: [CONE.lon, CONE.lat] },
116 attributes: {},
117 });
118 view.map.add(cone);
119 
120 await view.when();
121 
122 return {
123 dispose: () => view.destroy(),
124 controls: [],
125 };
126}