@mappedin/blue-dot
v6.14.0-beta.0
Published
An extension for [Mappedin JS](https://www.npmjs.com/package/@mappedin/mappedin-js) to track the device position on the map.
Readme
@mappedin/blue-dot
An extension for Mappedin JS to track the device position on the map.
Usage
Installation
With NPM:
npm install @mappedin/blue-dotWith Yarn:
yarn add @mappedin/blue-dotGetting Started
import { show3dMap } from '@mappedin/mappedin-js';
import { BlueDot } from '@mappedin/blue-dot';
const mapView = await show3dMap(...);
// Create a new BlueDot
const blueDot = new BlueDot(mapView);
blueDot.enable();
// Listening to device position updates from the browser
blueDot.watchDevicePosition(true);
// Send custom position updates from a different source
blueDot.update({
// Latitude of the update
latitude,
// Longitude of the update
longitude,
// Optional accuracy in meters
accuracy,
// Optional heading in degrees from north
heading,
// Optional floor or floor ID
floorOrFloorId,
});
// Attach the camera to the BlueDot
blueDot.follow('position-only');Permissions
Geolocation Permission
watchDevicePosition(true) requires the geolocation permission. The browser will automatically prompt the user to allow location access when called.
// Browser will prompt for permission
await blueDot.watchDevicePosition(true);
// Listen for errors (including permission denied)
blueDot.on('error', error => {
if (error.code === error.PERMISSION_DENIED) {
console.log('User denied geolocation permission');
}
});Device Orientation Permission (iOS)
watchDeviceOrientation(true) requires the deviceorientation permission on iOS. This method must be called in response to a user gesture (click, tap) on iOS devices.
// Must be called from a user gesture handler on iOS
button.addEventListener('click', async () => {
await blueDot.watchDeviceOrientation(true);
});On Android, the deviceorientationabsolute event is used and does not require explicit permission.
API Reference
Constructor
const blueDot = new BlueDot(mapView: MapView);Creates a new BlueDot instance attached to the given MapView.
Methods
enable(options?: BlueDotUpdateState)
Enable the BlueDot. Must be called before other methods.
blueDot.enable({
color: '#2266ff',
radius: 10,
timeout: 30000,
watchDevicePosition: true,
});disable()
Disable and hide the BlueDot. Stops all position tracking.
blueDot.disable();watchDevicePosition(watch: boolean): Promise<void>
Start or stop listening to device GPS position.
- Requires geolocation permission (browser prompts automatically)
- Emits
position-updateevents when position changes - Emits
errorevents on geolocation errors
await blueDot.watchDevicePosition(true); // Start tracking
await blueDot.watchDevicePosition(false); // Stop trackingwatchDeviceOrientation(watch: boolean): Promise<void>
Start or stop listening to device compass heading.
- On iOS: Requires permission, must call from user gesture
- On Android: Uses
deviceorientationabsoluteevent (no permission needed) - Emits
device-orientation-updateevents when heading changes
// Call from a click handler on iOS
button.addEventListener('click', async () => {
await blueDot.watchDeviceOrientation(true);
});update(position, options?)
Manually set or override position properties.
// Set full position
blueDot.update({
latitude: 43.6532,
longitude: -79.3832,
accuracy: 5,
heading: 90,
floorOrFloorId: floor,
});
// Clear manual position
blueDot.update(undefined);Options:
silent?: boolean- If true, don't trigger status transitions or eventsanimate?: boolean- If false, skip position animation (default: true)
follow(mode, options?)
Make the camera follow the BlueDot.
// Follow position only
blueDot.follow('position-only');
// Follow position and rotate camera with heading
blueDot.follow('position-and-heading');
// Follow position and align camera with navigation path direction
blueDot.follow('position-and-path-direction');
// Stop following
blueDot.follow(false);Camera options:
zoomLevel?: number- Target zoom levelpitch?: number- Camera pitch angleanimationDuration?: number- Animation duration in ms
setPositionProcessor(processor?)
Set a callback to process/filter incoming geolocation position updates.
blueDot.setPositionProcessor((currentState, incomingUpdate) => {
// Filter out positions outside venue bounds
if (!isWithinBounds(incomingUpdate)) {
return undefined; // Discard update
}
// Modify position
return {
...incomingUpdate,
accuracy: Math.min(incomingUpdate.accuracy, 50),
};
});
// Clear processor
blueDot.setPositionProcessor(undefined);on(eventName, callback)
Subscribe to BlueDot events.
const unsubscribe = blueDot.on('position-update', event => {
console.log(event.coordinate, event.floor);
});
// Later: unsubscribe
unsubscribe();off(eventName, callback)
Unsubscribe from BlueDot events.
blueDot.off('position-update', handler);destroy()
Clean up all resources. Call when done with the BlueDot.
blueDot.destroy();Events
position-update
Fired when position changes (from device or manual update).
blueDot.on('position-update', event => {
console.log(event.coordinate); // Coordinate object
console.log(event.floor); // Floor object or undefined
console.log(event.heading); // Heading in degrees
console.log(event.accuracy); // Accuracy in meters
});device-orientation-update
Fired when device heading changes.
blueDot.on('device-orientation-update', event => {
console.log(event.heading); // Heading in degrees from north
});status-change
Fired when BlueDot status changes.
blueDot.on('status-change', event => {
console.log(event.status); // New status
console.log(event.previousStatus); // Previous status
});error
Fired on geolocation errors.
blueDot.on('error', (error: GeolocationPositionError) => {
switch (error.code) {
case error.PERMISSION_DENIED:
console.log('User denied geolocation permission');
break;
case error.POSITION_UNAVAILABLE:
console.log('Position unavailable');
break;
case error.TIMEOUT:
console.log('Position request timed out');
break;
}
});follow-change
Fired when follow mode changes.
blueDot.on('follow-change', event => {
console.log(event.mode); // Current follow mode or false
});click
Fired when the BlueDot is clicked.
blueDot.on('click', event => {
console.log('BlueDot clicked at', event.coordinate);
});hover
Fired when the BlueDot is hovered.
blueDot.on('hover', event => {
console.log('BlueDot hovered at', event.coordinate);
});Options
export type BlueDotOptions = {
/**
* The radius of the BlueDot in pixels. The BlueDot will maintain this size clamped to a minimum of 0.35 metres.
* @default 10
*/
radius?: number;
/**
* The color of the BlueDot core element.
* @default #2266ff
*/
color?: string;
/**
* The color of the BlueDot when it has timed out and gone inactive.
* @default #808080
*/
inactiveColor?: string;
/**
* Options for the accuracy ring around the BlueDot.
*/
accuracyRing?: {
/**
* The color of the accuracy ring.
* @default #2266ff
*/
color?: string;
/**
* The opacity of the accuracy ring.
* @default 0.3
*/
opacity?: number;
};
/**
* Options for the heading directional indicator.
*/
heading?: {
/**
* The color of the heading cone.
* @default #2266ff
*/
color?: string;
/**
* The opacity of the heading cone.
* @default 0.7
*/
opacity?: number;
/**
* Whether to display the heading cone when the BlueDot is inactive.
* @default false
*/
displayWhenInactive?: boolean;
};
/**
* The duration of the timeout in milliseconds.
* If the BlueDot does not receive a position update within this time, it will grey out until a position is received.
* @default 30000
*/
timeout?: number;
/**
* Whether to watch the device's position automatically when enabled.
* @default true
*/
watchDevicePosition?: boolean;
/**
* The initial state of the BlueDot. Use 'inactive' to show the BlueDot immediately in grey.
* @default 'hidden'
*/
initialState?: 'hidden' | 'inactive';
/**
* Whether to prevent position updates outside the map bounds.
* @default false
*/
preventOutOfBounds?: boolean;
/**
* Maximum accuracy (in meters) to accept. Updates with lower accuracy are ignored.
* @default Infinity
*/
accuracyThreshold?: number;
/**
* Whether to log debug messages.
* @default false
*/
debug?: boolean;
};React Native
import React, { useEffect, useCallback } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { MapView, useMapData } from '@mappedin/react-native-sdk';
import { useBlueDot, useBlueDotEvent } from '@mappedin/blue-dot/rn';
function MyComponent() {
const { mapData } = useMapData({
key: 'your-api-key',
secret: 'your-api-secret',
mapId: 'your-map-id',
});
return (
<MapView mapData={mapData}>
<BlueDotDisplay />
</MapView>
);
}
function BlueDotDisplay() {
// All methods are async and return Promises
const {
isReady,
isEnabled,
state,
position,
floor,
isFollowing,
accuracy,
heading,
enable,
disable,
update,
follow,
} = useBlueDot();
// Listen for position updates
useBlueDotEvent(
'position-update',
useCallback(event => {
console.log('Position updated:', event.coordinate, event.floor);
}, []),
);
// Listen for state changes
useBlueDotEvent(
'state-change',
useCallback(event => {
console.log('State changed:', event.state);
}, []),
);
// Listen for follow mode changes
useBlueDotEvent(
'follow-change',
useCallback(event => {
console.log('Follow mode:', event.following);
}, []),
);
useEffect(() => {
if (isReady && !isEnabled) {
// All methods are async - use await or .then()
enable({
radius: 15,
color: '#ff0000',
watchDevicePosition: false,
});
}
}, [isReady, isEnabled, enable]);
const handleUpdatePosition = useCallback(async () => {
try {
// Update position manually
await update({
latitude: 43.6532,
longitude: -79.3832,
accuracy: 5,
heading: 90,
floorOrFloorId: floor,
});
// Enable follow mode
await follow('position-and-heading', {
zoomLevel: 19,
});
} catch (error) {
console.error('Failed to update position:', error);
}
}, [update, follow, floor]);
return (
<View>
<Text>Is Ready: {isReady ? 'Yes' : 'No'}</Text>
<Text>Is Enabled: {isEnabled ? 'Yes' : 'No'}</Text>
<Text>State: {state}</Text>
<Text>Following: {isFollowing ? 'Yes' : 'No'}</Text>
{position && (
<Text>
Position: {position.latitude.toFixed(4)}, {position.longitude.toFixed(4)}
</Text>
)}
{accuracy && <Text>Accuracy: {accuracy.toFixed(1)}m</Text>}
{heading && <Text>Heading: {heading.toFixed(0)}°</Text>}
<TouchableOpacity onPress={handleUpdatePosition}>
<Text>Update Position & Follow</Text>
</TouchableOpacity>
</View>
);
}Key Differences from Vanilla JS:
- All methods are async:
enable(),disable(),update(), andfollow()return Promises - Rich state: Hook returns
isReady,state,position,floor,isFollowing,accuracy,headingfor real-time updates - Separate event hook: Use
useBlueDotEventfor listening to position-update, state-change, and follow-change events
