@aapicoits/powermap-web-sdk
v1.0.1
Published
A professional-grade, high-performance Map SDK. Optimized for logistics, real-time tracking, indoor mapping, and 3D vehicle simulations.
Readme
PowerMap Web SDK 🚀
A professional-grade, high-performance Map SDK. Optimized for logistics, real-time tracking, indoor mapping, and 3D vehicle simulations.
2. 🏛️ Architecture Overview
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.
graph TD
App[Web Application] --> PM[PowerMap Instance]
subgraph "Facade Layer (Fluent API)"
PM
end
subgraph "Manager Layer (Lazy Loading)"
PM --> Registry[Component Registry]
Registry --> MarkerMgr[MarkerManager]
Registry --> SearchMgr[SearchManager]
Registry --> RouteMgr[RoutingManager]
Registry --> GeomMgr[GeometryManager]
Registry --> EditorMgr[EditorManager]
Registry --> LayerMgr[LayerManager]
end
subgraph "Service Layer"
PM --> AuthMgr[AuthManager]
PM --> Events[EventBus]
AuthMgr --> Cache[Token Cache]
end
subgraph "Visual Engine"
PM --> ML[PowerMap Visual Engine]
end
Cache -.-> Storage[(Local Storage)]
AuthMgr -.-> API((PowerMap APIs))3. 📦 Installation
Install the package via npm or yarn:
npm install @powermap/web-sdk4. 🚀 Quick Start
Get a map up and running in less than 2 minutes. The PowerMap SDK is designed for chaining and ease of use.
Step A: Prepare your HTML
<div id="map" style="width: 100%; height: 500px; border-radius: 8px; overflow: hidden;"></div>Step B: Initialize and Style
import { PowerMap } from "@powermap/web-sdk";
// 1. Initialize with dual-auth system
const map = new PowerMap({
container: "map",
center: [100.5018, 13.7563], // Bangkok [Longitude, Latitude]
zoom: 14,
mapApiKey: "YOUR_MAP_API_KEY", // Public key for tiles and base layers
auth: {
clientId: "YOUR_CLIENT_ID", // Service Credentials for backend APIs
clientSecret: "YOUR_CLIENT_SECRET"
},
previewOnly: true // Optional: Lock all user interactions (Static View)
});
// 2. Chaining for smooth DX
map.onMapLoaded((e) => {
console.log("PowerMap is ready!", e.map);
// Add a marker as soon as the map loads
map.addMarker({
position: [100.5018, 13.7563],
type: "start",
popupContent: "<strong>Origin Point</strong>"
});
})
.setStyle('th') // Options: 'th', 'en', 'dark', 'gray'
.flyTo({ zoom: 16, pitch: 45 });[!TIP] 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.
5. 🔗 Method Chaining
The PowerMap SDK is built with a fluent interface, allowing you to link multiple configuration steps and event listeners together in a single statement.
Supported Methods
Most methods that perform an action on the map (styling, camera movement, or event registration) return the PowerMap instance to facilitate chaining.
| Group | Chainable Methods |
| :--- | :--- |
| Events | on, off, onMapLoaded, onClick, onMapLongClick, onMarkerClick, onPoiClick, onMarkerDrag, onMarkerDragEnd, onRouteCalculated, onError |
| Camera | flyTo, easeTo, fitBounds, jumpTo, setPitch, setBearing, zoomIn, zoomOut, rotateLeft, rotateRight |
| Map Style | setStyle |
| Markers | removeMarker, updatePopup, clearMarkers |
| Search | search |
| Routing | clearRoutes |
| Layers | showLayer, hideLayer, toggleLayer |
[!NOTE] Methods that return a Promise (like
addRoute) or a new Object (likeaddMarkerorgetRouteDetails) interrupt the chain.
Chaining Example
map.onMapLoaded(() => console.log("Ready!"))
.onMarkerClick(data => console.log("Clicked:", data))
.setStyle('dark')
.flyTo({ center: [100.5, 13.7], zoom: 12 })
.setPitch(45);6. 🛠️ Configuration Reference
When creating a new PowerMap(options), the following options are available:
| Option | Type | Default | Required | Description |
| :--- | :--- | :--- | :--- | :--- |
| container | string | - | Yes | The HTML element ID where the map will be rendered. |
| mapApiKey | string | - | Yes | Your PowerMap Public API Key for map tiles. |
| auth | Object | - | Yes | Service Credentials. Used for connecting to backend APIs of PowerMap services (e.g., Routing, Optimization). Contains clientId and clientSecret. |
| center | [lng, lat] | [100, 13] | No | The initial center point of the map. |
| zoom | number | 14 | No | Total zoom level (0-22). |
| baseUrl | string | - | No | Optional proxy URL (e.g., /api/proxy). If set, the SDK will route all API requests through this URL. |
| maptilerKey | string | - | No | Your MapTiler API Key. Required if you intend to use high-resolution satellite layers. |
| previewOnly | boolean | false | No | If true, the map will be non-interactive (no zoom, pan, rotate, or pitch). Perfect for static map previews. |
Core Instance Methods
These methods are available on the PowerMap instance to manage the map state and lifecycle.
| Method | Arguments | Description |
| :--- | :--- | :--- |
| on(event, cb) | string, function | Subscribe to any map event (see Section 9). |
| off(event, cb) | string, function | Unsubscribe from a previously registered event. |
| setMode(mode, opts) | string, Object | Switch between 'standard' and 'indoor' modes. |
| setStyle(name) | string | Change the map style (e.g., 'th', 'dark'). |
| setNavigationControl(opts) | boolean\|Object | Enable/disable or customize zoom/compass controls. |
| setFullscreenControl(opts) | boolean\|Object | Enable/disable fullscreen control. |
| debug() | - | Access the Debug Manager for telemetry and performance tools. |
| onError(callback) | function | Global error listener for Auth, Map, or Routing issues. |
7. 📍 Marker Management
Our marker system simplifies standard map markers with built-in presets for logistics and easy event handling.
addMarker(options) Options
| Option | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| position | [lng, lat] | Required | Coordinates for the marker. |
| type | string | 'default' | Preset icons: 'start', 'destination', 'waypoint'. |
| iconUrl | string | - | Custom image URL to use as the icon. Overrides type. |
| width/height | string | '30px'/'38px' | CSS dimensions for the marker icon. |
| anchor | string | 'bottom' | Where the marker is anchored to the coordinate. |
| draggable | boolean | false | If true, user can drag the marker. Fires onMarkerDrag events. |
| rotation | number | 0 | Icon rotation in degrees. |
| popupContent | string|Object | - | HTML string or Structured Object to display in a premium popup. |
| id | string | - | Unique ID to track this marker. Allows retrieval via getMarker(id). |
| color | string | - | Custom hex color for pinpoint marker (e.g. '#ff4444'). Uses High-Accuracy GL Rendering. |
| icon | string | - | FontAwesome icon class for pinpoint marker (e.g. 'fas fa-car'). |
| element | HTMLElement | - | Use a raw HTML element as a fully custom marker. |
[!IMPORTANT] High-Accuracy Rendering: Markers with a
colorproperty 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.
Marker Examples
Standard Presets
map.addMarker({
position: [100.5, 13.7],
type: 'start', // 'start', 'destination', 'waypoint'
popupContent: "Start Point"
});Premium Colored Markers (No Image Required)
map.addMarker({
position: [100.52, 13.78],
color: '#f97316', // Vibrant Orange
icon: 'fas fa-store', // FontAwesome Icon
draggable: true, // Enable high-accuracy dragging
popupContent: {
title: "Power Shop",
tag: "Retail",
body: "Custom colored marker with icon."
}
});Draggable Markers & Events
You can enable dragging for any marker. For GL-rendered markers (those with color), dragging is handled with sub-pixel precision.
const marker = map.addMarker({
position: [100.5, 13.7],
color: '#3b82f6',
draggable: true
});
// 1. Listen via Proxy (Recommended)
marker.on('drag', (e) => console.log('Current:', e.lngLat));
marker.on('dragend', (e) => console.log('Final:', e.lngLat));
// 2. Listen via Global Event Bus
map.onMarkerDrag(({ marker, lngLat }) => {
console.log(`Marker is moving to: ${lngLat.lng}, ${lngLat.lat}`);
});Structured Popup Content & Actions
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:
map.addMarker({
position: [100.5, 13.7],
popupContent: {
title: "Siam Paragon",
tag: "Mall",
body: "The pride of Bangkok with world-class shopping.",
footer: "Open: 10:00 - 22:00",
actions: [
{ label: "View Details", url: "https://example.com/details", target: "_blank" },
{ label: "Navigate Here", url: "https://example.com/nav", color: "#10b981" }
]
}
});Marker Methods
| Method | Arguments | Description |
| :--- | :--- | :--- |
| addMarker(options) | Object | Create and add a marker. Returns marker instance. |
| getMarker(id) | string | Retrieve a marker instance by its unique ID. |
| removeMarker(markerOrId) | Object\|string | Remove a marker using instance or ID. |
| updatePopup(marker, content) | Object, string\|Object | Update the popup content (HTML or Structured Object). |
| clearMarkers() | - | Remove all markers from the map. |
8. 🎨 CSS Customization
The PowerMap SDK automatically wraps all map elements in a global .powermap-sdk class and provides branded aliases for all internal UI components.
The Global Wrapper
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.
<!-- Input -->
<div id="map"></div>
<!-- Output at runtime -->
<div id="map" class="powermap-sdk">...</div>Premium Class Aliases
To make customization even more intuitive, we've provided branded CSS classes that you can target directly:
| Feature | PowerMap Class | Description |
| :--- | :--- | :--- |
| Marker | .pm-marker | The main container for any marker. |
| Popup | .pm-popup | The root container of a popup. |
| Popup Content | .pm-popup-content | The main content area (glassmorphism). |
| Popup Header | .pm-popup-header | The header (part of structured content). |
| Popup Body | .pm-popup-body | The body text area. |
| Popup Tag | .pm-popup-tag | The category tag. |
| Popup Footer | .pm-popup-footer | The bottom meta info area. |
| Popup Close | .pm-popup-close | The close button element. |
| Popup Tip | .pm-popup-tip | The triangular tip pointing to the marker. |
| Nav Group | .pm-ctrl-group | The container of zoom/compass buttons. |
| Zoom In | .pm-ctrl-zoom-in | The zoom in button. |
| Zoom Out | .pm-ctrl-zoom-out | The zoom out button. |
| Compass | .pm-ctrl-compass | The compass/rotation button. |
Premium Overrides
Target your own CSS using the scoped wrapper or branded aliases:
/* Change the header color of structured popups */
.powermap-sdk .pm-popup-header {
background: linear-gradient(135deg, #000 0%, #333 100%);
}
/* Customize the glassmorphism background of all popups */
.powermap-sdk .pm-popup-content {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20px);
}
/* Custom Marker Scale */
.powermap-sdk .pm-marker {
transform: scale(1.1);
}setNavigationControl(options) Options
| Option | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| position | string | 'top-right' | Position on the map: 'top-left', 'top-right', 'bottom-left', 'bottom-right'. |
| showZoom | boolean | true | If true, display the zoom in and zoom out buttons. |
| showCompass | boolean | true | If true, display the compass (rotation) button. |
| visualizePitch | boolean | true | If true, the compass button will rotate on the Y-axis to visualize map pitch (3D view). |
Map Control Examples
// Simple: Enable default branded controls (top-right)
map.setNavigationControl(true);
// Advanced: Custom position and visibility
map.setNavigationControl({
position: 'bottom-right',
showZoom: true,
showCompass: false
});
// Disable controls
map.setNavigationControl(false);9. 🛣️ Routing & Optimization
Generate high-precision routes with automatic visualization. The SDK automatically fits the map view to the resulting route.
addRoute(options) Options
| Option | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| start | [lng, lat] | Required | Start point coordinates. |
| destination | [lng, lat] | Required | End point coordinates. |
| waypoints | Array<[lng, lat]> | [] | List of points to hit between start and end. |
| optimize | boolean | false | If true, reorders waypoints to find the shortest total trip (TSP). Requires at least 1 waypoint. |
| profile | string | 'driving' | Routing engine profile: 'driving', 'walking', 'cycling'. |
| language | string | 'th' | Return instructions/names in 'th' or 'en'. |
| routeColor | string | '#3b82f6' | Hex color code for the main route line. |
| routeWidth | number | 5 | Thickness of the main route line. |
| routeOpacity | number | 1.0 | Opacity of the route line (0.0 to 1.0). |
| casingColor | string | '#1d4ed8' | Hex color for the route's outer border (shadow). |
| casingWidth | number | 8 | Thickness of the casing line. |
Route Methods
| Method | Arguments | Description |
| :--- | :--- | :--- |
| addRoute(options) | Object | Calculate, draw route and fit view. Returns Promise. |
| clearRoutes() | - | Remove all route lines and markers from map. |
| getRouteDetails() | - | Returns distance, duration, and formatted strings. |
| getRouteSummary() | - | Returns top-level distance/duration strings. |
addRoute(options) Return Object
The addRoute method returns a Promise that resolves to a RouteResult object containing:
| Property | Type | Description |
| :--- | :--- | :--- |
| distance | number | Total distance of the route in meters. |
| duration | number | Estimated travel time in seconds. |
| steps | Array<Object> | Turn-by-turn instructions (text, distance, duration, location). |
| geometry | Object | GeoJSON LineString of the route. |
| raw | Object | Raw response from the PowerMap routing backend. |
steps Object Structure
Each step in the steps array represents a turn or maneuver:
| Field | Type | Description |
| :--- | :--- | :--- |
| text | string | The maneuver instruction (e.g., "เลี้ยวขวาที่ถนนสุขุมวิท"). |
| distance | number | Distance to travel for this step (meters). |
| duration | number | Estimated time for this step (seconds). |
| location | [lng, lat] | Geographic coordinate where the maneuver starts. |
Routing Example
// Simple route from A to B
const route = await map.addRoute({
start: [100.5018, 13.7563],
destination: [100.5118, 13.7663],
language: "en"
});
// Complex optimized route with waypoints
const optimizedRoute = await map.addRoute({
start: [100.50, 13.75],
destination: [100.60, 13.85],
waypoints: [
[100.55, 13.80],
[100.52, 13.78]
],
optimize: true, // Will reorder the 2 waypoints for the best efficiency
language: "th"
});
console.log(`Total Distance: ${optimizedRoute.distance}m`);
console.log(`Total Duration: ${optimizedRoute.duration}s`);
// Custom red route for heavy traffic visualization
const redRoute = await map.addRoute({
start: [100.5, 13.7],
destination: [100.6, 13.8],
routeColor: "#ff0000",
routeWidth: 10,
casingColor: "#990000"
});
// Clear all routes from visibility
map.clearRoutes();📍 Route Summary Helpers
Quickly access formatted summaries (distance in km/m, time in hours/mins) for the total route and individual steps:
// Get enhanced details (Recommended)
const details = map.getRouteDetails();
if (details) {
console.log(`Total: ${details.distanceStr}, Time: ${details.durationStr}`);
// Each step also contains formatted strings
details.steps.forEach(step => {
console.log(`${step.text} (${step.distanceStr})`);
});
}
// Get just the top-level summary
const summary = map.getRouteSummary(); // { distanceStr, durationStr }🏗️ 10. Map Layers Management
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.
Built-in Layer Groups
Map layers are grouped into 3 main categories:
- CITY PLAN (
city): City plans for various provinces. - TRANSPORT (
transport): Roads, Current/Future Railways, Current/Future BTS/MRT. - INSIGHT DATA (
insight): Night lights, Satellite imagery.
Layer APIs
| Method | Arguments | Description |
| :--- | :--- | :--- |
| map.layers.getCatalog() | - | Returns the full list of available layer groups and IDs. |
| map.layers.ensure(layerId) | string | Ensures a layer's sources are created (idempotent). |
| map.layers.show(layerId) | string | Ensures and shows a specific layer. |
| map.layers.hide(layerId) | string | Hides a specific layer. |
| map.layers.toggle(layerId) | string | Toggles the visibility of a layer. |
| map.layers.getLayerState(id) | string | Returns {loaded, visible} status. |
| map.layers.setOpacity(id, val) | string, number | Adjusts the transparency (0.0 - 1.0). |
| map.layers.showGroup(key) | string | Shows all layers within a specific group. |
| map.layers.hideGroup(key) | string | Hides all layers within a specific group. |
| map.layers.hideAll() | - | Hides every active layer from the catalog. |
Usage Example
map.onMapLoaded(async () => {
// 1. View all available layers
const catalog = map.layers.getCatalog();
console.log(catalog);
// 2. Show the Road layer
await map.layers.show('pm-road-vt');
// 3. Toggle the current Railway layer
await map.layers.toggle('pm-current-railway-th-vt');
// 4. Adjust opacity
map.layers.setOpacity('pm-road-vt', 0.5);
// 5. Hide an entire group
map.layers.hideGroup('city');
});You can also use convenience methods directly on the PowerMap instance:
await map.showLayer('pm-road-vt');
map.hideLayer('pm-road-vt');
await map.toggleLayer('pm-road-vt');[!IMPORTANT] Satellite Layer: High-resolution satellite imagery (
pm-maptiler-satellite-layer) requires amaptilerKeyprovided in the initialPowerMapconfiguration.
11. 🎥 Camera & Viewport Control
Take full control of the user's perspective with smooth transitions or direct manipulation.
Movement Functions
| Method | Arguments | Description |
| :--- | :--- | :--- |
| flyTo(options) | Object | Smooth cinematic movement with options: center, zoom, pitch, bearing, duration. |
| easeTo(options) | Object | Linear transition movement. Same options as flyTo. |
| jumpTo(options) | Object | Instant teleportation without animation. |
| fitBounds(bounds, opts) | Array, Object | Fits view to show all coords. Options: padding. |
| zoomIn() | - | Smoothly zoom in by level 1. |
| zoomOut() | - | Smoothly zoom out by level 1. |
| rotateLeft() | - | Smoothly rotate left by 45 degrees. |
| rotateRight() | - | Smoothly rotate right by 45 degrees. |
State Functions
| Method | Arguments | Description |
| :--- | :--- | :--- |
| setPitch(deg, anim) | number, bool | Set tilt angle (0-85). Smooth by default. |
| setBearing(deg, anim) | number, bool | Set direction (0-360). Smooth by default. |
| getCenter() | - | Returns {lng, lat} of the current map center. |
| getZoom() | - | Returns the current zoom level as a number. |
| getPitch() | - | Returns the current pitch (tilt) as a number. |
| getBearing() | - | Returns the current bearing (rotation) as a number. |
Camera Examples
// Fly into a specific warehouse view
map.flyTo({
center: [100.5, 13.8],
zoom: 17,
pitch: 60,
bearing: 45,
duration: 4000
});
// Fit view to a specific region
map.fitBounds([
[100.4, 13.7],
[100.6, 13.9]
], { padding: 40 });11. 🔥 Event System
The SDK uses a semantic event system for a modern developer experience. All events can be chained.
Event List & Arguments
| Method | Callback Arguments | Triggered When... |
| :--- | :--- | :--- |
| onMapLoaded | {map} | The map is fully initialized and layers can be added. |
| onClick | {lngLat, point, ...} | The map is clicked. |
| onMapLongClick | {lngLat, point, ...} | The map is long-pressed or right-clicked. |
| onMarkerClick | {marker, originalEvent, options} | A user clicks on any marker added via SDK. |
| onPoiClick | {feature, lngLat, point} | A user clicks on a Point of Interest (POI) on the base map. |
| onModeChanged | {mode, options} | The SDK operational mode (standard/indoor) has changed. |
| onMarkerDrag | {marker, lngLat, options} | A draggable marker is currently being moved. |
| onMarkerDragEnd | {marker, lngLat, options} | A user releases a draggable marker. |
| onRouteCalculated | route | A new route has been fetched and drawn. |
| onGeometryExported | {data} | GeoJSON data has been exported from the editor. |
| layerEnsured | {layerId, group} | A catalog layer has finished initializing sources. |
| onError | {source, message} | Any internal error (Auth, Routing, Map) occurs. |
Event Subscription Example
map.onMapLoaded((e) => {
console.log("Ready!");
})
.onClick((e) => {
console.log(`Map clicked at: ${e.lngLat}`);
})
.onMarkerClick((data) => {
alert(`You clicked marker: ${data.options.popupContent}`);
})
.onMarkerDragEnd((data) => {
console.log(`New Coordinate: ${data.lngLat.lng}, ${data.lngLat.lat}`);
});12. 🔍 Search & Geocoding
The Web SDK provides built-in search capabilities using the PowerMap Search API.
search(query, options)
Available options:
lang:'th'(default) or'en'.location:[latitude, longitude]to prioritize nearby results.
const results = await map.search("Central World", { lang: 'en' });
results.forEach(item => {
console.log(`${item.name}: ${item.address}`);
console.log(`Position: ${item.position}`); // [lat, lng]
});13. 🕹️ Interactive Metadata (POI)
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).
map.onPoiClick(({ feature, lngLat }) => {
const name = feature.properties.name_t;
console.log(`User clicked on: ${name}`);
// Highlighting the POI with a popup
map.addMarker({
id: 'poi-temp',
position: [lngLat.lng, lngLat.lat],
popupContent: { title: name, tag: 'POI' }
});
});14. 🧰 Utility Functions
SDK provides handy formatters for displaying routing data.
| Method | Arguments | Description |
| :--- | :--- | :--- |
| formatDistance(meters) | number | Converts meters to string (e.g., "1.25 km" or "500 m"). |
| formatDuration(seconds) | number | Converts seconds to string (e.g., "1h 30m" or "5 min"). |
15. 📐 Geometry Engine & Editor
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.
15.1 The Editor Toolbar
Enable a premium, built-in toolbar that handles the entire drawing lifecycle.
map.editor.enable({
position: 'bottom-left', // 'top-left', 'top-right', 'bottom-left', 'bottom-right'
mode: 'standard' // 'standard' (Logistics) or 'indoor' (Indoor Mapping)
});Interactive Tools
- Standard Mode: Point, Line (Measure), Polygon (Area), Rectangle.
- Indoor Mode: Beacon, POI, Walkway, Floor, Obstacle.
15.2 Measurement & Info Panel
When a feature is selected, a real-time measurement panel appears, providing:
- Distance: For lines and walkways.
- Area: For polygons and floors.
- 3D Height: For extruded features.
- Live Coordinates: For Points (POIs and Beacons), showing relative X/Y metrics dynamically during placement inside buildings, or Latitude/Longitude for outdoor placements.
15.3 Property Editor UI
The PowerMap Editor includes a functional Property Editor panel (available in indoor mode or via the settings icon). It allows you to manage:
- Core Metadata: ID and Name.
- Classification: Independent
feature_type(e.g., level, building, venue), explicit Categories, and Ordinal/Level (for stacking floors). - Visual Styling: Choose colors and icons (for Beacons and POIs).
- Custom Properties: Add unlimited key-value pairs for your own business logic.
15.4 3D & Advanced Tools
Take drawing to the next dimension:
- Extrude: Transform 2D polygons into 3D buildings or obstacles by setting the
heightproperty. - Rotate & Clone: Rapidly duplicate and align features.
- Vertical Alignment: Manage
ordinalproperties to ensure data belongs to the correct floor level.
15.5 Data Lifecycle (Import/Export)
The Editor facilitates easy data exchange via GeoJSON:
- Export: Built-in "Download" button in the toolbar, or call
map.geometry.export(). - Import: Built-in "Upload" button, or call
map.geometry.import(geojson). - IMDF Compliance: Indoor fields (
feature_type,category,ordinal) are mapped to IMDF format automatically.
15.6 Unified Programmatic CRUD API
For the full reference, see GEOMETRY.md
All operations — whether from draw tool or code — emit the same geometryChanged event.
// CREATE (standard)
const id = map.geometry.create('polygon', {
coordinates: [[[100.5,13.7],[100.6,13.7],[100.6,13.8],[100.5,13.7]]],
properties: { fillColor: '#4f46e5', height: 10 }
});
// CREATE (indoor)
const beaconId = map.geometry.create('beacon', {
lngLat: [100.52, 13.75],
properties: { ordinal: 1, name: 'BKK-01' }
});
// READ
map.geometry.getById(id); // Feature | null
map.geometry.getByKind('beacon'); // Feature[]
map.geometry.query().byKind('poi').byFloor(1).toArray(); // Feature[]
// UPDATE
map.geometry.update(id, {
properties: { name: 'Updated Name', color: '#ff0000' }
});
// DELETE
map.geometry.remove(id);
map.geometry.clear();All CRUD Methods:
| Method | Arguments | Description |
| :--- | :--- | :--- |
| create(kind, options) | string, Object | Create a standard or indoor feature programmatically |
| update(id, patch) | string, Object | Update properties and/or geometry of an existing feature |
| getById(id) | string | Returns a single GeoJSON Feature or null |
| getByKind(kind) | string | Returns all indoor features matching the given kind |
| query() | - | Returns a chainable GeometryQuery builder |
| add(geojson) | Object | Manually add a raw GeoJSON feature (legacy) |
| remove(id) | string\|string[] | Remove one or multiple features by ID |
| clear() | - | Remove all geometry from the map |
| setFeatureHeight(id, m) | string, number | Extrude a polygon into a 3D volume |
| getGeoJSON(id?, opts) | string, Object | Returns feature(s) as GeoJSON (options.format: 'imdf' \| 'standard') |
| export(opts) | Object | Returns all features as GeoJSON |
| import(geojson, opts) | Object, Object | Loads GeoJSON/IMDF data |
15.7 GeometryStore — Placeholder API Bridge
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.
// Register before the map loads
map.geometry.store.onCreate(async (payload) => {
console.log('[Placeholder] CREATE', payload);
// await fetch('/api/features', { method: 'POST', body: JSON.stringify(payload) })
});
map.geometry.store.onUpdate(async (payload) => {
console.log('[Placeholder] UPDATE', payload);
// await fetch(`/api/features/${payload.featureId}`, { method: 'PUT', body: JSON.stringify(payload) })
});
map.geometry.store.onDelete(async (payload) => {
console.log('[Placeholder] DELETE', payload);
// await fetch(`/api/features/${payload.featureId}`, { method: 'DELETE' })
});
// Used when a developer clicks the "Save" icon on the draw toolbar
map.geometry.store.onSave(async (data) => {
console.log('[Placeholder] MANUAL SAVE TRIGGERED', data);
// const success = await saveAllToDatabase(data);
// if (success) map.geometry.store.setDirty(false); // Clear unsaved warnings
});Each callback receives a clean payload with user_ prefixes stripped:
{
operation: 'create' | 'update' | 'delete',
source: 'draw' | 'programmatic',
format: 'standard' | 'indoor',
featureId: 'abc123',
kind: 'beacon' | 'poi' | ... | null, // null for standard
floor: 1 | null, // ordinal, null for standard
timestamp: '2026-03-27T...',
geometry: { type: 'Point', coordinates: [...] },
properties: { name: 'BKK-01', color: '#fbbf24' }
}[!NOTE] If no callback is registered, the SDK logs all events to the console automatically with 📦 CREATE / ✏️ UPDATE / 🗑️ DELETE icons.
15.8 SDK Events
// Subscribe to every geometry change (create/update/delete)
map.on('geometryChanged', ({ operation, source, feature }) => {
// operation: 'create' | 'update' | 'delete'
// source: 'draw' | 'programmatic'
// feature: GeoJSON Feature (null for delete)
});
// Subscribe to live coordinate/property updates from the UI Editor
// Use this to refresh UI panels instantly without waiting for or triggering a backend save.
map.on('geometryLiveUpdate', ({ type, features }) => {
// type: 'update'
// features: Array of GeoJSON Features that are currently being edited
});
// Listen to selection changes
map.on('geometrySelectionChanged', ({ features }) => {
// Array of currently selected feature objects
});16. 🏠 Indoor Mapping
The PowerMap SDK provides a robust API for managing indoor environments, including floor switching, category filtering, and specialized geometry handling.
16.1 Enabling Indoor Mode
Switch to indoor mode to enable building-specific logic and specialized drawing tools (e.g., Beacons, Walkways).
map.setMode('indoor');16.2 Floor Management (map.indoor)
When in indoor mode, use the map.indoor facade to control the vertical context of the map.
| Method | Arguments | Description |
| :--- | :--- | :--- |
| setFloor(ordinal, opts) | number, Object | Switch to a specific floor level (e.g., 1, 2, -1). |
| getAvailableFloors() | - | Returns a sorted array of all unique floor ordinals found in the map data. |
| getCurrentFloor() | - | Returns the ordinal of the currently active floor. |
Switching Floors with Animation
// Switch to Floor 2 with a smooth fade transition
map.indoor.setFloor(2, { animate: true });Building a Dynamic Floor Selector UI
// Get all available floors to populate a dropdown
const floors = map.indoor.getAvailableFloors();
// Returns: [1, 2, 3] or [] if no indoor data is found16.3 Visibility Control
Toggle the visibility of specific indoor data categories. These settings persist across floor changes.
// Toggle individual categories
map.indoor.setCategoryVisible('poi', true); // Points of Interest
map.indoor.setCategoryVisible('beacon', false); // Beacons/Sensors
map.indoor.setCategoryVisible('walkway', true); // Navigation Paths
map.indoor.setCategoryVisible('obstacle', true); // 3D Obstacles/Buildings16.4 Indoor Data Kinds
The SDK categorizes indoor features using the user_kind (or kind) property:
- floor: The walkable surface area of a floor (Polygon).
- obstacle: Non-walkable areas. Can be extruded into 3D.
- walkway: Navigable path lines for indoor routing.
- poi: Indoor points of interest with custom icons.
- beacon: Positioning hardware or logical points.
16.5 Event Synchronization
// Listen for mode changes
map.on('onModeChanged', ({ mode }) => {
console.log(`Mode switched to: ${mode}`); // 'standard' or 'indoor'
});
// Sync UI when data is added/deleted or imported
map.on('geometryChanged', () => {
const updatedFloors = map.indoor.getAvailableFloors();
// Refresh your floor selector here
});17. ⌨️ Keyboard Shortcuts
The SDK provides built-in productivity shortcuts whenever the geometry engine is active:
- Ctrl + C: Copy selected geometry.
- Ctrl + V: Paste geometry at the current cursor position.
- Delete / Backspace: Remove selected features.
18. 🩺 Debug & Telemetry
Enable the Debug HUD during development to monitor performance and coordinates.
// Enable debug panel
map.debug().enable({ position: 'top-right' });
// Toggle specific metrics
map.debug().showFPS(true);
map.debug().showCoordinate(false);
// Disable
map.debug().disable();19. 🛠 Build & Development
For contributors to the SDK core:
Contributor Guide
Setup Environment:
npm installLocal Development:
npm run devProduction Build:
npm run build
Project Structure
/src/core: Map lifecycle and shared managers./src/geometry: Drawing engine and coordinate logic./src/editor: Toolbar UI and control components./src/routing: Navigation and path-finding./src/auth: Security and API tokens.
📌 Appendix A: Complete SDK Event List
The SDK emits the following events through its central EventBus. You can subscribe to them via map.on(eventName, callback).
🌍 Core / Global
| Event | Payload | Description |
|---|---|---|
| mapLoaded | { map } | Fired when PowerMap finishes loading the style and is ready. |
| mapStyleChanged | { styleKey } | Fired after switching the base map style. |
| modeChanged | { mode, options } | Fired when SDK interactive mode changes (e.g. mode_free → mode_routing). |
| error | PowerMapError | Fired globally for SDK internal errors. |
📐 Geometry & Draw
| Event | Payload | Description |
|---|---|---|
| geometryChanged | { operation/type, source, feature(s) } | Fired upon completion of a CRUD operation or drawing action. |
| geometryLiveUpdate | { type: 'update', features } | Fired rapidly during property/coordinate editing for real-time UI mapping. |
| geometrySelectionChanged | { features } | Fired when the active selection changes in drawing mode. |
📍 Markers
| Event | Payload | Description |
|---|---|---|
| markerAdded | { marker, options } | Fired when a new marker is added to the map. |
| markerRemoved | { marker\|id } | Fired when a marker is explicitly removed. |
| markerClick | { marker, originalEvent, options } | Fired when a marker is clicked. |
| markerDrag | { marker, lngLat, options } | Fired continuously while a draggable marker is being dragged. |
| markerDragEnd | { marker, lngLat, options } | Fired when a marker is dropped after dragging. |
| markersCleared| - | Fired when all markers on the map are cleared. |
🏢 Indoor
| Event | Payload | Description |
|---|---|---|
| floorChanged | ordinal (number) | Fired when the active indoor floor changes. |
🗺️ Routing & Navigation
| Event | Payload | Description |
|---|---|---|
| routeRequestStart| options | Fired when a route calculation request is initiated. |
| routeCalculated | GeoJSON Feature | Fired upon successful calculation of a MultiLineString route. |
| routeCleared | - | Fired when the active route is removed from the map. |
| navigationStart | - | Fired when Turn-by-Turn navigation is initiated. |
| navigationEnd | - | Fired when Turn-by-Turn navigation stops. |
| simulationStart | - | Fired when GPS Route Simulator starts running. |
| simulationUpdate| { position, step } | Fired at every tick of the GPS simulator with the interpolated position. |
| simulationEnd | - | Fired when GPS simulation halts or reaches the destination. |
🎨 Layers
| Event | Payload | Description |
|---|---|---|
| layerVisibilityChanged| { layerId, visible } | Fired when a system layer's visibility is toggled. |
| layerEnsured | { layerId, group } | Fired when the SDK auto-creates a missing required layer group. |
© 2026 PowerMap Development Team. All rights reserved.
