Complete SDK Example
This comprehensive example demonstrates all major features of the MapVX Web SDK, including:
- ✅ SDK Initialization - Setting up the SDK with API credentials
- ✅ Observable Map Ready State - Using promises to wait for map readiness
- ✅ Map Creation Options - Creating maps with or without parent places
- ✅ Map Configuration - Custom zoom, center, and event callbacks
- ✅ Data Loading - Fetching available places and subplaces
- ✅ Marker Management - Creating markers with custom icons and positioning
- ✅ Map Navigation - Centering and fitting coordinates
- ✅ Route Creation - Adding navigation routes between places
- ✅ Event Handling - Responding to map interactions and state changes
- ✅ Error Handling - Proper error management and recovery
Complete Example with Map Initialization and Markers
import { initializeSDK, loadStyles, type MapVXSDK, type MapVXMap } from "@mapvx/web-js"
// Load default styles once at the beginning
loadStyles()
class MapExample {
private sdk: MapVXSDK | null = null
private map: MapVXMap | null = null
private mapReady: boolean = false
private mapReadyResolve?: () => void
private mapReadyPromise: Promise<void>
constructor() {
// Initialize map ready observable pattern
// This creates a Promise that will be resolved when the map is ready
this.mapReadyPromise = new Promise<void>((resolve) => {
this.mapReadyResolve = resolve
})
}
async initialize() {
try {
// 1. Initialize the SDK
this.sdk = initializeSDK("your-api-key-here", { lang: "en" })
// 2. Create map with parent place (callbacks configured in mapConfig)
await this.createMapWithParentPlace()
// 3. Wait for map to be ready
await this.waitForMapReady()
// 4. Load data and create markers
await this.loadDataAndCreateMarkers()
} catch (error) {
console.error("Error initializing:", error)
}
}
// Option 1: Create map with specific parent place
async createMapWithParentPlace() {
const mapContainer = document.getElementById("mapContainer") as HTMLElement
// Configure map with callbacks
const mapConfig = {
zoom: 15,
center: { lat: 40.7128, lng: -74.006 },
onMapReady: () => {
console.log("Map ready!")
// IMPORTANT: Set internal state and resolve promise when map is loaded
// This allows waitForMapReady() to work properly
this.mapReady = true
this.mapReadyResolve?.()
},
onZoomEnd: (zoomLevel?: number) => {
console.log(`Zoom changed to: ${zoomLevel}`)
},
onFloorChange: (newFloorId: string) => {
console.log(`Floor changed to: ${newFloorId}`)
},
}
this.map = this.sdk!.createMap(mapContainer, { ...mapConfig, parentPlaceId: "parent-place-id" })
}
// Option 2: Create map without parent place (general configuration)
async createMapWithoutParentPlace() {
const mapContainer = document.getElementById("mapContainer") as HTMLElement
const mapConfig = {
zoom: 15,
center: { lat: 40.7128, lng: -74.006 },
onMapReady: () => {
console.log("Map ready!")
// Set our internal state and resolve promise
this.mapReady = true
this.mapReadyResolve?.()
},
}
this.map = this.sdk!.createMap(mapContainer, mapConfig)
}
// Wait for map to be completely ready using observable pattern
// This method returns a Promise that resolves when the map is fully loaded
async waitForMapReady(): Promise<void> {
// If map is already ready, resolve immediately
if (this.mapReady) {
return Promise.resolve()
}
// Otherwise, wait for the onMapReady callback to resolve the promise
return this.mapReadyPromise
}
// Load places data
async loadDataAndCreateMarkers() {
try {
// Option 1: Load available places
const places = await this.sdk!.getAvailablePlaces()
console.log("Available places:", places)
// Option 2: Load subplaces from a parent place
const subplaces = await this.sdk!.getSubPlaces("parent-place-id")
console.log("Subplaces:", subplaces)
// Create markers for first places
if (places.length > 0) {
await this.createMarkersForPlaces(places.slice(0, 3))
}
} catch (error) {
console.error("Error loading data:", error)
}
}
// Create markers for a list of places
async createMarkersForPlaces(places: any[]) {
for (const place of places) {
await this.createMarker(place)
}
}
// Create an individual marker
async createMarker(place: any) {
try {
const markerId = `marker-${place.mapvxId}`
// Marker configuration
const markerConfig = {
id: markerId,
coordinate: place.position, // { lat: number, lng: number }
text: place.title || place.mapvxId,
textPosition: "bottom", // "top" | "bottom" | "left" | "right"
icon: "https://cdn-icons-png.flaticon.com/512/684/684908.png", // Icon URL
iconProperties: {
width: 40,
height: 40,
},
}
// Add marker to map
await this.map!.addMarker(markerConfig)
console.log(`Marker created: ${markerId}`)
} catch (error) {
console.error("Error creating marker:", error)
}
}
// Example of marker with different pin types
async createMarkerWithCustomPin(place: any, pinType: "default" | "image" | "none") {
const markerConfig = {
id: `custom-marker-${place.mapvxId}`,
coordinate: place.position,
text: place.title,
textPosition: "right",
// Default SDK pin
...(pinType === "default" && { icon: undefined }),
// Custom image pin
...(pinType === "image" && {
icon: "https://cdn-icons-png.flaticon.com/512/684/684908.png",
}),
// Text only, no pin
...(pinType === "none" && { icon: null }),
iconProperties: {
width: 32,
height: 32,
},
}
await this.map!.addMarker(markerConfig)
}
// Create route between two places
async createRoute(fromPlace: any, toPlace: any) {
try {
const routeConfig = {
initialLocation: { id: fromPlace.mapvxId },
finalLocation: { id: toPlace.mapvxId },
preferAccessibleRoute: false,
language: "en",
}
const route = await this.map!.addRoute(routeConfig)
console.log("Route created:", route)
} catch (error) {
console.error("Error creating route:", error)
}
}
// Center map on a specific coordinate with optional zoom level
// This method moves the map to focus on a particular location
centerMapOnLocation(lat: number, lng: number, zoom: number = 15) {
const camera = {
center: { lat, lng },
zoom: zoom,
}
this.map!.updateCamera(camera)
}
// Fit map view to show all provided coordinates
// Automatically calculates the best zoom and center to show all points
fitMapToCoordinates(coordinates: Array<{ lat: number; lng: number }>) {
if (coordinates.length === 0) return
this.map!.fitCoordinates(coordinates, {
padding: 50, // Add padding around the bounds
maxZoom: 18, // Prevent zooming too close
})
}
// Advanced camera control with animation
animateCameraToLocation(lat: number, lng: number, zoom: number = 15) {
const camera = {
center: { lat, lng },
zoom: zoom,
duration: 2000, // 2 second animation
essential: false,
}
this.map!.updateCamera(camera, () => {
console.log("Camera animation completed")
})
}
// Advanced example: Bulk marker operations
async createMultipleMarkersWithDifferentStyles() {
const places = await this.sdk!.getAvailablePlaces()
for (let i = 0; i < places.length && i < 5; i++) {
const place = places[i]
// Create marker with different styles based on index
const markerConfig = {
id: `bulk-marker-${i}`,
coordinate: place.position,
text: `${place.title} (${i + 1})`,
textPosition: ["top", "right", "bottom", "left"][i % 4] as
| "top"
| "right"
| "bottom"
| "left",
// Different icon for each marker
icon: i % 2 === 0 ? "https://cdn-icons-png.flaticon.com/512/684/684908.png" : undefined, // Use default SDK pin
iconProperties: {
width: 30 + i * 5, // Varying sizes
height: 30 + i * 5,
},
}
await this.map!.addMarker(markerConfig)
}
// Fit map to show all markers
const coordinates = places.slice(0, 5).map((place) => place.position)
this.fitMapToCoordinates(coordinates)
}
// Demonstrate map interaction features
setupMapInteractions() {
if (!this.map) return
// Start listening for place clicks
this.map.startClickListener((placeId: string) => {
console.log(`Place clicked: ${placeId}`)
// You could show a popup, highlight the place, etc.
})
// Example: Add click handler that creates markers on clicked places
this.map.startClickListener(async (placeId: string) => {
const places = await this.sdk!.getAvailablePlaces()
const clickedPlace = places.find((p) => p.mapvxId === placeId)
if (clickedPlace) {
await this.createMarker(clickedPlace)
console.log(`Created marker for clicked place: ${clickedPlace.title}`)
}
})
}
}
// Usage example showing different initialization patterns
const mapExample = new MapExample()
// Pattern 1: Simple initialization
document.addEventListener("DOMContentLoaded", () => {
mapExample.initialize()
})
// Pattern 2: With error handling and additional setup
document.addEventListener("DOMContentLoaded", async () => {
try {
await mapExample.initialize()
// Additional setup after map is ready
await mapExample.waitForMapReady()
// Setup interactions
mapExample.setupMapInteractions()
// Demo: Create multiple markers with different styles
setTimeout(async () => {
await mapExample.createMultipleMarkersWithDifferentStyles()
}, 2000)
} catch (error) {
console.error("Failed to initialize map example:", error)
// Show user-friendly error message
const container = document.getElementById("mapContainer")
if (container) {
container.innerHTML = `
<div style="
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #666;
font-family: Arial, sans-serif;
">
<div style="text-align: center;">
<h3>Map could not be loaded</h3>
<p>Please check your API key and network connection.</p>
</div>
</div>
`
}
}
})Required Basic HTML
Option 1: With Manual CSS Loading
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MapVX SDK - Complete Example</title>
<!-- Load CSS manually -->
<link rel="stylesheet" href="./node_modules/@mapvx/web-js/dist/umd/styles.css" />
</head>
<body>
<div id="mapContainer" style="width: 100%; height: 500px;"></div>
<!-- Your compiled script -->
<script src="your-compiled-script.js"></script>
</body>
</html>Option 2: With Automatic CSS Loading
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MapVX SDK - Complete Example</title>
<!-- CSS will be loaded automatically by calling loadStyles() in your JavaScript -->
</head>
<body>
<div id="mapContainer" style="width: 100%; height: 500px;"></div>
<!-- Your compiled script that calls loadStyles() -->
<script src="your-compiled-script.js"></script>
</body>
</html>Useful Events and Callbacks
// Configure callbacks in MapConfig when creating the map
const mapConfig = {
zoom: 15,
center: { lat: 40.7128, lng: -74.006 },
parentPlaceId: "your-parent-place-id",
// Map ready callback
onMapReady: () => {
console.log("Map completely loaded and ready!")
},
// Zoom change callback
onZoomEnd: (zoomLevel?: number) => {
console.log(`Zoom level changed to: ${zoomLevel}`)
},
// Map rotation callback
onRotate: (degrees: number) => {
console.log(`Map rotated to: ${degrees} degrees`)
},
// Floor change callback
onFloorChange: (newFloorId: string) => {
console.log(`Floor changed to: ${newFloorId}`)
},
// Parent place change callback
onParentPlaceChange: (newParentPlaceId: string) => {
console.log(`Parent place changed to: ${newParentPlaceId}`)
},
}
// Create map with configured callbacks
const map = sdk.createMap(mapContainer, { ...mapConfig, parentPlaceId: "parent-place-id" })
// Place click listener (configured after map creation)
map.startClickListener((placeId: string) => {
console.log(`Place clicked: ${placeId}`)
})
// Stop listening to place clicks
map.stopClickListener()Error Handling
async function safeMapInitialization() {
try {
const sdk = initializeSDK("your-api-key", { lang: "en" })
const map = sdk.createMap(mapContainer, { parentPlaceId: "parent-place-id" })
return { sdk, map }
} catch (error) {
if (error.message.includes("API key")) {
console.error("Invalid or missing API key")
} else if (error.message.includes("network")) {
console.error("Connection error")
} else {
console.error("Unknown error:", error)
}
throw error
}
}Customization
The map configuration is loaded remotely by default, but you can override MapConfig to customize
map options:
// Example: Override map configuration
const customMapConfig = {
zoom: 18,
center: { lat: -33.4489, lng: -70.6693 },
parentPlaceId: "custom-parent-place",
minZoom: 10,
maxZoom: 22,
pitch: 45,
enableHover: true,
showCompass: true,
showZoom: true,
navigationPosition: "top-right",
bearingSnap: 15,
lang: "en",
onMapReady: () => {
console.log("Custom map configuration loaded!")
},
onZoomEnd: (zoomLevel) => {
console.log("Zoom changed to:", zoomLevel)
},
onRotate: (degrees) => {
console.log("Map rotated to:", degrees, "degrees")
},
onFloorChange: (floorId) => {
console.log("Floor changed to:", floorId)
},
onParentPlaceChange: (parentPlaceId) => {
console.log("Parent place changed to:", parentPlaceId)
},
}
// Apply custom configuration when creating the map
const map = sdk.createMap(mapContainer, customMapConfig)Note: Configuration is fetched remotely, but can be overridden for specific customization needs.
See the complete MapConfig interface documentation for all available options.
Key Features Demonstrated
🎯 Map Ready Observable Pattern
- Custom Promise-based system to wait for map readiness
- Prevents operations before map is fully loaded
- Resolves immediately if map is already ready
🗺️ Flexible Map Creation
- With Parent Place: Loads specific building/venue maps
- Without Parent Place: General map configuration
- Custom Configuration: Zoom, center, callbacks, and controls
📍 Advanced Marker Management
- Custom Icons: URL-based or default SDK pins
- Text Positioning: Top, bottom, left, right placement
- Dynamic Sizing: Variable icon dimensions
- Bulk Operations: Create multiple markers efficiently
📐 Camera Control & Navigation
- Center on Location: Focus map on specific coordinates
- Fit to Coordinates: Auto-zoom to show multiple points
- Animated Transitions: Smooth camera movements
- Bounds Management: Automatic padding and zoom limits
🔄 Interactive Features
- Click Listeners: Respond to place selections
- Event Callbacks: Map zoom, rotation, floor changes
- Real-time Updates: Dynamic marker creation
- Error Recovery: Graceful failure handling
Important Notes
- Automatic Dependencies: SDK includes all required libraries
- Async/Await Pattern: All operations are Promise-based
- Observable State: Use
waitForMapReady()before map operations - Event Configuration: Set callbacks in
MapConfigduring creation - Error Handling: Always implement try/catch for network operations
- Performance: Use bulk operations for multiple markers
- Responsive Design: Map adapts to container size changes
Last updated on