mappls-map-react-native
Version:
A Mappls GL react native module for creating custom maps
164 lines (133 loc) • 4.38 kB
text/typescript
import { NativeEventEmitter, NativeModules, type EmitterSubscription } from 'react-native';
import NativeRCTMGLModule from '../../../specs/NativeRCTMGLModule';
import NativeRCTMGLLocationModule from '../../../specs/NativeRCTMGLLocationModule';
const MapplsGL = NativeRCTMGLModule == null ? Object.create(NativeModules.MGLModule): NativeRCTMGLModule;
const MapplsGLLocationManager = NativeRCTMGLLocationModule == null ? Object.create(NativeModules.MGLLocationModule): NativeRCTMGLLocationModule;
if(NativeRCTMGLModule != null) {
MapplsGL.LocationCallbackName = NativeRCTMGLModule?.getConstants()?.LocationCallbackName;
}
export const LocationModuleEventEmitter = new NativeEventEmitter(
MapplsGLLocationManager,
);
/*
* Location sent by LocationManager
*/
export interface Location {
coords: Coordinates;
timestamp?: number;
}
/*
* Coordinates sent by LocationManager
*/
interface Coordinates {
/*
* The heading (measured in degrees) relative to true north.
* Heading is used to describe the direction the device is pointing to (the value of the compass).
* Note that on Android this is incorrectly reporting the course value as mentioned in issue https://github.com/rnmapbox/maps/issues/1213
* and will be corrected in a future update.
*/
heading?: number;
/*
* The direction in which the device is traveling, measured in degrees and relative to due north.
* The course refers to the direction the device is actually moving (not the same as heading).
*/
course?: number;
/*
* The instantaneous speed of the device, measured in meters per second.
*/
speed?: number;
/*
* The latitude in degrees.
*/
latitude: number;
/*
* The longitude in degrees.
*/
longitude: number;
/*
* The radius of uncertainty for the location, measured in meters.
*/
accuracy?: number;
/*
* The altitude, measured in meters.
*/
altitude?: number;
}
class LocationManager {
_listeners: ((location: Location) => void)[];
_lastKnownLocation: Location | null;
_isListening: boolean;
subscription: EmitterSubscription | null;
constructor() {
this._listeners = [];
this._lastKnownLocation = null;
this._isListening = false;
this.onUpdate = this.onUpdate.bind(this);
this.subscription = null;
}
async getLastKnownLocation(): Promise<Location | null> {
if (!this._lastKnownLocation) {
let lastKnownLocation;
// as location can be brittle it might happen,
// that we get an exception from native land
// let's silently catch it and simply log out
// instead of throwing an exception
try {
lastKnownLocation = await MapplsGLLocationManager.getLastKnownLocation();
} catch (error) {
console.log("LocationManager Error: ", error);
}
if (!this._lastKnownLocation && lastKnownLocation) {
this._lastKnownLocation = lastKnownLocation;
}
}
return this._lastKnownLocation;
}
addListener(listener: (location: Location) => void): void {
if (!this._isListening) {
this.start();
}
if (!this._listeners.includes(listener)) {
this._listeners.push(listener);
if (this._lastKnownLocation) {
listener(this._lastKnownLocation);
}
}
}
removeListener(listener: (location: Location) => void): void {
this._listeners = this._listeners.filter((l) => l !== listener);
if (this._listeners.length === 0) {
this.stop();
}
}
removeAllListeners(): void {
this._listeners = [];
this.stop();
}
start(displacement = 0): void {
if (!this._isListening) {
MapplsGLLocationManager.start(displacement);
this.subscription = LocationModuleEventEmitter.addListener(
MapplsGL.LocationCallbackName.Update,
this.onUpdate,
);
this._isListening = true;
}
}
stop(): void {
MapplsGLLocationManager.stop();
if (this._isListening) {
this.subscription?.remove();
}
this._isListening = false;
}
setMinDisplacement(minDisplacement: number): void {
MapplsGLLocationManager.setMinDisplacement(minDisplacement);
}
onUpdate(location: Location): void {
this._lastKnownLocation = location;
this._listeners.forEach((l) => l(location));
}
}
const locationManager = new LocationManager();
export { locationManager as LocationManager};