imobile_for_reactnative
Version:
iMobile for ReactNative,是SuperMap iMobile推出的一款基于React-Native框架的移动应用开发工具。基于该开发工具,用户可以使用JavaScript开发语言,开发出在Android和IOS操作系统下运行的原生移动GIS应用,入门门槛低,一次开发,处处运行。
654 lines (585 loc) • 16.6 kB
text/typescript
import { NativeModules, NativeEventEmitter, EmitterSubscription, Platform } from 'react-native'
import EventConst from 'imobile_for_reactnative/NativeModule/constains/EventConst'
const nativeLocation = NativeModules.SLocation
const eventEmitter = new NativeEventEmitter(nativeLocation)
let deviceListener: EmitterSubscription | undefined = undefined
let changeDeviceSuccessListener: EmitterSubscription | undefined = undefined
/**
* 定位连接方式
* LocalConnectionParam: 本机定位
* ExternalConnectionParam: 自定义设备定位
* LocalConnectionParam: 蓝牙设备定位
*/
export type LocationConnectionParam = LocalConnectionParam
| ExternalConnectionParam
| BluetoothConnectionParam
/** 本机定位 */
interface LocalConnectionParam {
/** 类型 */
type: 'local'
}
/** 自定义设备定位 */
interface ExternalConnectionParam {
/** 类型 */
type: 'external'
/** 设备名称 */
name: string
}
/** 蓝牙设备定位 */
interface BluetoothConnectionParam {
/** 类型 */
type: 'bluetooth'
/** mac地址 */
mac: string
/** gnss类型 */
gnssTppe: 'rtk'| 'gps'
/** 设备品牌 */
brand: 'other' | 'situoli' | 'woncan' | 'mijiaH20'
}
/** Ntrip差分服务器信息 */
export interface NtripServerInfo {
/** 差分服务器地址 */
address: string
/** 差分服务器端口号 */
port: number
/** 用户名 */
userName: string
/** 密码 */
password: string
}
/** Ntrip加载点信息 */
export interface NtripMountPoint {
/** 加载点名称 */
name: string
/** 是否需要上传当前位置的GGA信息 */
requireGGA: boolean
}
/** 蓝牙设备信息 */
interface BluetoothDevice {
/** 设备名称 */
name: string
/** 设备地址 */
address: string
}
/** 定位模式 */
export interface IRTKFixType {
/** 无效数据 */
invalid: 0
/** GPS单点定位,没有校准数据 */
GPS: 1
/** DGPS 校准定位 */
DGPS: 2
/** PPS 校准定位 */
PPS: 3
/** RTK 固定解,高精度校准 */
RTK: 4
/** RTK 浮点解, 比 DGPS 好,但不如 RTK 精准 */
floatRTK: 5
/** 近似 Estimated fix (dead reckoning). */
estimated: 6
/** 手动 */
manual: 7
/** 模拟 */
simulation: 8
/** WAAS */
WAAS: 9
}
export const RTKFixType: IRTKFixType = {
invalid: 0,
GPS: 1,
DGPS: 2,
PPS: 3,
RTK: 4,
floatRTK: 5,
estimated: 6,
manual: 7,
simulation: 8,
WAAS: 9,
}
/** NMEA GGA 语句参数对象 */
export interface GGA {
/** UTC时间 */
time: number
/** 经度 */
longitude: number
/** 纬度 */
latitude : number
/** 定位模式 */
fixType: IRTKFixType[keyof IRTKFixType]
/** 卫星数量 */
satNums: number
/** Horizontal Dilution of Precision 水平精度, 0 - 99.99, 无单位,值越低越好*/
HDOP: number
/** 相对于大地水准面(平均海平面)的高 单位米 */
altitude: number
/** 大地水准面(平均海平面)高 (相对于地球椭球体ellipsoid的高) 单位米*/
geoidHeight: number
/**
* 差分时间 单位秒
* DGPS 或 RTK 状态下才有意义
*/
age: number
}
/** NMEA RMC 语句参数对象 */
interface RMC {
/** 速度,单位 节 */
speed: number
/** 方向, 0-360度,正北方向为0度,东为90度 */
heading: number
}
/* **************************************************
* 监听
* **************************************************/
let nmeaListener: EmitterSubscription | undefined
/**
* 添加 NMEA 协议定位语句语句监听
*
* @param listener 语句监听,每产生一条NMEA语句就会回调一次
* @platform android
*/
export function setNMEAListener(listener?: (line: string) => void) {
if(Platform.OS !== 'android') return
nmeaListener?.remove()
nmeaListener = listener && eventEmitter.addListener(EventConst.LOCATION_NMEA_SENTENCE, listener)
}
let nmeaggaListener: EmitterSubscription | undefined
/**
* NMEA 语句中的 GGA 类型消息回调
*
* raw类型的语句可以通过 {@link addNMEAListener} 监听
* 此处将raw消息解析后包装成对象,方便直接使用
* @param listener GGA 类型消息回调监听
* @platform android
*/
export function setNMEAGGAListener(listener?: (gga: GGA) => void) {
if(Platform.OS !== 'android') return
nmeaggaListener?.remove()
nmeaggaListener = listener && eventEmitter.addListener(EventConst.LOCATION_NMEA_GGA, listener)
}
/** 定位信息 */
export interface aMapLocationParam {
/** 经度 */
longitude: number,
/** 纬度 */
latitude: number,
/** 精度 */
accuracy: number,
/** 高度 */
altitude: number,
/** 速度 m/s */
speed: number,
/** 当前GPS状态 */
gpsAccuracyStatus: number,
}
let aMapLocationListener: EmitterSubscription | undefined
/** 当前设备定位信息监听 */
export function setLocalDeviceLocationInfoListener(listener?: (locationInfo: aMapLocationParam) => void) {
if(Platform.OS !== 'android') return
aMapLocationListener?.remove()
aMapLocationListener = listener && eventEmitter.addListener(EventConst.LOCATION_LOCAL_DEVICE_LOCATION_INFO, listener)
}
let rtkCalibrationListener: EmitterSubscription | undefined
/** RTK融合校准监听 */
export function setRTKCalibrationListener(listener?: (info: boolean) => void) {
if(Platform.OS !== 'android') return
rtkCalibrationListener?.remove()
rtkCalibrationListener = listener && eventEmitter.addListener(EventConst.LOCATION_RTK_CALIBRATION, listener)
}
/**
* 设置融合校准精度
* @param accuracyDiff 精度 (单位:m)
* @returns
*/
export async function setPositionAccuracyDiff(accuracyDiff: number):Promise<boolean> {
if(Platform.OS !== 'android') return false
return await nativeLocation.setPositionAccuracyDiff(accuracyDiff)
}
let nmearmcListener: EmitterSubscription | undefined
/**
* NMEA 语句中 RMC 类型消息回调
*
* raw类型的语句可以通过 {@link addNMEAListener} 监听
* 此处将raw消息解析后包装成对象,方便直接使用
* @param listener RMC 类型消息回调监听
* @platform android
*/
export function setNMEARMCListener(listener?: (rmc: RMC) => void) {
if(Platform.OS !== 'android') return
nmearmcListener?.remove()
nmearmcListener = listener && eventEmitter.addListener(EventConst.LOCATION_NMEA_RMC, listener)
}
/**
* 打开GPS
* @returns 返回是否打开成功
*/
export async function openGPS(): Promise<boolean> {
try {
return await nativeLocation.openGPS()
} catch (e) {
return false
}
}
/**
* 关闭GPS
* @returns 返回是否关闭成功
*/
export async function closeGPS(): Promise<boolean> {
try {
return await nativeLocation.closeGPS()
} catch (e) {
return false
}
}
/**
* 是否允许定位功能在后台开启
* @param enable 是否开启后台运行定位
* @returns 返回是否开启成功
*/
export function setBackgroundLocationEnable(enable = true) {
try {
return nativeLocation.setBackgroundLocationEnable(enable)
} catch (e) {
console.error(e)
}
}
/**
* 变更设备
* @param param 设备信息
*/
export async function changeLocationDevice(param: LocationConnectionParam): Promise<void> {
try {
await nativeLocation.closeGPS()
await nativeLocation.changeLocationDevice(param)
await nativeLocation.openGPS()
} catch (e) {
console.error(e)
}
}
/**
* 扫描蓝牙设备
*
* 目前只能扫 woncan rtk 设备
*
* @platform android
*/
export function scanBluetooth(): Promise<true> {
return nativeLocation.scanBluetooth()
}
let btscanevent: EmitterSubscription | null = null
/** 设置蓝牙扫描结果监听 */
export function setBTScanListner(listener?: (devices: BluetoothDevice) => void): void {
if(Platform.OS !== 'ios') {
btscanevent?.remove()
btscanevent = listener ? eventEmitter.addListener(EventConst.LOCATION_BT_SCAN_RESULT, listener) : null
}
}
/**
* 获取Ntrip服务器加载点列表
* @param info Ntrip服务器地址信息
* @returns 返回Ntrip服务器加载点列表
*/
export function getNtripSourceTable(info: Partial<NtripServerInfo> & Pick<NtripServerInfo, 'address' | 'port'>): Promise<NtripMountPoint[]> {
return nativeLocation.getNtripSourceTable(info)
}
/**
* 设置Ntrip服务器信息
* @param info Ntrip服务器/挂载点的信息
* @returns 是否设置成功
*/
export function setNtripInfo(info: NtripServerInfo & NtripMountPoint): Promise<boolean> {
return nativeLocation.setNtripInfo(info)
}
/**
* 连接Ntrip服务器
* @returns 是否连接成功
*/
export function connectNtrip(): Promise<boolean> {
return nativeLocation.connectNtrip()
}
/**
* 断开Ntrip服务器
* @returns 是否断开成功
*/
export function disconnectNtrip(): Promise<boolean> {
return nativeLocation.connectNtrip()
}
/**
* 开启/关闭搜索外部设备
* @param isSearch 是否开启
* @returns 是否开启/关闭成功
*/
export async function searchDevice(isSearch: boolean): Promise<boolean> {
return nativeLocation.searchDevice(isSearch)
}
/**
* 添加设备查询监听
* @param callback 监听事件
*/
export function addDeviceListener(callback?:(deviceName: string) => void) {
deviceListener?.remove()
deviceListener = undefined
if (callback && typeof callback === 'function') {
deviceListener = eventEmitter.addListener(EventConst.LOCATION_SEARCH_DEVICE, function (e) {
callback(e)
})
}
}
/**
* 蓝牙的状态类型
* 1: 蓝牙状态变化已打开
* 2: 蓝牙状态变化为已关闭
*/
export type bluetoothStateType = 1 | 2
let bluetoothStateListener: EmitterSubscription | undefined = undefined
/**
* 添加/移除蓝牙状态变化监听
* @param callback 回调方法,参数值为数字
* 1: 蓝牙状态变化已打开
* 2: 蓝牙状态变化为已关闭
* @author lyx
*/
export function setBluetoothStateListener(callback?:(bluetoothState: bluetoothStateType) => void) {
if(Platform.OS !== 'ios') {
bluetoothStateListener?.remove()
bluetoothStateListener = callback ? eventEmitter.addListener(EventConst.BLUETOOTH_STATE_CHANGE, callback) : undefined
}
}
/**
* 获取系统配对的蓝牙设备
* @returns 返回蓝牙设备信息数组
*/
export function getPairedBTDevices(): Promise<BluetoothDevice[]> {
return nativeLocation.getPairedBTDevices()
}
/**
* 设置是否开启时间定位
* @returns 返回是否开启成功
*/
export function setTimeLocation (location: boolean): Promise<boolean> {
return nativeLocation.setTimeLocation(location)
}
/**
* 设置是否开启距离定位
* @returns 返回是否开启成功
*/
export function setDistanceLocation (location: boolean): Promise<boolean> {
return nativeLocation.setDistanceLocation(location)
}
/**
* 设置时间定位时间间隔
* @returns 返回是否设置成功
*/
export function setLocationTime (time: string): Promise<boolean> {
return nativeLocation.setLocationTime(time)
}
/**
* 设置距离定位长度
* @returns 返回是否设置成功
*/
export function setLocationDistance (distance:string): Promise<boolean> {
return nativeLocation.setLocationDistance(distance)
}
/**
* 获取是否开启时间定位
* @returns 是否开启时间定位
*/
export function getTimeLocation(): Promise<boolean>{
return nativeLocation.getTimeLocation()
}
/**
* 获取是否开启成功
* @returns 是否开启成功
*/
export function getDistanceLocation(): Promise<boolean>{
return nativeLocation.getDistanceLocation()
}
/**
* 获取时间定位时间间隔
* @returns 时间定位时间间隔
*/
export async function getLocationTime(): Promise<number> {
return await nativeLocation.getLocationTime()
}
/**
* 获取距离定位长度
* @returns 距离定位长度
*/
export async function getLocationDistance(): Promise<number> {
return await nativeLocation.getLocationDistance()
}
/**
* 开启硬件感应器监听设备方向变化
* @returns 返回是否开启成功,失败返回false
*/
export async function startCompassSensor(): Promise<boolean> {
try {
return await nativeLocation.startCompassSensor()
} catch (e) {
return false
}
}
/**
* 关闭硬件感应器监听设备方向变化
* @returns 返回是否关闭成功,失败返回false
*/
export async function stopCompassSensor(): Promise<boolean> {
try {
return await nativeLocation.stopCompassSensor()
} catch (e) {
return false
}
}
let compassListener: EmitterSubscription | null = null
/**
* 添加/移除设备朝向方位监听
* @param listener 监听回调事件
*
* 监听返回与正北方向的夹角度数 范围 0 - 360
*
* 东 - 90
*
* 南 - 180
*
* 西 - 270
*
*/
export function addCompassListener(listener?: (degree: number) => void) {
compassListener && compassListener.remove()
compassListener = null
if (listener && typeof listener === 'function') {
compassListener = eventEmitter.addListener(EventConst.LOCATION_COMPASS, function (e) {
listener(e)
})
}
}
/** 蓝牙设备定位的精度
* 1 米级(不拦截任何精度)
* 5 亚米级(只接收 floatRtk 和 Rtk)
* 4 厘米级(仅接收Rtk)
* */
export type PositionAccuracyType = 1 | 4 | 5
/**
* 设置蓝牙设备使用的精度 限android使用
* @param positionAccuracyTemp 精度
* 1 米级(不拦截任何精度)
* 5 亚米级(只接收 floatRtk 和 Rtk)
* 4 厘米级(仅接收Rtk)
* @param promise
* @returns {boolean}
* @author lyx
*/
export async function setPositionAccuracy(positionAccuracy: PositionAccuracyType): Promise<boolean> {
if(Platform.OS !== 'android') return false
return await nativeLocation.setPositionAccuracy(positionAccuracy)
}
/**
* 获取蓝牙状态 判断蓝牙是否打开 限android使用
* @return boolean 蓝牙已打开返回true,否者false
* @author lyx
*/
export async function bluetoothIsOpen(): Promise<boolean> {
if(Platform.OS !== 'android') return false
return await nativeLocation.bluetoothIsOpen()
}
/**
* 打开蓝牙 限android使用
* @return boolean 是否打开蓝牙成功 成功返回true,否者fasle
* @author lyx
*/
export async function openBluetooth(): Promise<boolean> {
if(Platform.OS !== 'android') return false
return await nativeLocation.openBluetooth()
}
/**
* 关闭蓝牙 限android使用
* @return boolean 是否关闭蓝牙成功 成功返回true,否者fasle
* @author lyx
*/
export async function closeBluetooth(): Promise<boolean> {
if(Platform.OS !== 'android') return false
return await nativeLocation.closeBluetooth()
}
/**
* 停止千寻设备的扫描 限android使用
* @return boolean 停止千寻设备的扫描是否成功 成功返回true,否者fasle
* @author lyx
*/
export async function stopScanBluetooth(): Promise<boolean> {
if(Platform.OS !== 'android') return false
return await nativeLocation.stopScanBluetooth()
}
/*
* NMEA 消息转换类 及 各种需要计算的参数
*/
/**
* 定位精度信息
*/
interface GST {
/** UTC时间 */
time: number
/**
* 用于导航计算的伪距标准偏差的平方根值
*/
rms: number
/**
* 椭球体长半轴标准偏差(单位:米)
*/
stdSemiMajor: number
/**
* 椭球体短半轴标准偏差(单位:米)
*/
stdSemiMinor: number
/**
* 椭球体长半轴方位(单位:度)
*/
orient: number
/**
* 标准纬度偏差(单位:米)
*/
stdLat: number
/**
* 标准经度偏差(单位:米)
*/
stdLon: number
/**
* 标准高度偏差(单位:米)
*/
stdAlt: number
}
/**
* 解析 NMEA GST 报文
*
* 参考: https://docs.novatel.com/OEM7/Content/Logs/GPGST.htm
* @param line gst消息字符串 eg: $GPGST,203017.00,1.25,0.02,0.01,-16.7566,0.02,0.01,0.03*7D
* @returns GST 报文对象
*/
export function parseGST(line: string): GST | null {
try {
//todo 校验消息完整性
const protocal = line.substring(3, 6)
if(protocal === 'GST') {
const gst = line.split(',')
return {
time: parseFloat(gst[1]),
rms: parseFloat(gst[2]),
stdSemiMajor: parseFloat(gst[3]),
stdSemiMinor: parseFloat(gst[4]),
orient: parseFloat(gst[5]),
stdLat: parseFloat(gst[6]),
stdLon: parseFloat(gst[7]),
stdAlt: parseFloat(gst[8])
}
}
return null
} catch(e) {
return null
}
}
/**
* 获取水平方向均方根 Root Mean Square (RMS)
* @param gst GST报文对象
* @returns 水平方向精度 hrms
*/
export function getHRMS(gst: GST): number {
return Math.sqrt(Math.pow(gst.stdLat, 2) + Math.pow(gst.stdLon, 2))
}