@aapicoits/powermap-web-sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1138 -0
- package/dist/powermap-web-sdk.cjs.cjs +355 -0
- package/dist/powermap-web-sdk.js +7953 -0
- package/dist/powermap-web-sdk.umd.cjs +355 -0
- package/dist/style.css +5 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,1138 @@
|
|
|
1
|
+
# PowerMap Web SDK π
|
|
2
|
+
|
|
3
|
+
A professional-grade, high-performance Map SDK. Optimized for logistics, real-time tracking, indoor mapping, and 3D vehicle simulations.
|
|
4
|
+
|
|
5
|
+
<!-- [](https://www.npmjs.com/package/@powermap/web-sdk) -->
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 2. ποΈ Architecture Overview
|
|
10
|
+
|
|
11
|
+
The PowerMap Web SDK is built with a **Layered Facade Architecture**, ensuring that high-performance map rendering is wrapped in an easy-to-use, fluent JS API.
|
|
12
|
+
|
|
13
|
+
```mermaid
|
|
14
|
+
graph TD
|
|
15
|
+
App[Web Application] --> PM[PowerMap Instance]
|
|
16
|
+
|
|
17
|
+
subgraph "Facade Layer (Fluent API)"
|
|
18
|
+
PM
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
subgraph "Manager Layer (Lazy Loading)"
|
|
22
|
+
PM --> Registry[Component Registry]
|
|
23
|
+
Registry --> MarkerMgr[MarkerManager]
|
|
24
|
+
Registry --> SearchMgr[SearchManager]
|
|
25
|
+
Registry --> RouteMgr[RoutingManager]
|
|
26
|
+
Registry --> GeomMgr[GeometryManager]
|
|
27
|
+
Registry --> EditorMgr[EditorManager]
|
|
28
|
+
Registry --> LayerMgr[LayerManager]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
subgraph "Service Layer"
|
|
32
|
+
PM --> AuthMgr[AuthManager]
|
|
33
|
+
PM --> Events[EventBus]
|
|
34
|
+
AuthMgr --> Cache[Token Cache]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
subgraph "Visual Engine"
|
|
38
|
+
PM --> ML[PowerMap Visual Engine]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
Cache -.-> Storage[(Local Storage)]
|
|
42
|
+
AuthMgr -.-> API((PowerMap APIs))
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 3. π¦ Installation
|
|
48
|
+
|
|
49
|
+
Install the package via npm or yarn:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm install @powermap/web-sdk
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 4. π Quick Start
|
|
58
|
+
|
|
59
|
+
Get a map up and running in less than 2 minutes. The PowerMap SDK is designed for chaining and ease of use.
|
|
60
|
+
|
|
61
|
+
### Step A: Prepare your HTML
|
|
62
|
+
|
|
63
|
+
```html
|
|
64
|
+
<div id="map" style="width: 100%; height: 500px; border-radius: 8px; overflow: hidden;"></div>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Step B: Initialize and Style
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
import { PowerMap } from "@powermap/web-sdk";
|
|
71
|
+
|
|
72
|
+
// 1. Initialize with dual-auth system
|
|
73
|
+
const map = new PowerMap({
|
|
74
|
+
container: "map",
|
|
75
|
+
center: [100.5018, 13.7563], // Bangkok [Longitude, Latitude]
|
|
76
|
+
zoom: 14,
|
|
77
|
+
mapApiKey: "YOUR_MAP_API_KEY", // Public key for tiles and base layers
|
|
78
|
+
auth: {
|
|
79
|
+
clientId: "YOUR_CLIENT_ID", // Service Credentials for backend APIs
|
|
80
|
+
clientSecret: "YOUR_CLIENT_SECRET"
|
|
81
|
+
},
|
|
82
|
+
previewOnly: true // Optional: Lock all user interactions (Static View)
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// 2. Chaining for smooth DX
|
|
86
|
+
map.onMapLoaded((e) => {
|
|
87
|
+
console.log("PowerMap is ready!", e.map);
|
|
88
|
+
|
|
89
|
+
// Add a marker as soon as the map loads
|
|
90
|
+
map.addMarker({
|
|
91
|
+
position: [100.5018, 13.7563],
|
|
92
|
+
type: "start",
|
|
93
|
+
popupContent: "<strong>Origin Point</strong>"
|
|
94
|
+
});
|
|
95
|
+
})
|
|
96
|
+
.setStyle('th') // Options: 'th', 'en', 'dark', 'gray'
|
|
97
|
+
.flyTo({ zoom: 16, pitch: 45 });
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
> [!TIP]
|
|
101
|
+
> **Performance Note**: The PowerMap Web SDK uses a lazy-loading registry pattern. Managers (Routing, Search, etc.) are only initialized once you first call them, keeping your app's initial memory footprint minimal.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 5. π Method Chaining
|
|
106
|
+
|
|
107
|
+
The PowerMap SDK is built with a **fluent interface**, allowing you to link multiple configuration steps and event listeners together in a single statement.
|
|
108
|
+
|
|
109
|
+
### Supported Methods
|
|
110
|
+
|
|
111
|
+
Most methods that perform an action on the map (styling, camera movement, or event registration) return the `PowerMap` instance to facilitate chaining.
|
|
112
|
+
|
|
113
|
+
| Group | Chainable Methods |
|
|
114
|
+
| :--- | :--- |
|
|
115
|
+
| **Events** | `on`, `off`, `onMapLoaded`, `onClick`, `onMapLongClick`, `onMarkerClick`, `onPoiClick`, `onMarkerDrag`, `onMarkerDragEnd`, `onRouteCalculated`, `onError` |
|
|
116
|
+
| **Camera** | `flyTo`, `easeTo`, `fitBounds`, `jumpTo`, `setPitch`, `setBearing`, `zoomIn`, `zoomOut`, `rotateLeft`, `rotateRight` |
|
|
117
|
+
| **Map Style** | `setStyle` |
|
|
118
|
+
| **Markers** | `removeMarker`, `updatePopup`, `clearMarkers` |
|
|
119
|
+
| **Search** | `search` |
|
|
120
|
+
| **Routing** | `clearRoutes` |
|
|
121
|
+
| **Layers** | `showLayer`, `hideLayer`, `toggleLayer` |
|
|
122
|
+
<!-- | **Sim/Nav** | `startSimulation`, `stopSimulation`, `startNavigation`, `stopNavigation` | -->
|
|
123
|
+
|
|
124
|
+
> [!NOTE]
|
|
125
|
+
> Methods that return a **Promise** (like `addRoute`) or a **new Object** (like `addMarker` or `getRouteDetails`) interrupt the chain.
|
|
126
|
+
|
|
127
|
+
### Chaining Example
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
map.onMapLoaded(() => console.log("Ready!"))
|
|
131
|
+
.onMarkerClick(data => console.log("Clicked:", data))
|
|
132
|
+
.setStyle('dark')
|
|
133
|
+
.flyTo({ center: [100.5, 13.7], zoom: 12 })
|
|
134
|
+
.setPitch(45);
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 6. π οΈ Configuration Reference
|
|
140
|
+
|
|
141
|
+
When creating a `new PowerMap(options)`, the following options are available:
|
|
142
|
+
|
|
143
|
+
| Option | Type | Default | Required | Description |
|
|
144
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
145
|
+
| `container` | `string` | - | **Yes** | The HTML element ID where the map will be rendered. |
|
|
146
|
+
| `mapApiKey` | `string` | - | **Yes** | Your PowerMap Public API Key for map tiles. |
|
|
147
|
+
| `auth` | `Object` | - | **Yes** | **Service Credentials**. Used for connecting to backend APIs of PowerMap services (e.g., Routing, Optimization). Contains `clientId` and `clientSecret`. |
|
|
148
|
+
| `center` | `[lng, lat]` | `[100, 13]` | No | The initial center point of the map. |
|
|
149
|
+
| `zoom` | `number` | `14` | No | Total zoom level (0-22). |
|
|
150
|
+
| `baseUrl` | `string` | - | No | Optional proxy URL (e.g., `/api/proxy`). If set, the SDK will route all API requests through this URL. |
|
|
151
|
+
| `maptilerKey` | `string` | - | No | Your MapTiler API Key. Required if you intend to use high-resolution satellite layers. |
|
|
152
|
+
| `previewOnly` | `boolean` | `false` | No | If **true**, the map will be non-interactive (no zoom, pan, rotate, or pitch). Perfect for static map previews. |
|
|
153
|
+
|
|
154
|
+
### Core Instance Methods
|
|
155
|
+
|
|
156
|
+
These methods are available on the `PowerMap` instance to manage the map state and lifecycle.
|
|
157
|
+
|
|
158
|
+
| Method | Arguments | Description |
|
|
159
|
+
| :--- | :--- | :--- |
|
|
160
|
+
| `on(event, cb)` | `string, function` | Subscribe to any map event (see Section 9). |
|
|
161
|
+
| `off(event, cb)` | `string, function` | Unsubscribe from a previously registered event. |
|
|
162
|
+
| `setMode(mode, opts)` | `string, Object` | Switch between `'standard'` and `'indoor'` modes. |
|
|
163
|
+
| `setStyle(name)` | `string` | Change the map style (e.g., `'th'`, `'dark'`). |
|
|
164
|
+
| `setNavigationControl(opts)` | `boolean\|Object` | Enable/disable or customize zoom/compass controls. |
|
|
165
|
+
| `setFullscreenControl(opts)` | `boolean\|Object` | Enable/disable fullscreen control. |
|
|
166
|
+
| `debug()` | - | Access the Debug Manager for telemetry and performance tools. |
|
|
167
|
+
| `onError(callback)` | `function` | Global error listener for Auth, Map, or Routing issues. |
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 7. π Marker Management
|
|
172
|
+
|
|
173
|
+
Our marker system simplifies standard map markers with built-in presets for logistics and easy event handling.
|
|
174
|
+
|
|
175
|
+
### `addMarker(options)` Options
|
|
176
|
+
|
|
177
|
+
| Option | Type | Default | Description |
|
|
178
|
+
| :--- | :--- | :--- | :--- |
|
|
179
|
+
| `position` | `[lng, lat]` | **Required** | Coordinates for the marker. |
|
|
180
|
+
| `type` | `string` | `'default'` | Preset icons: `'start'`, `'destination'`, `'waypoint'`. |
|
|
181
|
+
| `iconUrl` | `string` | - | Custom image URL to use as the icon. Overrides `type`. |
|
|
182
|
+
| `width`/`height` | `string` | `'30px'`/`'38px'` | CSS dimensions for the marker icon. |
|
|
183
|
+
| `anchor` | `string` | `'bottom'` | Where the marker is anchored to the coordinate. |
|
|
184
|
+
| `draggable` | `boolean` | `false` | If true, user can drag the marker. Fires `onMarkerDrag` events. |
|
|
185
|
+
| `rotation` | `number` | `0` | Icon rotation in degrees. |
|
|
186
|
+
| `popupContent` | `string|Object` | - | HTML string or **Structured Object** to display in a premium popup. |
|
|
187
|
+
| `id` | `string` | - | **Unique ID** to track this marker. Allows retrieval via `getMarker(id)`. |
|
|
188
|
+
| `color` | `string` | - | Custom hex color for pinpoint marker (e.g. `'#ff4444'`). Uses **High-Accuracy GL Rendering**. |
|
|
189
|
+
| `icon` | `string` | - | FontAwesome icon class for pinpoint marker (e.g. `'fas fa-car'`). |
|
|
190
|
+
| `element` | `HTMLElement` | - | Use a raw HTML element as a fully custom marker. |
|
|
191
|
+
|
|
192
|
+
> [!IMPORTANT]
|
|
193
|
+
> **High-Accuracy Rendering**: Markers with a `color` property use our new **GL Symbol Layer** system. These markers are rendered directly on the map canvas, ensuring **perfect alignment** and **zero-lag** during zoom or pan. Ordinary preset markers (`type`) use standard DOM elements for maximum compatibility.
|
|
194
|
+
|
|
195
|
+
### Marker Examples
|
|
196
|
+
|
|
197
|
+
#### Standard Presets
|
|
198
|
+
|
|
199
|
+
```javascript
|
|
200
|
+
map.addMarker({
|
|
201
|
+
position: [100.5, 13.7],
|
|
202
|
+
type: 'start', // 'start', 'destination', 'waypoint'
|
|
203
|
+
popupContent: "Start Point"
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
#### Premium Colored Markers (No Image Required)
|
|
208
|
+
|
|
209
|
+
```javascript
|
|
210
|
+
map.addMarker({
|
|
211
|
+
position: [100.52, 13.78],
|
|
212
|
+
color: '#f97316', // Vibrant Orange
|
|
213
|
+
icon: 'fas fa-store', // FontAwesome Icon
|
|
214
|
+
draggable: true, // Enable high-accuracy dragging
|
|
215
|
+
popupContent: {
|
|
216
|
+
title: "Power Shop",
|
|
217
|
+
tag: "Retail",
|
|
218
|
+
body: "Custom colored marker with icon."
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### Draggable Markers & Events
|
|
224
|
+
|
|
225
|
+
You can enable dragging for any marker. For GL-rendered markers (those with `color`), dragging is handled with sub-pixel precision.
|
|
226
|
+
|
|
227
|
+
```javascript
|
|
228
|
+
const marker = map.addMarker({
|
|
229
|
+
position: [100.5, 13.7],
|
|
230
|
+
color: '#3b82f6',
|
|
231
|
+
draggable: true
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// 1. Listen via Proxy (Recommended)
|
|
235
|
+
marker.on('drag', (e) => console.log('Current:', e.lngLat));
|
|
236
|
+
marker.on('dragend', (e) => console.log('Final:', e.lngLat));
|
|
237
|
+
|
|
238
|
+
// 2. Listen via Global Event Bus
|
|
239
|
+
map.onMarkerDrag(({ marker, lngLat }) => {
|
|
240
|
+
console.log(`Marker is moving to: ${lngLat.lng}, ${lngLat.lat}`);
|
|
241
|
+
});
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
#### Structured Popup Content & Actions
|
|
245
|
+
|
|
246
|
+
Instead of raw HTML, you can pass a structured object to `popupContent` for a premium, consistent look. You can also define interactive buttons using the `action` or `actions` property:
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
map.addMarker({
|
|
250
|
+
position: [100.5, 13.7],
|
|
251
|
+
popupContent: {
|
|
252
|
+
title: "Siam Paragon",
|
|
253
|
+
tag: "Mall",
|
|
254
|
+
body: "The pride of Bangkok with world-class shopping.",
|
|
255
|
+
footer: "Open: 10:00 - 22:00",
|
|
256
|
+
actions: [
|
|
257
|
+
{ label: "View Details", url: "https://example.com/details", target: "_blank" },
|
|
258
|
+
{ label: "Navigate Here", url: "https://example.com/nav", color: "#10b981" }
|
|
259
|
+
]
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Marker Methods
|
|
265
|
+
|
|
266
|
+
| Method | Arguments | Description |
|
|
267
|
+
| :--- | :--- | :--- |
|
|
268
|
+
| `addMarker(options)` | `Object` | Create and add a marker. Returns marker instance. |
|
|
269
|
+
| `getMarker(id)` | `string` | Retrieve a marker instance by its unique ID. |
|
|
270
|
+
| `removeMarker(markerOrId)` | `Object\|string` | Remove a marker using instance or ID. |
|
|
271
|
+
| `updatePopup(marker, content)` | `Object, string\|Object` | Update the popup content (HTML or Structured Object). |
|
|
272
|
+
| `clearMarkers()` | - | Remove all markers from the map. |
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## 8. π¨ CSS Customization
|
|
277
|
+
|
|
278
|
+
The PowerMap SDK automatically wraps all map elements in a global `.powermap-sdk` class and provides branded aliases for all internal UI components.
|
|
279
|
+
|
|
280
|
+
### The Global Wrapper
|
|
281
|
+
|
|
282
|
+
Any HTML element you provide as a `container` will automatically have the `powermap-sdk` class applied at runtime. This provides a clean scope for all PowerMap styles.
|
|
283
|
+
|
|
284
|
+
```html
|
|
285
|
+
<!-- Input -->
|
|
286
|
+
<div id="map"></div>
|
|
287
|
+
|
|
288
|
+
<!-- Output at runtime -->
|
|
289
|
+
<div id="map" class="powermap-sdk">...</div>
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Premium Class Aliases
|
|
293
|
+
|
|
294
|
+
To make customization even more intuitive, we've provided branded CSS classes that you can target directly:
|
|
295
|
+
|
|
296
|
+
| Feature | PowerMap Class | Description |
|
|
297
|
+
| :--- | :--- | :--- |
|
|
298
|
+
| **Marker** | `.pm-marker` | The main container for any marker. |
|
|
299
|
+
| **Popup** | `.pm-popup` | The root container of a popup. |
|
|
300
|
+
| **Popup Content** | `.pm-popup-content` | The main content area (glassmorphism). |
|
|
301
|
+
| **Popup Header** | `.pm-popup-header` | The header (part of structured content). |
|
|
302
|
+
| **Popup Body** | `.pm-popup-body` | The body text area. |
|
|
303
|
+
| **Popup Tag** | `.pm-popup-tag` | The category tag. |
|
|
304
|
+
| **Popup Footer** | `.pm-popup-footer` | The bottom meta info area. |
|
|
305
|
+
| **Popup Close** | `.pm-popup-close` | The close button element. |
|
|
306
|
+
| **Popup Tip** | `.pm-popup-tip` | The triangular tip pointing to the marker. |
|
|
307
|
+
| **Nav Group** | `.pm-ctrl-group` | The container of zoom/compass buttons. |
|
|
308
|
+
| **Zoom In** | `.pm-ctrl-zoom-in` | The zoom in button. |
|
|
309
|
+
| **Zoom Out** | `.pm-ctrl-zoom-out` | The zoom out button. |
|
|
310
|
+
| **Compass** | `.pm-ctrl-compass` | The compass/rotation button. |
|
|
311
|
+
|
|
312
|
+
### Premium Overrides
|
|
313
|
+
|
|
314
|
+
Target your own CSS using the scoped wrapper or branded aliases:
|
|
315
|
+
|
|
316
|
+
```css
|
|
317
|
+
/* Change the header color of structured popups */
|
|
318
|
+
.powermap-sdk .pm-popup-header {
|
|
319
|
+
background: linear-gradient(135deg, #000 0%, #333 100%);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/* Customize the glassmorphism background of all popups */
|
|
323
|
+
.powermap-sdk .pm-popup-content {
|
|
324
|
+
background: rgba(255, 255, 255, 0.95);
|
|
325
|
+
backdrop-filter: blur(20px);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/* Custom Marker Scale */
|
|
329
|
+
.powermap-sdk .pm-marker {
|
|
330
|
+
transform: scale(1.1);
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### `setNavigationControl(options)` Options
|
|
335
|
+
|
|
336
|
+
| Option | Type | Default | Description |
|
|
337
|
+
| :--- | :--- | :--- | :--- |
|
|
338
|
+
| `position` | `string` | `'top-right'` | Position on the map: `'top-left'`, `'top-right'`, `'bottom-left'`, `'bottom-right'`. |
|
|
339
|
+
| `showZoom` | `boolean` | `true` | If `true`, display the zoom in and zoom out buttons. |
|
|
340
|
+
| `showCompass` | `boolean` | `true` | If `true`, display the compass (rotation) button. |
|
|
341
|
+
| `visualizePitch` | `boolean` | `true` | If `true`, the compass button will rotate on the Y-axis to visualize map pitch (3D view). |
|
|
342
|
+
|
|
343
|
+
### Map Control Examples
|
|
344
|
+
|
|
345
|
+
```javascript
|
|
346
|
+
// Simple: Enable default branded controls (top-right)
|
|
347
|
+
map.setNavigationControl(true);
|
|
348
|
+
|
|
349
|
+
// Advanced: Custom position and visibility
|
|
350
|
+
map.setNavigationControl({
|
|
351
|
+
position: 'bottom-right',
|
|
352
|
+
showZoom: true,
|
|
353
|
+
showCompass: false
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// Disable controls
|
|
357
|
+
map.setNavigationControl(false);
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## 9. π£οΈ Routing & Optimization
|
|
363
|
+
|
|
364
|
+
Generate high-precision routes with automatic visualization. The SDK automatically fits the map view to the resulting route.
|
|
365
|
+
|
|
366
|
+
### `addRoute(options)` Options
|
|
367
|
+
|
|
368
|
+
| Option | Type | Default | Description |
|
|
369
|
+
| :--- | :--- | :--- | :--- |
|
|
370
|
+
| `start` | `[lng, lat]` | **Required** | Start point coordinates. |
|
|
371
|
+
| `destination` | `[lng, lat]` | **Required** | End point coordinates. |
|
|
372
|
+
| `waypoints` | `Array<[lng, lat]>` | `[]` | List of points to hit between start and end. |
|
|
373
|
+
| `optimize` | `boolean` | `false` | If true, reorders waypoints to find the shortest total trip (TSP). **Requires at least 1 waypoint**. |
|
|
374
|
+
| `profile` | `string` | `'driving'` | Routing engine profile: `'driving'`, `'walking'`, `'cycling'`. |
|
|
375
|
+
| `language` | `string` | `'th'` | Return instructions/names in `'th'` or `'en'`. |
|
|
376
|
+
| `routeColor` | `string` | `'#3b82f6'` | Hex color code for the main route line. |
|
|
377
|
+
| `routeWidth` | `number` | `5` | Thickness of the main route line. |
|
|
378
|
+
| `routeOpacity` | `number` | `1.0` | Opacity of the route line (0.0 to 1.0). |
|
|
379
|
+
| `casingColor` | `string` | `'#1d4ed8'` | Hex color for the route's outer border (shadow). |
|
|
380
|
+
| `casingWidth` | `number` | `8` | Thickness of the casing line. |
|
|
381
|
+
|
|
382
|
+
### Route Methods
|
|
383
|
+
|
|
384
|
+
| Method | Arguments | Description |
|
|
385
|
+
| :--- | :--- | :--- |
|
|
386
|
+
| `addRoute(options)` | `Object` | Calculate, draw route and fit view. Returns Promise. |
|
|
387
|
+
| `clearRoutes()` | - | Remove all route lines and markers from map. |
|
|
388
|
+
| `getRouteDetails()` | - | Returns distance, duration, and formatted strings. |
|
|
389
|
+
| `getRouteSummary()` | - | Returns top-level distance/duration strings. |
|
|
390
|
+
|
|
391
|
+
### `addRoute(options)` Return Object
|
|
392
|
+
|
|
393
|
+
The `addRoute` method returns a Promise that resolves to a `RouteResult` object containing:
|
|
394
|
+
|
|
395
|
+
| Property | Type | Description |
|
|
396
|
+
| :--- | :--- | :--- |
|
|
397
|
+
| `distance` | `number` | Total distance of the route in **meters**. |
|
|
398
|
+
| `duration` | `number` | Estimated travel time in **seconds**. |
|
|
399
|
+
| `steps` | `Array<Object>` | Turn-by-turn instructions (text, distance, duration, location). |
|
|
400
|
+
| `geometry` | `Object` | GeoJSON `LineString` of the route. |
|
|
401
|
+
| `raw` | `Object` | Raw response from the PowerMap routing backend. |
|
|
402
|
+
|
|
403
|
+
#### `steps` Object Structure
|
|
404
|
+
|
|
405
|
+
Each step in the `steps` array represents a turn or maneuver:
|
|
406
|
+
|
|
407
|
+
| Field | Type | Description |
|
|
408
|
+
| :--- | :--- | :--- |
|
|
409
|
+
| `text` | `string` | The maneuver instruction (e.g., "ΰΉΰΈ₯ΰΈ΅ΰΉΰΈ’ΰΈ§ΰΈΰΈ§ΰΈ²ΰΈΰΈ΅ΰΉΰΈΰΈΰΈΰΈͺΰΈΈΰΈΰΈΈΰΈ‘ΰΈ§ΰΈ΄ΰΈ"). |
|
|
410
|
+
| `distance` | `number` | Distance to travel for this step (meters). |
|
|
411
|
+
| `duration` | `number` | Estimated time for this step (seconds). |
|
|
412
|
+
| `location` | `[lng, lat]` | Geographic coordinate where the maneuver starts. |
|
|
413
|
+
|
|
414
|
+
### Routing Example
|
|
415
|
+
|
|
416
|
+
```javascript
|
|
417
|
+
// Simple route from A to B
|
|
418
|
+
const route = await map.addRoute({
|
|
419
|
+
start: [100.5018, 13.7563],
|
|
420
|
+
destination: [100.5118, 13.7663],
|
|
421
|
+
language: "en"
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
// Complex optimized route with waypoints
|
|
425
|
+
const optimizedRoute = await map.addRoute({
|
|
426
|
+
start: [100.50, 13.75],
|
|
427
|
+
destination: [100.60, 13.85],
|
|
428
|
+
waypoints: [
|
|
429
|
+
[100.55, 13.80],
|
|
430
|
+
[100.52, 13.78]
|
|
431
|
+
],
|
|
432
|
+
optimize: true, // Will reorder the 2 waypoints for the best efficiency
|
|
433
|
+
language: "th"
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
console.log(`Total Distance: ${optimizedRoute.distance}m`);
|
|
437
|
+
console.log(`Total Duration: ${optimizedRoute.duration}s`);
|
|
438
|
+
|
|
439
|
+
// Custom red route for heavy traffic visualization
|
|
440
|
+
const redRoute = await map.addRoute({
|
|
441
|
+
start: [100.5, 13.7],
|
|
442
|
+
destination: [100.6, 13.8],
|
|
443
|
+
routeColor: "#ff0000",
|
|
444
|
+
routeWidth: 10,
|
|
445
|
+
casingColor: "#990000"
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// Clear all routes from visibility
|
|
449
|
+
map.clearRoutes();
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
#### π Route Summary Helpers
|
|
453
|
+
|
|
454
|
+
Quickly access formatted summaries (distance in km/m, time in hours/mins) for the total route and individual steps:
|
|
455
|
+
|
|
456
|
+
```javascript
|
|
457
|
+
// Get enhanced details (Recommended)
|
|
458
|
+
const details = map.getRouteDetails();
|
|
459
|
+
if (details) {
|
|
460
|
+
console.log(`Total: ${details.distanceStr}, Time: ${details.durationStr}`);
|
|
461
|
+
|
|
462
|
+
// Each step also contains formatted strings
|
|
463
|
+
details.steps.forEach(step => {
|
|
464
|
+
console.log(`${step.text} (${step.distanceStr})`);
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Get just the top-level summary
|
|
469
|
+
const summary = map.getRouteSummary(); // { distanceStr, durationStr }
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
474
|
+
## ποΈ 10. Map Layers Management
|
|
475
|
+
|
|
476
|
+
The `LayerManager` provides a robust API to toggle built-in map layers (e.g., Roads, Railways, BTS/MRT, City Plans) on and off. The SDK dynamically fetches these layers from the catalog and applies them to your current map style.
|
|
477
|
+
|
|
478
|
+
### Built-in Layer Groups
|
|
479
|
+
|
|
480
|
+
Map layers are grouped into 3 main categories:
|
|
481
|
+
|
|
482
|
+
1. **CITY PLAN** (`city`): City plans for various provinces.
|
|
483
|
+
2. **TRANSPORT** (`transport`): Roads, Current/Future Railways, Current/Future BTS/MRT.
|
|
484
|
+
3. **INSIGHT DATA** (`insight`): Night lights, Satellite imagery.
|
|
485
|
+
|
|
486
|
+
### Layer APIs
|
|
487
|
+
|
|
488
|
+
| Method | Arguments | Description |
|
|
489
|
+
| :--- | :--- | :--- |
|
|
490
|
+
| `map.layers.getCatalog()` | - | Returns the full list of available layer groups and IDs. |
|
|
491
|
+
| `map.layers.ensure(layerId)` | `string` | Ensures a layer's sources are created (idempotent). |
|
|
492
|
+
| `map.layers.show(layerId)` | `string` | Ensures and shows a specific layer. |
|
|
493
|
+
| `map.layers.hide(layerId)` | `string` | Hides a specific layer. |
|
|
494
|
+
| `map.layers.toggle(layerId)` | `string` | Toggles the visibility of a layer. |
|
|
495
|
+
| `map.layers.getLayerState(id)` | `string` | Returns `{loaded, visible}` status. |
|
|
496
|
+
| `map.layers.setOpacity(id, val)` | `string, number` | Adjusts the transparency (0.0 - 1.0). |
|
|
497
|
+
| `map.layers.showGroup(key)` | `string` | Shows all layers within a specific group. |
|
|
498
|
+
| `map.layers.hideGroup(key)` | `string` | Hides all layers within a specific group. |
|
|
499
|
+
| `map.layers.hideAll()` | - | Hides every active layer from the catalog. |
|
|
500
|
+
|
|
501
|
+
### Usage Example
|
|
502
|
+
|
|
503
|
+
```javascript
|
|
504
|
+
map.onMapLoaded(async () => {
|
|
505
|
+
// 1. View all available layers
|
|
506
|
+
const catalog = map.layers.getCatalog();
|
|
507
|
+
console.log(catalog);
|
|
508
|
+
|
|
509
|
+
// 2. Show the Road layer
|
|
510
|
+
await map.layers.show('pm-road-vt');
|
|
511
|
+
|
|
512
|
+
// 3. Toggle the current Railway layer
|
|
513
|
+
await map.layers.toggle('pm-current-railway-th-vt');
|
|
514
|
+
|
|
515
|
+
// 4. Adjust opacity
|
|
516
|
+
map.layers.setOpacity('pm-road-vt', 0.5);
|
|
517
|
+
|
|
518
|
+
// 5. Hide an entire group
|
|
519
|
+
map.layers.hideGroup('city');
|
|
520
|
+
});
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
You can also use convenience methods directly on the `PowerMap` instance:
|
|
524
|
+
```javascript
|
|
525
|
+
await map.showLayer('pm-road-vt');
|
|
526
|
+
map.hideLayer('pm-road-vt');
|
|
527
|
+
await map.toggleLayer('pm-road-vt');
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
> [!IMPORTANT]
|
|
531
|
+
> **Satellite Layer**: High-resolution satellite imagery (`pm-maptiler-satellite-layer`) requires a `maptilerKey` provided in the initial `PowerMap` configuration.
|
|
532
|
+
|
|
533
|
+
---
|
|
534
|
+
|
|
535
|
+
## 11. π₯ Camera & Viewport Control
|
|
536
|
+
|
|
537
|
+
Take full control of the user's perspective with smooth transitions or direct manipulation.
|
|
538
|
+
|
|
539
|
+
### Movement Functions
|
|
540
|
+
|
|
541
|
+
| Method | Arguments | Description |
|
|
542
|
+
| :--- | :--- | :--- |
|
|
543
|
+
| `flyTo(options)` | `Object` | Smooth cinematic movement with options: `center`, `zoom`, `pitch`, `bearing`, `duration`. |
|
|
544
|
+
| `easeTo(options)` | `Object` | Linear transition movement. Same options as `flyTo`. |
|
|
545
|
+
| `jumpTo(options)` | `Object` | Instant teleportation without animation. |
|
|
546
|
+
| `fitBounds(bounds, opts)` | `Array, Object` | Fits view to show all coords. Options: `padding`. |
|
|
547
|
+
| `zoomIn()` | - | Smoothly zoom in by level 1. |
|
|
548
|
+
| `zoomOut()` | - | Smoothly zoom out by level 1. |
|
|
549
|
+
| `rotateLeft()` | - | Smoothly rotate left by 45 degrees. |
|
|
550
|
+
| `rotateRight()` | - | Smoothly rotate right by 45 degrees. |
|
|
551
|
+
|
|
552
|
+
### State Functions
|
|
553
|
+
|
|
554
|
+
| Method | Arguments | Description |
|
|
555
|
+
| :--- | :--- | :--- |
|
|
556
|
+
| `setPitch(deg, anim)` | `number, bool` | Set tilt angle (0-85). Smooth by default. |
|
|
557
|
+
| `setBearing(deg, anim)` | `number, bool` | Set direction (0-360). Smooth by default. |
|
|
558
|
+
| `getCenter()` | - | Returns `{lng, lat}` of the current map center. |
|
|
559
|
+
| `getZoom()` | - | Returns the current zoom level as a `number`. |
|
|
560
|
+
| `getPitch()` | - | Returns the current pitch (tilt) as a `number`. |
|
|
561
|
+
| `getBearing()` | - | Returns the current bearing (rotation) as a `number`. |
|
|
562
|
+
|
|
563
|
+
### Camera Examples
|
|
564
|
+
|
|
565
|
+
```javascript
|
|
566
|
+
// Fly into a specific warehouse view
|
|
567
|
+
map.flyTo({
|
|
568
|
+
center: [100.5, 13.8],
|
|
569
|
+
zoom: 17,
|
|
570
|
+
pitch: 60,
|
|
571
|
+
bearing: 45,
|
|
572
|
+
duration: 4000
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
// Fit view to a specific region
|
|
576
|
+
map.fitBounds([
|
|
577
|
+
[100.4, 13.7],
|
|
578
|
+
[100.6, 13.9]
|
|
579
|
+
], { padding: 40 });
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
<!--
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
## 8. π Simulation & Navigation
|
|
586
|
+
|
|
587
|
+
Advanced tracking simulation with 2D/3D model support. This is ideal for visualizing real-time vehicle movement.
|
|
588
|
+
|
|
589
|
+
### `startSimulation(route, options)` Options
|
|
590
|
+
|
|
591
|
+
| Option | Type | Default | Description |
|
|
592
|
+
| :--- | :--- | :--- | :--- |
|
|
593
|
+
| `route` | `Object` | **Required** | The route result object returned by `addRoute`. |
|
|
594
|
+
| `use3D` | `boolean` | `false` | If true, renders a 3D GLB model. If false, renders a 2D marker. |
|
|
595
|
+
| `modelUrl` | `string` | (Internal) | URL to a GLB model file for 3D simulation. |
|
|
596
|
+
| `scale` | `number` | `0.5` | Scale factor for the 3D model. |
|
|
597
|
+
|
|
598
|
+
### Simulation & Navigation Methods
|
|
599
|
+
|
|
600
|
+
| Method | Arguments | Description |
|
|
601
|
+
| :--- | :--- | :--- |
|
|
602
|
+
| `startSimulation(route, opts)` | `Object, Object` | Start 2D or 3D vehicle animation along a route. |
|
|
603
|
+
| `stopSimulation()` | - | Terminate current running simulation. |
|
|
604
|
+
| `startNavigation()` | - | Enable "Follow" camera mode during simulation. |
|
|
605
|
+
| `stopNavigation()` | - | Disable camera following. |
|
|
606
|
+
|
|
607
|
+
### Simulation & Navigation Example
|
|
608
|
+
|
|
609
|
+
```javascript
|
|
610
|
+
const myRoute = await map.addRoute({ ... });
|
|
611
|
+
|
|
612
|
+
// 1. Start simulation: Moving along the path
|
|
613
|
+
map.startSimulation(myRoute, {
|
|
614
|
+
use3D: true, // Awesome 3D car mode!
|
|
615
|
+
scale: 0.8,
|
|
616
|
+
modelUrl: "/assets/my-truck.glb"
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
// 2. Navigation Mode: Make the camera follow the car
|
|
620
|
+
map.startNavigation();
|
|
621
|
+
|
|
622
|
+
// 3. Listen to updates
|
|
623
|
+
map.onSimulationUpdate(({ position, step }) => {
|
|
624
|
+
console.log(`Car is at ${position}. Progress: ${step}`);
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
// 4. Terminate
|
|
628
|
+
// map.stopNavigation();
|
|
629
|
+
// map.stopSimulation();
|
|
630
|
+
```
|
|
631
|
+
-->
|
|
632
|
+
|
|
633
|
+
---
|
|
634
|
+
|
|
635
|
+
## 11. π₯ Event System
|
|
636
|
+
|
|
637
|
+
The SDK uses a semantic event system for a modern developer experience. All events can be chained.
|
|
638
|
+
|
|
639
|
+
### Event List & Arguments
|
|
640
|
+
|
|
641
|
+
| Method | Callback Arguments | Triggered When... |
|
|
642
|
+
| :--- | :--- | :--- |
|
|
643
|
+
| `onMapLoaded` | `{map}` | The map is fully initialized and layers can be added. |
|
|
644
|
+
| `onClick` | `{lngLat, point, ...}` | The map is clicked. |
|
|
645
|
+
| `onMapLongClick` | `{lngLat, point, ...}` | The map is long-pressed or right-clicked. |
|
|
646
|
+
| `onMarkerClick` | `{marker, originalEvent, options}` | A user clicks on any marker added via SDK. |
|
|
647
|
+
| `onPoiClick` | `{feature, lngLat, point}` | A user clicks on a Point of Interest (POI) on the base map. |
|
|
648
|
+
| `onModeChanged` | `{mode, options}` | The SDK operational mode (standard/indoor) has changed. |
|
|
649
|
+
| `onMarkerDrag` | `{marker, lngLat, options}` | A draggable marker is currently being moved. |
|
|
650
|
+
| `onMarkerDragEnd` | `{marker, lngLat, options}` | A user releases a draggable marker. |
|
|
651
|
+
| `onRouteCalculated` | `route` | A new route has been fetched and drawn. |
|
|
652
|
+
| `onGeometryExported` | `{data}` | GeoJSON data has been exported from the editor. |
|
|
653
|
+
| `layerEnsured` | `{layerId, group}` | A catalog layer has finished initializing sources. |
|
|
654
|
+
| `onError` | `{source, message}` | Any internal error (Auth, Routing, Map) occurs. |
|
|
655
|
+
|
|
656
|
+
### Event Subscription Example
|
|
657
|
+
|
|
658
|
+
```javascript
|
|
659
|
+
map.onMapLoaded((e) => {
|
|
660
|
+
console.log("Ready!");
|
|
661
|
+
})
|
|
662
|
+
.onClick((e) => {
|
|
663
|
+
console.log(`Map clicked at: ${e.lngLat}`);
|
|
664
|
+
})
|
|
665
|
+
.onMarkerClick((data) => {
|
|
666
|
+
alert(`You clicked marker: ${data.options.popupContent}`);
|
|
667
|
+
})
|
|
668
|
+
.onMarkerDragEnd((data) => {
|
|
669
|
+
console.log(`New Coordinate: ${data.lngLat.lng}, ${data.lngLat.lat}`);
|
|
670
|
+
});
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
---
|
|
674
|
+
|
|
675
|
+
<!-- ## 9. π Security & Production Best Practices
|
|
676
|
+
|
|
677
|
+
For production deployments, **NEVER** expose your `clientSecret` in client-side code. This exposes your account to unauthorized usage.
|
|
678
|
+
|
|
679
|
+
**Recommended Proxy Setup:**
|
|
680
|
+
|
|
681
|
+
1. Your Frontend calls your own Backend (e.g., `POST /api/get-route`).
|
|
682
|
+
2. Your Backend appends `clientId` and `clientSecret` and calls PowerMap API.
|
|
683
|
+
3. Your Backend returns the result to your Frontend.
|
|
684
|
+
|
|
685
|
+
**SDK Setup for Proxy:**
|
|
686
|
+
```javascript
|
|
687
|
+
const map = new PowerMap({
|
|
688
|
+
baseUrl: "/api/my-proxy", // Point to your backend route
|
|
689
|
+
mapApiKey: "PUBLIC_MAP_KEY"
|
|
690
|
+
// No clientSecret needed here; the backend handles it!
|
|
691
|
+
});
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
--- -->
|
|
695
|
+
|
|
696
|
+
## 12. π Search & Geocoding
|
|
697
|
+
|
|
698
|
+
The Web SDK provides built-in search capabilities using the PowerMap Search API.
|
|
699
|
+
|
|
700
|
+
### `search(query, options)`
|
|
701
|
+
|
|
702
|
+
Available options:
|
|
703
|
+
|
|
704
|
+
- `lang`: `'th'` (default) or `'en'`.
|
|
705
|
+
- `location`: `[latitude, longitude]` to prioritize nearby results.
|
|
706
|
+
|
|
707
|
+
```javascript
|
|
708
|
+
const results = await map.search("Central World", { lang: 'en' });
|
|
709
|
+
|
|
710
|
+
results.forEach(item => {
|
|
711
|
+
console.log(`${item.name}: ${item.address}`);
|
|
712
|
+
console.log(`Position: ${item.position}`); // [lat, lng]
|
|
713
|
+
});
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
---
|
|
717
|
+
|
|
718
|
+
## 13. πΉοΈ Interactive Metadata (POI)
|
|
719
|
+
|
|
720
|
+
The SDK can detect when a user clicks on a Point of Interest (POI) on the base map (e.g., shopping malls, parks, gas stations).
|
|
721
|
+
|
|
722
|
+
```javascript
|
|
723
|
+
map.onPoiClick(({ feature, lngLat }) => {
|
|
724
|
+
const name = feature.properties.name_t;
|
|
725
|
+
console.log(`User clicked on: ${name}`);
|
|
726
|
+
|
|
727
|
+
// Highlighting the POI with a popup
|
|
728
|
+
map.addMarker({
|
|
729
|
+
id: 'poi-temp',
|
|
730
|
+
position: [lngLat.lng, lngLat.lat],
|
|
731
|
+
popupContent: { title: name, tag: 'POI' }
|
|
732
|
+
});
|
|
733
|
+
});
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
---
|
|
737
|
+
|
|
738
|
+
## 14. π§° Utility Functions
|
|
739
|
+
|
|
740
|
+
SDK provides handy formatters for displaying routing data.
|
|
741
|
+
|
|
742
|
+
| Method | Arguments | Description |
|
|
743
|
+
| :--- | :--- | :--- |
|
|
744
|
+
| `formatDistance(meters)` | `number` | Converts meters to string (e.g., "1.25 km" or "500 m"). |
|
|
745
|
+
| `formatDuration(seconds)` | `number` | Converts seconds to string (e.g., "1h 30m" or "5 min"). |
|
|
746
|
+
|
|
747
|
+
---
|
|
748
|
+
|
|
749
|
+
## 15. π Geometry Engine & Editor
|
|
750
|
+
|
|
751
|
+
The PowerMap SDK includes a professional-grade geometry engine and a built-in UI for creating, editing, and managing spatial data. This is optimized for both simple outdoor measuring and complex indoor mapping.
|
|
752
|
+
|
|
753
|
+
### 15.1 The Editor Toolbar
|
|
754
|
+
|
|
755
|
+
Enable a premium, built-in toolbar that handles the entire drawing lifecycle.
|
|
756
|
+
|
|
757
|
+
```javascript
|
|
758
|
+
map.editor.enable({
|
|
759
|
+
position: 'bottom-left', // 'top-left', 'top-right', 'bottom-left', 'bottom-right'
|
|
760
|
+
mode: 'standard' // 'standard' (Logistics) or 'indoor' (Indoor Mapping)
|
|
761
|
+
});
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
#### **Interactive Tools**
|
|
765
|
+
|
|
766
|
+
- **Standard Mode**: Point, Line (Measure), Polygon (Area), Rectangle.
|
|
767
|
+
- **Indoor Mode**: Beacon, POI, Walkway, Floor, Obstacle.
|
|
768
|
+
|
|
769
|
+
### 15.2 Measurement & Info Panel
|
|
770
|
+
|
|
771
|
+
When a feature is selected, a real-time measurement panel appears, providing:
|
|
772
|
+
|
|
773
|
+
- **Distance**: For lines and walkways.
|
|
774
|
+
- **Area**: For polygons and floors.
|
|
775
|
+
- **3D Height**: For extruded features.
|
|
776
|
+
- **Live Coordinates**: For Points (POIs and Beacons), showing relative X/Y metrics dynamically during placement inside buildings, or Latitude/Longitude for outdoor placements.
|
|
777
|
+
|
|
778
|
+
### 15.3 Property Editor UI
|
|
779
|
+
|
|
780
|
+
The PowerMap Editor includes a functional **Property Editor** panel (available in `indoor` mode or via the settings icon). It allows you to manage:
|
|
781
|
+
|
|
782
|
+
- **Core Metadata**: ID and Name.
|
|
783
|
+
- **Classification**: Independent `feature_type` (e.g., level, building, venue), explicit Categories, and Ordinal/Level (for stacking floors).
|
|
784
|
+
- **Visual Styling**: Choose colors and icons (for Beacons and POIs).
|
|
785
|
+
- **Custom Properties**: Add unlimited key-value pairs for your own business logic.
|
|
786
|
+
|
|
787
|
+
### 15.4 3D & Advanced Tools
|
|
788
|
+
|
|
789
|
+
Take drawing to the next dimension:
|
|
790
|
+
|
|
791
|
+
- **Extrude**: Transform 2D polygons into 3D buildings or obstacles by setting the `height` property.
|
|
792
|
+
- **Rotate & Clone**: Rapidly duplicate and align features.
|
|
793
|
+
- **Vertical Alignment**: Manage `ordinal` properties to ensure data belongs to the correct floor level.
|
|
794
|
+
|
|
795
|
+
### 15.5 Data Lifecycle (Import/Export)
|
|
796
|
+
|
|
797
|
+
The Editor facilitates easy data exchange via GeoJSON:
|
|
798
|
+
|
|
799
|
+
- **Export**: Built-in "Download" button in the toolbar, or call `map.geometry.export()`.
|
|
800
|
+
- **Import**: Built-in "Upload" button, or call `map.geometry.import(geojson)`.
|
|
801
|
+
- **IMDF Compliance**: Indoor fields (`feature_type`, `category`, `ordinal`) are mapped to IMDF format automatically.
|
|
802
|
+
|
|
803
|
+
### 15.6 Unified Programmatic CRUD API
|
|
804
|
+
|
|
805
|
+
> For the full reference, see [GEOMETRY.md](./GEOMETRY.md)
|
|
806
|
+
|
|
807
|
+
All operations β whether from draw tool or code β emit the same `geometryChanged` event.
|
|
808
|
+
|
|
809
|
+
```javascript
|
|
810
|
+
// CREATE (standard)
|
|
811
|
+
const id = map.geometry.create('polygon', {
|
|
812
|
+
coordinates: [[[100.5,13.7],[100.6,13.7],[100.6,13.8],[100.5,13.7]]],
|
|
813
|
+
properties: { fillColor: '#4f46e5', height: 10 }
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
// CREATE (indoor)
|
|
817
|
+
const beaconId = map.geometry.create('beacon', {
|
|
818
|
+
lngLat: [100.52, 13.75],
|
|
819
|
+
properties: { ordinal: 1, name: 'BKK-01' }
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
// READ
|
|
823
|
+
map.geometry.getById(id); // Feature | null
|
|
824
|
+
map.geometry.getByKind('beacon'); // Feature[]
|
|
825
|
+
map.geometry.query().byKind('poi').byFloor(1).toArray(); // Feature[]
|
|
826
|
+
|
|
827
|
+
// UPDATE
|
|
828
|
+
map.geometry.update(id, {
|
|
829
|
+
properties: { name: 'Updated Name', color: '#ff0000' }
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
// DELETE
|
|
833
|
+
map.geometry.remove(id);
|
|
834
|
+
map.geometry.clear();
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
**All CRUD Methods:**
|
|
838
|
+
|
|
839
|
+
| Method | Arguments | Description |
|
|
840
|
+
| :--- | :--- | :--- |
|
|
841
|
+
| `create(kind, options)` | `string, Object` | Create a standard or indoor feature programmatically |
|
|
842
|
+
| `update(id, patch)` | `string, Object` | Update properties and/or geometry of an existing feature |
|
|
843
|
+
| `getById(id)` | `string` | Returns a single GeoJSON Feature or null |
|
|
844
|
+
| `getByKind(kind)` | `string` | Returns all indoor features matching the given kind |
|
|
845
|
+
| `query()` | - | Returns a chainable `GeometryQuery` builder |
|
|
846
|
+
| `add(geojson)` | `Object` | Manually add a raw GeoJSON feature (legacy) |
|
|
847
|
+
| `remove(id)` | `string\|string[]` | Remove one or multiple features by ID |
|
|
848
|
+
| `clear()` | - | Remove all geometry from the map |
|
|
849
|
+
| `setFeatureHeight(id, m)` | `string, number` | Extrude a polygon into a 3D volume |
|
|
850
|
+
| `getGeoJSON(id?, opts)` | `string, Object` | Returns feature(s) as GeoJSON (`options.format: 'imdf' \| 'standard'`) |
|
|
851
|
+
| `export(opts)` | `Object` | Returns all features as GeoJSON |
|
|
852
|
+
| `import(geojson, opts)` | `Object, Object` | Loads GeoJSON/IMDF data |
|
|
853
|
+
|
|
854
|
+
### 15.7 GeometryStore β Placeholder API Bridge
|
|
855
|
+
|
|
856
|
+
`map.geometry.store` provides placeholder hooks that fire on every geometry change regardless of origin (draw tool or code). Use this to connect your own backend API.
|
|
857
|
+
|
|
858
|
+
```javascript
|
|
859
|
+
// Register before the map loads
|
|
860
|
+
map.geometry.store.onCreate(async (payload) => {
|
|
861
|
+
console.log('[Placeholder] CREATE', payload);
|
|
862
|
+
// await fetch('/api/features', { method: 'POST', body: JSON.stringify(payload) })
|
|
863
|
+
});
|
|
864
|
+
map.geometry.store.onUpdate(async (payload) => {
|
|
865
|
+
console.log('[Placeholder] UPDATE', payload);
|
|
866
|
+
// await fetch(`/api/features/${payload.featureId}`, { method: 'PUT', body: JSON.stringify(payload) })
|
|
867
|
+
});
|
|
868
|
+
map.geometry.store.onDelete(async (payload) => {
|
|
869
|
+
console.log('[Placeholder] DELETE', payload);
|
|
870
|
+
// await fetch(`/api/features/${payload.featureId}`, { method: 'DELETE' })
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
// Used when a developer clicks the "Save" icon on the draw toolbar
|
|
874
|
+
map.geometry.store.onSave(async (data) => {
|
|
875
|
+
console.log('[Placeholder] MANUAL SAVE TRIGGERED', data);
|
|
876
|
+
// const success = await saveAllToDatabase(data);
|
|
877
|
+
// if (success) map.geometry.store.setDirty(false); // Clear unsaved warnings
|
|
878
|
+
});
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
<!--
|
|
882
|
+
#### Unsaved Changes Protection
|
|
883
|
+
The SDK tracks if there are unsaved drawing changes (`isDirty = true`):
|
|
884
|
+
1. **Closing Tab or Refreshing**: The browser will display its Native Alert (this cannot be styled due to browser security).
|
|
885
|
+
2. **SPA / In-App Navigation**: You can trigger a **Custom UI Modal** (with blue/white buttons) using our built-in method before changing menus:
|
|
886
|
+
|
|
887
|
+
```javascript
|
|
888
|
+
const action = await map.geometry.store.promptUnsavedChanges();
|
|
889
|
+
if (action === 'save') {
|
|
890
|
+
// await api.saveData();
|
|
891
|
+
} else if (action === 'cancel') {
|
|
892
|
+
return; // Stay on page
|
|
893
|
+
}
|
|
894
|
+
```
|
|
895
|
+
|
|
896
|
+
Clear the protection manually once your API saves successfully:
|
|
897
|
+
```javascript
|
|
898
|
+
map.geometry.store.setDirty(false);
|
|
899
|
+
```
|
|
900
|
+
-->
|
|
901
|
+
|
|
902
|
+
Each callback receives a clean `payload` with `user_` prefixes stripped:
|
|
903
|
+
|
|
904
|
+
```javascript
|
|
905
|
+
{
|
|
906
|
+
operation: 'create' | 'update' | 'delete',
|
|
907
|
+
source: 'draw' | 'programmatic',
|
|
908
|
+
format: 'standard' | 'indoor',
|
|
909
|
+
featureId: 'abc123',
|
|
910
|
+
kind: 'beacon' | 'poi' | ... | null, // null for standard
|
|
911
|
+
floor: 1 | null, // ordinal, null for standard
|
|
912
|
+
timestamp: '2026-03-27T...',
|
|
913
|
+
geometry: { type: 'Point', coordinates: [...] },
|
|
914
|
+
properties: { name: 'BKK-01', color: '#fbbf24' }
|
|
915
|
+
}
|
|
916
|
+
```
|
|
917
|
+
|
|
918
|
+
> [!NOTE]
|
|
919
|
+
> If no callback is registered, the SDK logs all events to the console automatically with π¦ CREATE / βοΈ UPDATE / ποΈ DELETE icons.
|
|
920
|
+
|
|
921
|
+
### 15.8 SDK Events
|
|
922
|
+
|
|
923
|
+
```javascript
|
|
924
|
+
// Subscribe to every geometry change (create/update/delete)
|
|
925
|
+
map.on('geometryChanged', ({ operation, source, feature }) => {
|
|
926
|
+
// operation: 'create' | 'update' | 'delete'
|
|
927
|
+
// source: 'draw' | 'programmatic'
|
|
928
|
+
// feature: GeoJSON Feature (null for delete)
|
|
929
|
+
});
|
|
930
|
+
|
|
931
|
+
// Subscribe to live coordinate/property updates from the UI Editor
|
|
932
|
+
// Use this to refresh UI panels instantly without waiting for or triggering a backend save.
|
|
933
|
+
map.on('geometryLiveUpdate', ({ type, features }) => {
|
|
934
|
+
// type: 'update'
|
|
935
|
+
// features: Array of GeoJSON Features that are currently being edited
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
// Listen to selection changes
|
|
939
|
+
map.on('geometrySelectionChanged', ({ features }) => {
|
|
940
|
+
// Array of currently selected feature objects
|
|
941
|
+
});
|
|
942
|
+
```
|
|
943
|
+
|
|
944
|
+
---
|
|
945
|
+
|
|
946
|
+
## 16. π Indoor Mapping
|
|
947
|
+
|
|
948
|
+
The PowerMap SDK provides a robust API for managing indoor environments, including floor switching, category filtering, and specialized geometry handling.
|
|
949
|
+
|
|
950
|
+
### 16.1 Enabling Indoor Mode
|
|
951
|
+
|
|
952
|
+
Switch to indoor mode to enable building-specific logic and specialized drawing tools (e.g., Beacons, Walkways).
|
|
953
|
+
|
|
954
|
+
```javascript
|
|
955
|
+
map.setMode('indoor');
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
### 16.2 Floor Management (`map.indoor`)
|
|
959
|
+
|
|
960
|
+
When in indoor mode, use the `map.indoor` facade to control the vertical context of the map.
|
|
961
|
+
|
|
962
|
+
| Method | Arguments | Description |
|
|
963
|
+
| :--- | :--- | :--- |
|
|
964
|
+
| `setFloor(ordinal, opts)` | `number, Object` | Switch to a specific floor level (e.g., 1, 2, -1). |
|
|
965
|
+
| `getAvailableFloors()` | - | Returns a sorted array of all unique floor ordinals found in the map data. |
|
|
966
|
+
| `getCurrentFloor()` | - | Returns the ordinal of the currently active floor. |
|
|
967
|
+
|
|
968
|
+
#### Switching Floors with Animation
|
|
969
|
+
|
|
970
|
+
```javascript
|
|
971
|
+
// Switch to Floor 2 with a smooth fade transition
|
|
972
|
+
map.indoor.setFloor(2, { animate: true });
|
|
973
|
+
```
|
|
974
|
+
|
|
975
|
+
#### Building a Dynamic Floor Selector UI
|
|
976
|
+
|
|
977
|
+
```javascript
|
|
978
|
+
// Get all available floors to populate a dropdown
|
|
979
|
+
const floors = map.indoor.getAvailableFloors();
|
|
980
|
+
// Returns: [1, 2, 3] or [] if no indoor data is found
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
### 16.3 Visibility Control
|
|
984
|
+
|
|
985
|
+
Toggle the visibility of specific indoor data categories. These settings persist across floor changes.
|
|
986
|
+
|
|
987
|
+
```javascript
|
|
988
|
+
// Toggle individual categories
|
|
989
|
+
map.indoor.setCategoryVisible('poi', true); // Points of Interest
|
|
990
|
+
map.indoor.setCategoryVisible('beacon', false); // Beacons/Sensors
|
|
991
|
+
map.indoor.setCategoryVisible('walkway', true); // Navigation Paths
|
|
992
|
+
map.indoor.setCategoryVisible('obstacle', true); // 3D Obstacles/Buildings
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
### 16.4 Indoor Data Kinds
|
|
996
|
+
|
|
997
|
+
The SDK categorizes indoor features using the `user_kind` (or `kind`) property:
|
|
998
|
+
|
|
999
|
+
- **floor**: The walkable surface area of a floor (Polygon).
|
|
1000
|
+
- **obstacle**: Non-walkable areas. Can be extruded into 3D.
|
|
1001
|
+
- **walkway**: Navigable path lines for indoor routing.
|
|
1002
|
+
- **poi**: Indoor points of interest with custom icons.
|
|
1003
|
+
- **beacon**: Positioning hardware or logical points.
|
|
1004
|
+
|
|
1005
|
+
### 16.5 Event Synchronization
|
|
1006
|
+
|
|
1007
|
+
```javascript
|
|
1008
|
+
// Listen for mode changes
|
|
1009
|
+
map.on('onModeChanged', ({ mode }) => {
|
|
1010
|
+
console.log(`Mode switched to: ${mode}`); // 'standard' or 'indoor'
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
// Sync UI when data is added/deleted or imported
|
|
1014
|
+
map.on('geometryChanged', () => {
|
|
1015
|
+
const updatedFloors = map.indoor.getAvailableFloors();
|
|
1016
|
+
// Refresh your floor selector here
|
|
1017
|
+
});
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
---
|
|
1021
|
+
|
|
1022
|
+
## 17. β¨οΈ Keyboard Shortcuts
|
|
1023
|
+
|
|
1024
|
+
The SDK provides built-in productivity shortcuts whenever the geometry engine is active:
|
|
1025
|
+
|
|
1026
|
+
- **Ctrl + C**: Copy selected geometry.
|
|
1027
|
+
- **Ctrl + V**: Paste geometry at the current cursor position.
|
|
1028
|
+
- **Delete / Backspace**: Remove selected features.
|
|
1029
|
+
|
|
1030
|
+
---
|
|
1031
|
+
|
|
1032
|
+
## 18. π©Ί Debug & Telemetry
|
|
1033
|
+
|
|
1034
|
+
Enable the Debug HUD during development to monitor performance and coordinates.
|
|
1035
|
+
|
|
1036
|
+
```javascript
|
|
1037
|
+
// Enable debug panel
|
|
1038
|
+
map.debug().enable({ position: 'top-right' });
|
|
1039
|
+
|
|
1040
|
+
// Toggle specific metrics
|
|
1041
|
+
map.debug().showFPS(true);
|
|
1042
|
+
map.debug().showCoordinate(false);
|
|
1043
|
+
|
|
1044
|
+
// Disable
|
|
1045
|
+
map.debug().disable();
|
|
1046
|
+
```
|
|
1047
|
+
|
|
1048
|
+
---
|
|
1049
|
+
|
|
1050
|
+
## 19. π Build & Development
|
|
1051
|
+
|
|
1052
|
+
For contributors to the SDK core:
|
|
1053
|
+
|
|
1054
|
+
### Contributor Guide
|
|
1055
|
+
|
|
1056
|
+
1. **Setup Environment**:
|
|
1057
|
+
|
|
1058
|
+
```bash
|
|
1059
|
+
npm install
|
|
1060
|
+
```
|
|
1061
|
+
|
|
1062
|
+
2. **Local Development**:
|
|
1063
|
+
|
|
1064
|
+
```bash
|
|
1065
|
+
npm run dev
|
|
1066
|
+
```
|
|
1067
|
+
|
|
1068
|
+
3. **Production Build**:
|
|
1069
|
+
|
|
1070
|
+
```bash
|
|
1071
|
+
npm run build
|
|
1072
|
+
```
|
|
1073
|
+
|
|
1074
|
+
### Project Structure
|
|
1075
|
+
|
|
1076
|
+
- `/src/core`: Map lifecycle and shared managers.
|
|
1077
|
+
- `/src/geometry`: Drawing engine and coordinate logic.
|
|
1078
|
+
- `/src/editor`: Toolbar UI and control components.
|
|
1079
|
+
- `/src/routing`: Navigation and path-finding.
|
|
1080
|
+
- `/src/auth`: Security and API tokens.
|
|
1081
|
+
|
|
1082
|
+
---
|
|
1083
|
+
|
|
1084
|
+
## π Appendix A: Complete SDK Event List
|
|
1085
|
+
|
|
1086
|
+
The SDK emits the following events through its central `EventBus`. You can subscribe to them via `map.on(eventName, callback)`.
|
|
1087
|
+
|
|
1088
|
+
### π Core / Global
|
|
1089
|
+
| Event | Payload | Description |
|
|
1090
|
+
|---|---|---|
|
|
1091
|
+
| `mapLoaded` | `{ map }` | Fired when PowerMap finishes loading the style and is ready. |
|
|
1092
|
+
| `mapStyleChanged` | `{ styleKey }` | Fired after switching the base map style. |
|
|
1093
|
+
| `modeChanged` | `{ mode, options }` | Fired when SDK interactive mode changes (e.g. `mode_free` β `mode_routing`). |
|
|
1094
|
+
| `error` | `PowerMapError` | Fired globally for SDK internal errors. |
|
|
1095
|
+
|
|
1096
|
+
### π Geometry & Draw
|
|
1097
|
+
| Event | Payload | Description |
|
|
1098
|
+
|---|---|---|
|
|
1099
|
+
| `geometryChanged` | `{ operation/type, source, feature(s) }` | Fired upon completion of a CRUD operation or drawing action. |
|
|
1100
|
+
| `geometryLiveUpdate` | `{ type: 'update', features }` | Fired rapidly during property/coordinate editing for real-time UI mapping. |
|
|
1101
|
+
| `geometrySelectionChanged` | `{ features }` | Fired when the active selection changes in drawing mode. |
|
|
1102
|
+
|
|
1103
|
+
### π Markers
|
|
1104
|
+
| Event | Payload | Description |
|
|
1105
|
+
|---|---|---|
|
|
1106
|
+
| `markerAdded` | `{ marker, options }` | Fired when a new marker is added to the map. |
|
|
1107
|
+
| `markerRemoved` | `{ marker\|id }` | Fired when a marker is explicitly removed. |
|
|
1108
|
+
| `markerClick` | `{ marker, originalEvent, options }` | Fired when a marker is clicked. |
|
|
1109
|
+
| `markerDrag` | `{ marker, lngLat, options }` | Fired continuously while a draggable marker is being dragged. |
|
|
1110
|
+
| `markerDragEnd` | `{ marker, lngLat, options }` | Fired when a marker is dropped after dragging. |
|
|
1111
|
+
| `markersCleared`| `-` | Fired when all markers on the map are cleared. |
|
|
1112
|
+
|
|
1113
|
+
### π’ Indoor
|
|
1114
|
+
| Event | Payload | Description |
|
|
1115
|
+
|---|---|---|
|
|
1116
|
+
| `floorChanged` | `ordinal (number)` | Fired when the active indoor floor changes. |
|
|
1117
|
+
|
|
1118
|
+
### πΊοΈ Routing & Navigation
|
|
1119
|
+
| Event | Payload | Description |
|
|
1120
|
+
|---|---|---|
|
|
1121
|
+
| `routeRequestStart`| `options` | Fired when a route calculation request is initiated. |
|
|
1122
|
+
| `routeCalculated` | `GeoJSON Feature` | Fired upon successful calculation of a MultiLineString route. |
|
|
1123
|
+
| `routeCleared` | `-` | Fired when the active route is removed from the map. |
|
|
1124
|
+
| `navigationStart` | `-` | Fired when Turn-by-Turn navigation is initiated. |
|
|
1125
|
+
| `navigationEnd` | `-` | Fired when Turn-by-Turn navigation stops. |
|
|
1126
|
+
| `simulationStart` | `-` | Fired when GPS Route Simulator starts running. |
|
|
1127
|
+
| `simulationUpdate`| `{ position, step }` | Fired at every tick of the GPS simulator with the interpolated position. |
|
|
1128
|
+
| `simulationEnd` | `-` | Fired when GPS simulation halts or reaches the destination. |
|
|
1129
|
+
|
|
1130
|
+
### π¨ Layers
|
|
1131
|
+
| Event | Payload | Description |
|
|
1132
|
+
|---|---|---|
|
|
1133
|
+
| `layerVisibilityChanged`| `{ layerId, visible }` | Fired when a system layer's visibility is toggled. |
|
|
1134
|
+
| `layerEnsured` | `{ layerId, group }` | Fired when the SDK auto-creates a missing required layer group. |
|
|
1135
|
+
|
|
1136
|
+
---
|
|
1137
|
+
|
|
1138
|
+
Β© 2026 PowerMap Development Team. All rights reserved.
|