UNPKG

imobile_for_reactnative

Version:

iMobile for ReactNative,是SuperMap iMobile推出的一款基于React-Native框架的移动应用开发工具。基于该开发工具,用户可以使用JavaScript开发语言,开发出在Android和IOS操作系统下运行的原生移动GIS应用,入门门槛低,一次开发,处处运行。

654 lines (585 loc) 16.6 kB
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)) }