diff --git a/lib/android/build.gradle b/lib/android/build.gradle index 79db9f0..8959ed6 100644 --- a/lib/android/build.gradle +++ b/lib/android/build.gradle @@ -40,4 +40,5 @@ dependencies { api 'com.facebook.react:react-native:+' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'com.amap.api:3dmap:9.4.0' + implementation 'com.amap.api:location:6.1.0' } diff --git a/lib/android/src/main/AndroidManifest.xml b/lib/android/src/main/AndroidManifest.xml index 5942f9a..f1a33f1 100644 --- a/lib/android/src/main/AndroidManifest.xml +++ b/lib/android/src/main/AndroidManifest.xml @@ -7,4 +7,13 @@ + + + + + + + + + diff --git a/lib/android/src/main/java/cn/feewee/amap3d/AMap3DPackage.kt b/lib/android/src/main/java/cn/feewee/amap3d/AMap3DPackage.kt index 9214426..3c716d1 100644 --- a/lib/android/src/main/java/cn/feewee/amap3d/AMap3DPackage.kt +++ b/lib/android/src/main/java/cn/feewee/amap3d/AMap3DPackage.kt @@ -6,11 +6,13 @@ import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.uimanager.ViewManager import cn.feewee.amap3d.map_view.* import cn.feewee.amap3d.modules.SdkModule +import cn.feewee.amap3d.modules.AMapGeolocationModule class AMap3DPackage : ReactPackage { override fun createNativeModules(reactContext: ReactApplicationContext): List { return listOf( SdkModule(reactContext), + new AMapGeolocationModule(reactContext) ) } diff --git a/lib/android/src/main/java/cn/feewee/amap3d/modules/AMapGeolocationModule.java b/lib/android/src/main/java/cn/feewee/amap3d/modules/AMapGeolocationModule.java new file mode 100644 index 0000000..d999ea6 --- /dev/null +++ b/lib/android/src/main/java/cn/feewee/amap3d/modules/AMapGeolocationModule.java @@ -0,0 +1,205 @@ +package cn.feewee.amap3d.modules; + +import com.amap.api.location.AMapLocation; +import com.amap.api.location.AMapLocationClient; +import com.amap.api.location.AMapLocationClientOption; +import com.amap.api.location.AMapLocationListener; +import com.facebook.react.bridge.*; +import com.facebook.react.modules.core.DeviceEventManagerModule; +import org.jetbrains.annotations.NotNull; + +@SuppressWarnings("unused") +public class AMapGeolocationModule extends ReactContextBaseJavaModule implements AMapLocationListener { + private final ReactApplicationContext reactContext; + private final AMapLocationClientOption option = new AMapLocationClientOption(); + private DeviceEventManagerModule.RCTDeviceEventEmitter eventEmitter; + private AMapLocationClient client; + + AMapGeolocationModule(ReactApplicationContext reactContext) { + super(reactContext); + this.reactContext = reactContext; + } + + @NotNull + @Override + public String getName() { + return "AMapGeolocation"; + } + + @Override + public void onLocationChanged(AMapLocation location) { + if (location != null) { + eventEmitter.emit("AMapGeolocation", toJSON(location)); + } + } + + @ReactMethod + public void init(String key, Promise promise) throws Exception { + if (client != null) { + client.onDestroy(); + } + + AMapLocationClient.setApiKey(key); + AMapLocationClient.updatePrivacyShow(reactContext, true, true); + AMapLocationClient.updatePrivacyAgree(reactContext, true); + client = new AMapLocationClient(reactContext); + client.setLocationListener(this); + eventEmitter = reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class); + promise.resolve(null); + } + + @ReactMethod + public void start() { + client.startLocation(); + } + + @ReactMethod + public void stop() { + client.stopLocation(); + } + + @ReactMethod + public void addListener(String name) { + } + + @ReactMethod + public void removeListeners(Integer count) { + } + + @ReactMethod + public void isStarted(Promise promise) { + promise.resolve(client.isStarted()); + } + + @ReactMethod + public void getLastKnownLocation(Promise promise) { + promise.resolve(toJSON(client.getLastKnownLocation())); + } + + @ReactMethod + public void setOnceLocation(boolean value) { + option.setOnceLocation(value); + client.setLocationOption(option); + } + + @ReactMethod + public void setWifiScan(boolean value) { + option.setWifiScan(value); + client.setLocationOption(option); + } + + @ReactMethod + public void setInterval(int interval) { + option.setInterval(interval); + client.setLocationOption(option); + } + + @ReactMethod + public void setSensorEnable(boolean value) { + option.setSensorEnable(value); + client.setLocationOption(option); + } + + @ReactMethod + public void setOpenAlwaysScanWifi(boolean value) { + AMapLocationClientOption.setOpenAlwaysScanWifi(value); + client.setLocationOption(option); + } + + @ReactMethod + public void setNeedAddress(boolean value) { + option.setNeedAddress(value); + client.setLocationOption(option); + } + + @ReactMethod + public void setOnceLocationLatest(boolean value) { + option.setOnceLocationLatest(value); + client.setLocationOption(option); + } + + @ReactMethod + public void setMockEnable(boolean value) { + option.setMockEnable(value); + client.setLocationOption(option); + } + + @ReactMethod + public void setLocationCacheEnable(boolean value) { + option.setLocationCacheEnable(value); + client.setLocationOption(option); + } + + @ReactMethod + public void setGpsFirst(boolean value) { + option.setGpsFirst(value); + client.setLocationOption(option); + } + + @ReactMethod + public void setHttpTimeout(int value) { + option.setHttpTimeOut(value); + client.setLocationOption(option); + } + + @ReactMethod + public void setGpsFirstTimeout(int value) { + option.setGpsFirstTimeout(value); + client.setLocationOption(option); + } + + @ReactMethod + public void setLocationMode(String mode) { + option.setLocationMode(AMapLocationClientOption.AMapLocationMode.valueOf(mode)); + client.setLocationOption(option); + } + + @ReactMethod + public void setLocationPurpose(String purpose) { + option.setLocationPurpose(AMapLocationClientOption.AMapLocationPurpose.valueOf(purpose)); + client.setLocationOption(option); + } + + @ReactMethod + public void setGeoLanguage(String language) { + option.setGeoLanguage(AMapLocationClientOption.GeoLanguage.valueOf(language)); + client.setLocationOption(option); + } + + private ReadableMap toJSON(AMapLocation location) { + if (location == null) { + return null; + } + WritableMap map = Arguments.createMap(); + map.putInt("errorCode", location.getErrorCode()); + map.putString("errorInfo", location.getErrorInfo()); + map.putString("locationDetail", location.getLocationDetail()); + if (location.getErrorCode() == AMapLocation.LOCATION_SUCCESS) { + map.putDouble("timestamp", location.getTime()); + map.putDouble("accuracy", location.getAccuracy()); + map.putDouble("latitude", location.getLatitude()); + map.putDouble("longitude", location.getLongitude()); + map.putDouble("altitude", location.getAltitude()); + map.putDouble("speed", location.getSpeed()); + map.putDouble("heading", location.getBearing()); + map.putInt("locationType", location.getLocationType()); + map.putString("coordinateType", location.getCoordType()); + map.putInt("gpsAccuracy", location.getGpsAccuracyStatus()); + map.putInt("trustedLevel", location.getTrustedLevel()); + if (!location.getAddress().isEmpty()) { + map.putString("address", location.getAddress()); + map.putString("description", location.getDescription()); + map.putString("poiName", location.getPoiName()); + map.putString("country", location.getCountry()); + map.putString("province", location.getProvince()); + map.putString("city", location.getCity()); + map.putString("cityCode", location.getCityCode()); + map.putString("district", location.getDistrict()); + map.putString("street", location.getStreet()); + map.putString("streetNumber", location.getStreetNum()); + map.putString("adCode", location.getAdCode()); + } + } + return map; + } +} diff --git a/lib/src/amap-geolocation.ts b/lib/src/amap-geolocation.ts new file mode 100644 index 0000000..10dcf04 --- /dev/null +++ b/lib/src/amap-geolocation.ts @@ -0,0 +1,352 @@ +import { NativeModules, NativeEventEmitter, Platform } from "react-native"; +import { ILocation as Location, ReGeocode, AppKey, LocationMode, LocationPurpose, GeoLanguage } from "./gen-types"; + +const AMapGeolocation = NativeModules.AMapGeolocation; +const eventEmitter = new NativeEventEmitter(AMapGeolocation); + +/** + * 初始化 SDK + * + * @param key 高德开放平台应用 Key + */ +export function init(key: AppKey): Promise { + return AMapGeolocation.init(Platform.select(key)); +} + +/** + * 添加定位监听函数 + * + * @param listener + */ +export function addLocationListener(listener: (location: Location & ReGeocode) => void) { + return eventEmitter.addListener("AMapGeolocation", listener); +} + +/** + * 开始持续定位 + */ +export function start() { + AMapGeolocation.start(); +} + +/** + * 停止持续定位 + */ +export function stop() { + AMapGeolocation.stop(); +} + +/** + * 获取当前是否正在定位的状态 + * + * @platform android + */ +export function isStarted(): boolean { + return AMapGeolocation.isStarted(); +} + +/** + * 设置发起定位请求的时间间隔(毫秒),默认 2000,最小值为 1000 + * + * @default 2000 + * @platform android + */ +export function setInterval(interval: number) { + if (Platform.OS === "android") { + AMapGeolocation.setInterval(interval); + } +} + +/** + * 设置是否单次定位 + * + * @default false + * @platform android + */ +export function setOnceLocation(isOnceLocation: boolean) { + if (Platform.OS === "android") { + AMapGeolocation.setOnceLocation(isOnceLocation); + } +} + +/** + * 设置是否允许调用 WiFi 刷新 + * + * 当设置为 `false` 时会停止主动调用 wifi 刷新,将会极大程度影响定位精度, + * 但可以有效的降低定位耗电。 + * + * @default true + * @platform android + */ +export function setWifiScan(isWifiScan: boolean) { + if (Platform.OS === "android") { + AMapGeolocation.setWifiScan(isWifiScan); + } +} + +/** + * 设置是否使用设备传感器 + * + * @default false + * @platform android + */ +export function setSensorEnable(enable: boolean) { + if (Platform.OS === "android") { + AMapGeolocation.setSensorEnable(enable); + } +} + +/** + * 设置是否开启wifi始终扫描 + * + * 只有设置了 `android.permission.WRITE_SECURE_SETTINGS` 权限后才会开启。 + * 开启后,即使关闭 wifi 开关的情况下也会扫描 wifi。 + * 此方法为静态方法,设置一次后其他定位 client 也会生效。 + * + * @default true + * @platform android + */ +export function setOpenAlwaysScanWifi(isOpen: boolean) { + if (Platform.OS === "android") { + AMapGeolocation.setOpenAlwaysScanWifi(isOpen); + } +} + +/** + * 设置定位是否等待 WiFi 列表刷新 + * + * 定位精度会更高,但是定位速度会变慢 1-3 秒, + * 当设置为 `true` 时,连续定位会自动变为单次定位。 + * + * @default false + * @platform android + */ +export function setOnceLocationLatest(isOnceLocationLatest: boolean) { + if (Platform.OS === "android") { + AMapGeolocation.setOnceLocationLatest(isOnceLocationLatest); + } +} + +/** + * 设置是否返回地址信息,默认返回地址信息 + * + * GPS 定位时也可以返回地址信息,但需要网络通畅,第一次有可能没有地址信息返回。 + * + * @default true + * @platform android + */ +export function setNeedAddress(isNeedAddress: boolean) { + if (Platform.OS === "android") { + AMapGeolocation.setNeedAddress(isNeedAddress); + } +} + +/** + * 设置是否允许模拟位置 + * + * @default true + * @platform android + */ +export function setMockEnable(enable: boolean) { + if (Platform.OS === "android") { + AMapGeolocation.setMockEnable(enable); + } +} + +/** + * 设置是否使用缓存策略 + * + * @default true + * @platform android + */ +export function setLocationCacheEnable(enable: boolean) { + if (Platform.OS === "android") { + AMapGeolocation.setLocationCacheEnable(enable); + } +} + +/** + * 设置联网超时时间(毫秒) + * + * @default 30000 + * @platform android + */ +export function setHttpTimeout(timeout: number) { + if (Platform.OS === "android") { + AMapGeolocation.setHttpTimeout(timeout); + } +} + +/** + * 设置优先返回卫星定位信息时等待卫星定位结果的超时时间(毫秒) + * + * 只有在 `setGpsFirst(true)` 时才有效。 + * + * @platform android + */ +export function setGpsFirstTimeout(timeout: number) { + if (Platform.OS === "android") { + AMapGeolocation.setGpsFirstTimeout(timeout); + } +} + +/** + * 设置首次定位是否等待卫星定位结果 + * + * 只有在单次定位高精度定位模式下有效,设置为 `true` 时,会等待卫星定位结果返回, + * 最多等待 30 秒,若 30 秒后仍无卫星定位结果返回,返回网络定位结果。 + * 等待卫星定位结果返回的时间可以通过 [[setGpsFirstTimeout]] 进行设置。 + * + * @default false + * @platform android + */ +export function setGpsFirst(isGpsFirst: boolean) { + if (Platform.OS === "android") { + AMapGeolocation.setGpsFirst(isGpsFirst); + } +} + +/** + * 设置定位模式 + * + * @platform android + */ +export function setLocationMode(mode: LocationMode) { + if (Platform.OS === "android") { + AMapGeolocation.setLocationMode(mode); + } +} + +/** + * 设置定位场景 + * + * 根据场景快速修改 option,不支持动态改变,修改后需要调用 [[start]] 使其生效,当不需要场景时,可以设置为 `null`。 + * + * 注意:不建议设置场景和自定义 option 混合使用。设置场景后,如果已经开始定位了,建议调用一次 [[stop]],然后主动调用一次 [[start]] + * 以保证 option 正确生效。当主动设置的 option 和场景中的 option 有冲突时,以后设置的为准,比如:签到场景中默认的为单次定位, + * 当主动设置 option 为连续定位时,如果先设置的场景,后改变的 option,这时如果不调用 [[start]] 不会变为连续定位, + * 如果调用了 [[start]] 则会变为连续定位,如果先改变 option,后设置场景为签到场景,则会变为单次定位。 + * + * @platform android + */ +export function setLocationPurpose(purpose: LocationPurpose) { + if (Platform.OS === "android") { + AMapGeolocation.setLocationPurpose(purpose); + } +} + +/** + * 设置逆地理信息的语言,目前支持中文和英文 + * + * @default GeoLanguage.DEFAULT + */ +export function setGeoLanguage(language: GeoLanguage) { + AMapGeolocation.setGeoLanguage(language); +} + +/** + * 设定定位的最小更新距离(米) + * + * 默认为 `kCLDistanceFilterNone`,表示只要检测到设备位置发生变化就会更新位置信息。 + * + * @platform ios + */ +export function setDistanceFilter(distance: number) { + if (Platform.OS === "ios") { + AMapGeolocation.setDistanceFilter(distance); + } +} + +/** + * 设定期望的定位精度(米) + * + * 默认为 `kCLLocationAccuracyBest`。 + * 定位服务会尽可能去获取满足 `desiredAccuracy` 的定位结果,但不保证一定会得到满足期望的结果。 + * + * 注意:设置为 `kCLLocationAccuracyBest` 或 `kCLLocationAccuracyBestForNavigation` 时, + * 单次定位会在达到 `locationTimeout` 设定的时间后,将时间内获取到的最高精度的定位结果返回。 + * + * @platform ios + */ +export function setDesiredAccuracy(desiredAccuracy: number) { + if (Platform.OS === "ios") { + AMapGeolocation.setDesiredAccuracy(desiredAccuracy); + } +} + +/** + * 指定定位是否会被系统自动暂停 + * + * @default false + * @platform ios + */ +export function setPausesLocationUpdatesAutomatically(isPause: boolean) { + if (Platform.OS === "ios") { + AMapGeolocation.setPausesLocationUpdatesAutomatically(isPause); + } +} + +/** + * 是否允许后台定位 + * + * 只在iOS 9.0 及之后起作用。 + * 设置为YES的时候必须保证 `Background Modes` 中的 `Location updates` 处于选中状态,否则会抛出异常。 + * 由于iOS系统限制,需要在定位未开始之前或定位停止之后,修改该属性的值才会有效果。 + * + * @default false + * @platform ios + */ +export function setAllowsBackgroundLocationUpdates(isAllow: boolean) { + if (Platform.OS === "ios") { + AMapGeolocation.setAllowsBackgroundLocationUpdates(isAllow); + } +} + +/** + * 指定单次定位超时时间(秒) + * + * 最小值是 2s。注意在单次定位请求前设置。 + * + * 注意: 单次定位超时时间从确定了定位权限(非 `kCLAuthorizationStatusNotDetermined` 状态)后开始计算。 + * + * @default 10 + * @platform ios + */ +export function setLocationTimeout(timeout: number) { + if (Platform.OS === "ios") { + AMapGeolocation.setLocationTimeout(timeout); + } +} + +/** + * 指定单次定位逆地理超时时间(秒) + * + * 最小值是 2s。注意在单次定位请求前设置。 + * + * @default 5 + * @platform ios + */ +export function setReGeocodeTimeout(timeout: number) { + if (Platform.OS === "ios") { + AMapGeolocation.setReGeocodeTimeout(timeout); + } +} + +interface Options { + locatingWithReGeocode?: boolean; +} + +export const _options: Options = {}; + +/** + * 连续定位是否返回逆地理编码 + * + * @default false + * @platform ios + */ +export function setLocatingWithReGeocode(withReGeocode: boolean) { + _options.locatingWithReGeocode = withReGeocode; + if (Platform.OS === "ios") { + AMapGeolocation.setLocatingWithReGeocode(withReGeocode); + } +} diff --git a/lib/src/gen-types.ts b/lib/src/gen-types.ts new file mode 100644 index 0000000..b663845 --- /dev/null +++ b/lib/src/gen-types.ts @@ -0,0 +1,398 @@ +/** + * 高德开放平台应用 Key + */ +export interface AppKey { + ios: string; + android: string; +} + +/** + * 定位结果类型 + * + * @platform android + */ +export enum LocationType { + /** + * 卫星定位结果 + * + * 通过设备卫星定位模块返回的定位结果 + */ + GPS = 1, + + /** + * 前次定位结果 + * + * 网络定位请求低于1秒、或两次定位之间设备位置变化非常小时返回,设备位移通过传感器感知 + */ + SAME_REQ, + + /** + * @deprecated + */ + FAST, + + /** + * 缓存定位结果 + * + * 返回一段时间前设备在相同的环境中缓存下来的网络定位结果,节省无必要的设备定位消耗 + */ + FIX_CACHE, + + /** + * Wifi定位结果 + * + * 属于网络定位,定位精度相对基站定位会更好 + */ + WIFI, + + /** + * 基站定位结果 + * + * 属于网络定位 + */ + CELL, + + AMAP, + + /** + * 离线定位结果 + */ + OFFLINE, + + /** + * 最后位置缓存 + */ + LAST_LOCATION_CACHE, +} + +/** + * iOS 错误代码 + * + * @platform ios + */ +export enum ErrorCodeIOS {} + +/** + * Android 错误代码 + * + * @platform android + */ +export enum ErrorCodeAndroid { + /** + * 定位成功 + */ + LOCATION_SUCCESS, + + /** + * 一些重要参数为空,可以通过 [[Location.locationDetail]] 获取详细信息 + */ + INVALID_PARAMETER, + + /** + * 定位失败,由于设备仅扫描到单个 wifi,不能精准的计算出位置信息 + */ + FAILURE_WIFI_INFO, + + /** + * 获取到的请求参数为空,可能获取过程中出现异常,可以通过 [[Location.locationDetail]] 获取详细信息 + */ + FAILURE_LOCATION_PARAMETER, + + /** + * 网络连接异常,可以通过 [[Location.locationDetail]] 获取详细信息 + */ + FAILURE_CONNECTION, + + /** + * 解析 XML 出错,可以通过 [[Location.locationDetail]] 获取详细信息 + */ + FAILURE_PARSER, + + /** + * 定位结果错误,可以通过 [[Location.locationDetail]] 获取详细信息 + */ + FAILURE_LOCATION, + + /** + * Key 错误,可以通过 [[Location.locationDetail]] 获取详细信息来跟注册的 Key 信息进行对照 + */ + FAILURE_AUTH, + + /** + * 其他错误,可以通过 [[Location.locationDetail]] 获取详细信息 + */ + UNKNOWN, + + /** + * 初始化异常,可以通过 [[Location.locationDetail]] 获取详细信息 + */ + FAILURE_INIT, + + /** + * 定位服务启动失败,请检查是否配置 service 并且 manifest 中 service 标签是否配置在 application 标签内 + */ + SERVICE_FAIL, + + /** + * 错误的基站信息,请检查是否安装 sim 卡 + */ + FAILURE_CELL, + + /** + * 缺少定位权限,请检查是否配置定位权限,并在安全软件和设置中给应用打开定位权限 + */ + FAILURE_LOCATION_PERMISSION, + + /** + * 网络定位失败,请检查设备是否插入 sim 卡、开启移动网络或开启了 wifi 模块 + */ + FAILURE_NOWIFIANDAP, + + /** + * 卫星定位失败,可用卫星数不足 + */ + FAILURE_NOENOUGHSATELLITES, + + /** + * 定位位置可能被模拟 + */ + FAILURE_SIMULATION_LOCATION, + + /** + * 定位失败,飞行模式下关闭了 wifi 开关,请关闭飞行模式或者打开 wifi 开关 + */ + AIRPLANEMODE_WIFIOFF = 18, + + /** + * 定位失败,没有检查到 sim 卡,并且关闭了 wifi 开关,请打开 wifi 开关或者插入 sim 卡 + */ + NOCGI_WIFIOFF, +} + +export type ErrorCode = ErrorCodeAndroid | ErrorCodeIOS; + +/** + * 定位模式,目前支持三种定位模式 + * + * @platform android + */ +export enum LocationMode { + /** + * 低功耗模式,在这种模式下,将只使用高德网络定位。 + */ + Battery_Saving = "Battery_Saving", + + /** + * 仅设备模式,只使用卫星定位,不支持室内环境的定位 + */ + Device_Sensors = "Device_Sensors", + + /** + * 高精度模式,在这种定位模式下,将同时使用高德网络定位和卫星定位,优先返回精度高的定位 + */ + Hight_Accuracy = "Hight_Accuracy", +} + +/** + * 定位场景 + * + * @platform android + */ +export enum LocationPurpose { + /** + * 签到场景 + * + * 只进行一次定位返回最接近真实位置的定位结果(定位速度可能会延迟 1-3s)。 + */ + SignIn = "SignIn", + + /** + * 运动场景 + * + * 高精度连续定位,适用于有户内外切换的场景,卫星定位和网络定位相互切换,卫星定位成功之后网络定位不再返回,卫星信号断开之后一段时间才会返回网络结果。 + */ + Sport = "Sport", + + /** + * 出行场景 + * + * 高精度连续定位,适用于有户内外切换的场景,卫星定位和网络定位相互切换,卫星定位成功之后网络定位不再返回,卫星信号断开之后一段时间才会返回网络结果。 + */ + Transport = "Transport", +} + +/** + * 逆地理编码语言 + */ +export enum GeoLanguage { + /** + * 默认,根据位置按照相应的语言返回逆地理信息,在国外按英语返回,在国内按中文返回 + */ + DEFAULT = "DEFAULT", + + /** + * 中文,无论在国外还是国内都为返回中文的逆地理信息 + */ + ZH = "ZH", + + /** + * 英文,无论在国外还是国内都为返回英文的逆地理信息 + */ + EN = "EN", +} + +/** + * 卫星信号强度 + * + * @platform android + */ +export enum GpsAccuracy { + UNKNOWN, + BAD, + GOOD, +} + +/** + * 定位结果的可信度 + */ +export enum TrustedLevel { + HIGH = 1, + NORMAL, + LOW, + BAD, +} + +/** + * 定位信息 + */ +export interface ILocation { + /** + * 定位精度 (米) + */ + accuracy: number; + + /** + * 经度,[-180, 180] + */ + latitude: number; + + /** + * 纬度,[-90, 90] + */ + longitude: number; + + /** + * 海拔(米),需要 GPS + */ + altitude?: number; + + /** + * 移动速度(米/秒),需要 GPS + */ + speed?: number; + + /** + * 移动方向,需要 GPS + */ + heading?: number; + + /** + * 定位时间(毫秒) + */ + timestamp?: number; + + /** + * 错误码 + */ + errorCode?: ErrorCode; + + /** + * 错误信息 + */ + errorInfo?: string; + + /** + * 定位信息描述 + * + * @platform android + */ + locationDetail?: string; + + /** + * 定位结果来源 + * + * @platform android + */ + locationType?: LocationType; + + /** + * 卫星信号强度,仅在卫星定位时有效 + * + * @platform android + */ + gpsAccuracy?: GpsAccuracy; + + /** + * 坐标系类型 + * + * @platform android + */ + coordinateType?: "WGS84" | "GCJ02"; + + /** + * 定位结果的可信度,只有在定位结果正确时,才有意义 + * + * @platform android + */ + trustedLevel?: TrustedLevel; +} + +/** + * 逆地理编码信息 + */ +export interface ReGeocode { + /** + * 详细地址 + */ + address?: string; + + /** + * 国家 + */ + country?: string; + + /** + * 省份 + */ + province?: string; + + /** + * 城市 + */ + city?: string; + + /** + * 城市编码 + */ + cityCode?: string; + + /** + * 地区 + */ + district?: string; + + /** + * 街道 + */ + street?: string; + + /** + * 门牌号 + */ + streetNumber?: string; + + /** + * 兴趣点 + */ + poiName?: string; +} diff --git a/lib/src/geolocation.ts b/lib/src/geolocation.ts new file mode 100644 index 0000000..82744e0 --- /dev/null +++ b/lib/src/geolocation.ts @@ -0,0 +1,151 @@ +import { EmitterSubscription } from "react-native"; +import { addLocationListener, start, stop, _options } from "./amap-geolocation"; +import { ILocation as Location } from "./gen-types"; + +/** + * 坐标信息 + * + * @see https://developer.mozilla.org/zh-CN/docs/Web/API/Coordinates + */ +export interface Coordinates { + latitude: number; + longitude: number; + altitude: number; + accuracy: number; + altitudeAccuracy: number; + heading: number; + speed: number; +} + +/** + * 定位信息 + * + * @see https://developer.mozilla.org/zh-CN/docs/Web/API/Position + */ +export interface Position { + coords: Coordinates; + timestamp: number; + location: Location; +} + +/** + * 定位错误信息 + * + * @see https://developer.mozilla.org/zh-CN/docs/Web/API/PositionError + */ +export class PositionError { + static PERMISSION_DENIED: 1; + static POSITION_UNAVAILABLE: 2; + static TIMEOUT: 3; + + code: number; + message: string; + location: Location; + + constructor(code: number, message: string, location: Location) { + this.code = code; + this.message = message; + this.location = location; + } +} + +/** + * 定位选项 + * + * @see https://developer.mozilla.org/zh-CN/docs/Web/API/PositionOptions + */ +export interface PositionOptions { + timeout?: number; + maximumAge?: number; + enableHighAccuracy?: boolean; + + /** + * @see [[setDistanceFilter]] + */ + distanceFilter?: number; +} + +let watchId = 0; +const watchMap: { [watchId: number]: EmitterSubscription } = {}; + +/** + * @see https://developer.mozilla.org/zh-CN/docs/Web/API/Geolocation + */ +export default class Geolocation { + /** + * 获取当前位置信息 + * + * 注意:使用该方法会停止持续定位 + * + * @see https://developer.mozilla.org/zh-CN/docs/Web/API/Geolocation/getCurrentPosition + */ + static getCurrentPosition( + success: (position: Position) => void, + error?: (error: PositionError) => void + // options: PositionOptions = {} + ) { + const listener = addLocationListener((location) => { + if (location.errorCode) { + error && error(new PositionError(location.errorCode, location.errorInfo ?? "", location)); + stop(); + return listener.remove(); + } + if (_options.locatingWithReGeocode && typeof location.address !== "string") { + return; + } + success(toPosition(location)); + stop(); + return listener.remove(); + }); + start(); + } + + /** + * 注册监听器进行持续定位 + * + * @see https://developer.mozilla.org/zh-CN/docs/Web/API/Geolocation/watchPosition + */ + static watchPosition( + success: (position: Position) => void, + error?: (error: PositionError) => void + // options?: PositionOptions + ) { + watchMap[++watchId] = addLocationListener((location) => { + if (location.errorCode) { + error && error(new PositionError(location.errorCode, location.errorInfo ?? "", location)); + } else { + success(toPosition(location)); + } + }); + start(); + return watchId; + } + + /** + * 移除位置监听 + * + * @see https://developer.mozilla.org/zh-CN/docs/Web/API/Geolocation/clearWatch + */ + static clearWatch(id: number) { + const listener = watchMap[id]; + if (listener) { + listener.remove(); + } + } +} + +function toPosition(location: Location) { + return { + location, + coords: { + latitude: location.latitude, + longitude: location.longitude, + altitude: location.altitude ?? 0, + accuracy: location.accuracy, + altitudeAccuracy: 0, // 高德定位接口没有找到对应的数据 + heading: location.heading ?? 0, + speed: location.speed ?? 0, + }, + timestamp: location.timestamp ?? 0, + }; +} diff --git a/lib/src/index.ts b/lib/src/index.ts index c2ded5a..0b57598 100644 --- a/lib/src/index.ts +++ b/lib/src/index.ts @@ -6,6 +6,10 @@ export { default as Marker } from "./marker"; export { default as MultiPoint } from "./multi-point"; export { default as Polygon } from "./polygon"; export { default as Polyline } from "./polyline"; +import * as AMapSdk from "./sdk"; export * from "./types"; +export * from "./gen-types"; +export * from "./geolocation"; +export * from "./amap-geolocation"; +export { default as Geolocation } from "./geolocation"; export { AMapSdk }; -import * as AMapSdk from "./sdk"; diff --git a/package.json b/package.json index 4ac0578..c40f526 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cqfw/react-native-amap3d", - "version": "0.0.1", + "version": "0.0.2", "description": "react-native 高德地图组件,支持 Android + iOS", "author": "feewee", "license": "MIT", @@ -34,6 +34,7 @@ "supercluster": "^7.1.4" }, "devDependencies": { + "@types/react-native": "^0.70.4", "@types/supercluster": "^5.0.3", "react": "17.0.2", "react-native": "^0.62.0"