├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── android ├── README.md ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── uiwjs │ └── react │ └── geolocation │ ├── RNAMapGeolocationModule.java │ └── RNAMapGeolocationPackage.java ├── example ├── .eslintrc.js ├── .gitignore ├── .node-version ├── .prettierrc.js ├── .ruby-version ├── .watchmanconfig ├── App.tsx ├── Gemfile ├── Gemfile.lock ├── __tests__ │ └── App-test.tsx ├── android │ ├── app │ │ ├── build.gradle │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── uiwjs │ │ │ │ └── example │ │ │ │ └── geolocation │ │ │ │ └── ReactNativeFlipper.java │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── uiwjs │ │ │ │ │ └── example │ │ │ │ │ └── geolocation │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── MainApplication.java │ │ │ └── res │ │ │ │ ├── drawable │ │ │ │ └── rn_edit_text_material.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ └── values │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ └── release │ │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── ReactNativeFlipper.java │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── app.json ├── babel.config.js ├── index.js ├── ios │ ├── .xcode.env │ ├── Podfile │ ├── Podfile.lock │ ├── example.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── example.xcscheme │ ├── example.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── example │ │ ├── AppDelegate.h │ │ ├── AppDelegate.mm │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── LaunchScreen.storyboard │ │ └── main.m │ └── exampleTests │ │ ├── Info.plist │ │ └── exampleTests.m ├── metro.config.js ├── package.json ├── tsconfig.json └── yarn.lock ├── imgs ├── SERVICE_NOT_EXIST.png ├── amapkey.png ├── identifiers.png ├── sha1.png └── xcode.png ├── index.d.ts ├── index.js ├── ios ├── RNAMapGeolocation.h ├── RNAMapGeolocation.m ├── RNAMapGeolocation.xcodeproj │ └── project.pbxproj └── RNAMapGeolocation.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── package.json ├── react-native-amap-geolocation.podspec ├── renovate.json └── typedoc.json /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy Documents 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | build-deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - uses: actions/setup-node@v3 13 | with: 14 | node-version: 16 15 | registry-url: 'https://registry.npmjs.org' 16 | 17 | - run: npm install 18 | - run: npm run build 19 | 20 | - name: Is a tag created auto? 21 | id: create_tag 22 | uses: jaywcjlove/create-tag-action@main 23 | with: 24 | token: ${{ secrets.GITHUB_TOKEN }} 25 | package-path: ./package.json 26 | 27 | - name: get tag version 28 | id: tag_version 29 | uses: jaywcjlove/changelog-generator@main 30 | 31 | - name: Deploy 32 | uses: peaceiris/actions-gh-pages@v3 33 | with: 34 | github_token: ${{ secrets.GITHUB_TOKEN }} 35 | publish_dir: ./typedoc 36 | 37 | - name: Generate Changelog 38 | id: changelog 39 | uses: jaywcjlove/changelog-generator@main 40 | if: steps.create_tag.outputs.successful 41 | with: 42 | token: ${{ secrets.GITHUB_TOKEN }} 43 | head-ref: ${{ steps.create_tag.outputs.version }} 44 | filter-author: (小弟调调™|Renovate Bot) 45 | filter: '[R|r]elease[d]\s+[v|V]\d(\.\d+){0,2}' 46 | 47 | - name: Create Release 48 | uses: ncipollo/release-action@v1 49 | if: steps.create_tag.outputs.successful 50 | with: 51 | token: ${{ secrets.GITHUB_TOKEN }} 52 | name: ${{ steps.create_tag.outputs.version }} 53 | tag: ${{ steps.create_tag.outputs.version }} 54 | body: | 55 | [![](https://img.shields.io/badge/Open%20in-unpkg-blue)](https://uiwjs.github.io/npm-unpkg/#/pkg/@uiw/react-native-amap-geolocation@${{steps.create_tag.outputs.versionNumber}}/file/README.md) 56 | Documentation ${{ steps.changelog.outputs.tag }}: https://raw.githack.com/uiwjs/react-markdown-preview/${{ steps.changelog.outputs.gh-pages-short-hash }}/index.html 57 | Comparing Changes: ${{ steps.changelog.outputs.compareurl }} 58 | ${{ steps.changelog.outputs.changelog }} 59 | ```bash 60 | npm i @uiw/react-native-amap-geolocation@${{steps.create_tag.outputs.versionNumber}} 61 | ``` 62 | - name: package.json info 63 | uses: jaywcjlove/github-action-package@main 64 | with: 65 | path: package.json 66 | unset: scripts,devDependencies 67 | 68 | - run: npm publish 69 | name: 📦 @uiw/react-native-amap-geolocation publish to NPM 70 | continue-on-error: true 71 | env: 72 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # node.js 6 | # 7 | typedoc 8 | node_modules/ 9 | npm-debug.log 10 | yarn-error.log 11 | yarn.lock 12 | 13 | # Xcode 14 | # 15 | build/ 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | xcuserdata 25 | *.xccheckout 26 | *.moved-aside 27 | DerivedData 28 | *.hmap 29 | *.ipa 30 | *.xcuserstate 31 | project.xcworkspace 32 | 33 | # Android/IntelliJ 34 | # 35 | build/ 36 | .idea 37 | .gradle 38 | local.properties 39 | *.iml 40 | 41 | # BUCK 42 | buck-out/ 43 | \.buckd/ 44 | *.keystore 45 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # JS 2 | node_modules 3 | yarn.lock 4 | 5 | # Config files 6 | renovate.json 7 | 8 | # Example 9 | example/ 10 | imgs/ 11 | 12 | # Android 13 | android/*/build/ 14 | android/gradlew 15 | android/build 16 | android/gradlew.bat 17 | android/gradle/ 18 | android/com_crashlytics_export_strings.xml 19 | android/local.properties 20 | android/.gradle/ 21 | android/.signing/ 22 | android/.idea/gradle.xml 23 | android/.idea/libraries/ 24 | android/.idea/workspace.xml 25 | android/.idea/tasks.xml 26 | android/.idea/.name 27 | android/.idea/compiler.xml 28 | android/.idea/copyright/profiles_settings.xml 29 | android/.idea/encodings.xml 30 | android/.idea/misc.xml 31 | android/.idea/modules.xml 32 | android/.idea/scopes/scope_settings.xml 33 | android/.idea/vcs.xml 34 | android/*.iml 35 | android/.settings 36 | 37 | # iOS 38 | ios/*.xcodeproj/xcuserdata 39 | *.pbxuser 40 | *.mode1v3 41 | *.mode2v3 42 | *.perspectivev3 43 | *.xcuserstate 44 | project.xcworkspace/ 45 | xcuserdata/ 46 | ios/build/ 47 | 48 | # Misc 49 | .DS_Store 50 | .DS_Store? 51 | coverage.android.json 52 | coverage.ios.json 53 | npm-debug.log 54 | .github 55 | ._* 56 | .Spotlight-V100 57 | .Trashes 58 | ehthumbs.db 59 | Thumbs.dbandroid/gradle 60 | typedoc 61 | .idea 62 | codorials -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 uiw 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | @uiw/react-native-amap-geolocation 2 | ---- 3 | 4 | [![NPM Version](https://img.shields.io/npm/v/@uiw/react-native-amap-geolocation.svg)](https://npmjs.org/package/@uiw/react-native-amap-geolocation) 5 | ![SDK-6.2.0](https://shields.io/badge/SDK-6.2.0-green?logo=android&style=flat) 6 | ![AMapLocation-2.9.0](https://shields.io/badge/AMapLocation-2.9.0-green?logo=apple&style=flat) 7 | ![David](https://img.shields.io/david/peer/uiwjs/react-native-amap-geolocation) 8 | 9 | React Native 高德地图定位模块,支持 Android/iOS。提供尽可能完善的原生接口,同时提供符合 Web 标准的 Geolocation API 以及 [完整的接口文档](https://uiwjs.github.io/react-native-amap-geolocation/)。 10 | 11 | ## 注意事项 12 | 13 | ⚠️ 高德地图定位部分 API 需要真机调试和 `Access WiFi Information` 权限。 14 | 15 |
16 | Android:需要正确的设置 apiKey,获取 Key 的方法 17 | 18 |
19 | 20 | 官方获取 Key方法:https://lbs.amap.com/api/android-location-sdk/guide/create-project/get-key 21 | 22 | A. 使用 `keytool`(jdk自带工具)获取 `SHA1`,默认 测试版本 `keystore` 路径 `<项目名称>/android/app/debug.keystore` 23 | 24 | ```bash 25 | keytool -v -list -keystore keystore文件路径 26 | ``` 27 | 28 | B. 获取 `PackageName`,获取路径 `<项目名称>/android/app/src/main/AndroidManifest.xml` 29 | 30 | C. 在高德地图账号中设置 `SHA1` 和 `PackageName`。 31 | 32 | 33 | 34 | D. 按照上面步骤正确设置你的 `apiKey` 才会起作用。 35 | 36 |
37 | 38 |
39 | Android:无法获取逆地理信息的问题,KEY鉴权失败 40 | 41 | 1. APK 当前签名文件的 SHA1 与[高德开放平台](https://console.amap.com/)中设置不一致 42 | 2. Android Studio 开发者请注意调整 `build.gradle` 文件的 `applicationId` 与 `packageName` 一致,如不一致将会导致鉴权失败。 43 | 3. 工程中的 `packageName` 与[高德开放平台](https://console.amap.com/)中 `packageName` 设置不一致 44 | 4. 通过 `SDK` 提供的 `setApiKey(String key);` 接口设置 `Key`,注意 `Key` 设置要在 `SDK` 业务初始化之前。 45 | 46 |
47 | 48 |
49 | iOS:获取逆地理信息需要高德地图配置 apiKey 50 | 51 | 52 | 53 |
54 | 55 |
56 | iOS:高德地图包需要 WiFi 权限 57 | 58 |
59 | 60 | iOS 端高德地图包需要 WiFi 权限,否则报如下警告: 61 | 62 | ``` 63 | nehelper sent invalid result code [1] for Wi-Fi information request 64 | ``` 65 | 66 | 需要在[开发者账号中设置 WiFi 权限](https://developer.apple.com) 67 | 68 | 69 | 70 | 同时需要在 Xcode 中添加 `Access WiFi Information` 能力选项 71 | 72 | 73 | 74 |
75 | 76 |
77 | iOS:需要保证"Background Modes"中的"Location updates"处于选中状态 78 | 79 | 80 | 81 | 1. 左侧目录中选中工程名,开启 `TARGETS` -> `Capabilities` -> `Background Modes` 82 | 2. 在 `Background Modes` 中勾选 `Location updates` 83 | 84 |
85 | 86 |
87 | Android:获取 apikey 失败 errorCode : 10001 88 | 89 | ```bash 90 | 原因:***确保调用SDK任何接口前先调用更新隐私合规updatePrivacyShow、updatePrivacyAgree两个接口并且参数值都为true,若未正确设置有崩溃风险*** 91 | 使用loc SDK 功能使用前请确保已经正确设置apiKey,如有疑问请在高德开放平台官网中搜索【INVALID_USER_KEY】相关内容进行解决。 92 | ``` 93 | 94 | 解决方法:重新申请 `apikey` 95 | 96 |
97 | 98 | 99 |
100 | Android:new NativeEventEmitter()` was called with a non-null argument without the required `addListener` method 101 | 102 | ```bash 103 | WARN `new NativeEventEmitter()` was called with a non-null argument without the required `addListener` method. 104 | WARN `new NativeEventEmitter()` was called with a non-null argument without the required `removeListeners` method. 105 | ``` 106 | 107 | 解决方法:重新申请 `apikey` 108 | 109 |
110 | 111 |
112 | Android:请在高德开放平台官网中搜索"SERVICE_NOT_EXIST"相关内容进行解决 113 | 114 | ```bash 115 | 请在高德开放平台官网中搜索"SERVICE_NOT_EXIST"相关内容进行解决 116 | ``` 117 | 118 | 解决方法:[重新申请 `apikey`](https://lbs.amap.com/cooperation/technical_advisory/?tab=4) 119 | 120 | ![](./imgs/SERVICE_NOT_EXIST.png) 121 | 122 |
123 | 124 | ## 安装依赖 125 | 126 | ```bash 127 | yarn add @uiw/react-native-amap-geolocation 128 | # react-native version >= 0.60+ 129 | $ cd ios && pod install 130 | ``` 131 | 132 | ## 基本用法 133 | 134 | ```javascript 135 | import { PermissionsAndroid } from "react-native"; 136 | import { Platform } from 'react-native'; 137 | import AMapGeolocation from '@uiw/react-native-amap-geolocation'; 138 | 139 | await PermissionsAndroid.requestMultiple([ 140 | PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION, 141 | PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION, 142 | ]); 143 | 144 | let apiKey = ''; 145 | 146 | if (Platform.OS === 'ios') { 147 | apiKey = '用于 iOS 的 apiKey'; 148 | } 149 | if (Platform.OS === 'android') { 150 | apiKey = '用于 Android 的 apiKey'; 151 | } 152 | 153 | // 设置 高德地图 apiKey 154 | AMapGeolocation.setApiKey(apiKey); 155 | // iOS 指定所需的精度级别 156 | AMapGeolocation.setDesiredAccuracy(3); 157 | // Android 指定所需的精度级别,可选设置,默认 高精度定位模式 158 | AMapGeolocation.setLocationMode(1); 159 | // 定位是否返回逆地理信息 160 | AMapGeolocation.setLocatingWithReGeocode(true); 161 | // 开始定位 162 | AMapGeolocation.start(); 163 | // 停止更新位置信息 164 | AMapGeolocation.stop(); 165 | // 是否已经开始持续定位了 166 | AMapGeolocation.isStarted(); 167 | // 在某些情况获取不到数据,建议使用 `addLocationListener` 来实现 168 | async function getCurrentLocation(){ 169 | try { 170 | const json = await AMapGeolocation.getCurrentLocation(); 171 | console.log('json:', json); 172 | } catch (error) { 173 | console.log('error:', error); 174 | } 175 | } 176 | ``` 177 | 178 | ## 定位监听函数 179 | 180 | ```js 181 | import AMapGeolocation from '@uiw/react-native-amap-geolocation'; 182 | 183 | let apiKey = ''; 184 | if (Platform.OS === 'ios') { 185 | apiKey = '用于 iOS 的 apiKey'; 186 | } 187 | if (Platform.OS === 'android') { 188 | apiKey = '用于 Android 的 apiKey'; 189 | } 190 | 191 | // 设置 高德地图 apiKey 192 | AMapGeolocation.setApiKey(apiKey); 193 | // 定位是否返回逆地理信息 194 | AMapGeolocation.setLocatingWithReGeocode(true); 195 | 196 | // 必须,启动开始定位,addLocationListener 监听函数才有效 197 | AMapGeolocation.start(); 198 | // 当设备可以正常联网时,还可以返回该定位点的对应的中国境内位置信息(包括:省、市、区/县以及详细地址)。 199 | const listener = AMapGeolocation.addLocationListener((location) => { 200 | console.log('返回定位信息', location); 201 | this.setState({ 202 | location: JSON.stringify(location, null, 2), 203 | }); 204 | }); 205 | // 移除监听事件 206 | listener.remove(); 207 | // 开启监听 208 | AMapGeolocation.start(); 209 | ``` 210 | 211 | ## 逆地理编码 212 | 213 | ```js 214 | import AMapGeolocation from '@uiw/react-native-amap-geolocation'; 215 | 216 | let apiKey = ''; 217 | if (Platform.OS === 'ios') { 218 | apiKey = '用于 iOS 的 apiKey'; 219 | } 220 | if (Platform.OS === 'android') { 221 | apiKey = '用于 Android 的 apiKey'; 222 | } 223 | 224 | // 设置 高德地图 apiKey 225 | AMapGeolocation.setApiKey(apiKey); 226 | // 定位是否返回逆地理信息 227 | AMapGeolocation.setLocatingWithReGeocode(true); 228 | ``` 229 | 230 | ## 坐标转换 231 | 232 | 坐标转换,支持将iOS自带定位 GPS/Google/MapBar/Baidu/MapABC 多种坐标系的坐标转换成高德坐标 233 | 234 | ```js 235 | import AMapGeolocation from '@uiw/react-native-amap-geolocation'; 236 | 237 | // 将百度地图转换为 高德地图 经纬度 238 | const resulte = await AMapGeolocation.coordinateConvert({ 239 | latitude: 40.002172, 240 | longitude: 116.467357, 241 | }, 0); 242 | // => {longitude: 116.46071927031961, latitude: 39.99651501274128} 243 | ``` 244 | 245 | ## 定位回调频率限制 246 | 247 | ```js 248 | import AMapGeolocation from '@uiw/react-native-amap-geolocation'; 249 | 250 | // android,5 秒请求一次定位 251 | AMapGeolocation.setInterval(5000); 252 | 253 | // ios,设备移动超过 10 米才会更新位置信息 254 | AMapGeolocation.setDistanceFilter(10); 255 | ``` 256 | 257 | ## API 258 | 259 | ```ts 260 | /** 一个地理坐标点。 */ 261 | export interface Point { 262 | /** 纬度 */ 263 | latitude: number; 264 | /** 经度 */ 265 | longitude: number; 266 | } 267 | 268 | /** 269 | * 坐标信息 270 | * @see https://developer.mozilla.org/zh-CN/docs/Web/API/Coordinates 271 | */ 272 | export interface Coordinates extends Point { 273 | /** 高度 - 海拔高度,以米为单位。 */ 274 | altitude: number; 275 | /** 水平精度 - 位置的不确定性半径,以米为单位。 */ 276 | accuracy: number; 277 | /** 移动方向,需要 GPS */ 278 | heading: number; 279 | /** 移动速度(米/秒),需要 GPS */ 280 | speed: number; 281 | /** 时间戳记 - 确定此位置的时间。 */ 282 | timestamp: number; 283 | /** 284 | * 是否有可用坐标 285 | * @platform ios 286 | */ 287 | isAvailableCoordinate?: boolean; 288 | } 289 | 290 | /** 291 | * 逆地理信息 + 坐标信息 292 | */ 293 | export interface ReGeocode extends Coordinates { 294 | /** 格式化地址 */ 295 | address: string; 296 | /** 国家 */ 297 | country: string; 298 | /** 省/直辖市,如 `湖北省` */ 299 | province: string; 300 | /** 市,如 `武汉市`。对应城市{@link cityCode}编码 */ 301 | city: string; 302 | /** 区,如 `武昌区`。对应区域{@link adCode}编码 */ 303 | district: string; 304 | 305 | // ///乡镇 306 | // // 该字段从v2.2.0版本起不再返回数据,建议您使用AMapSearchKit的逆地理功能获取. 307 | // township: string; 308 | 309 | // ///社区 310 | // // 该字段从v2.2.0版本起不再返回数据,建议您使用AMapSearchKit的逆地理功能获取. 311 | // neighborhood: string; 312 | 313 | // ///建筑 314 | // // 该字段从v2.2.0版本起不再返回数据,建议您使用AMapSearchKit的逆地理功能获取. 315 | // building: string; 316 | 317 | /** 城市编码 */ 318 | cityCode: string; 319 | /** * 区域编码 */ 320 | adCode: string; 321 | /** 街道名称 */ 322 | street: string; 323 | /** 门牌号 */ 324 | streetNumber: string; 325 | /** 兴趣点名称 */ 326 | poiName: string; 327 | /** 所属兴趣点名称 */ 328 | aoiName: string; 329 | /** 330 | * 获取定位信息描述 331 | * @version SDK2.0.0 开始支持 332 | * @platform android 333 | */ 334 | description?: string; 335 | /** 336 | * 获取坐标系类型 高德定位sdk会返回两种坐标系: 337 | * 坐标系 AMapLocation.COORD_TYPE_GCJ02 -- GCJ02 338 | * 坐标系 AMapLocation.COORD_TYPE_WGS84 -- WGS84 339 | * 国外定位时返回的是WGS84坐标系 340 | * @platform android 341 | */ 342 | coordType?: 'GCJ02' | 'WGS84'; 343 | /** 344 | * 返回支持室内定位的建筑物ID信息 345 | * @platform android 346 | */ 347 | buildingId?: string; 348 | } 349 | /** 350 | * 配置高德地图 Key 351 | * - 352 | * - [高德获取 iOS key 文档地址](https://lbs.amap.com/api/ios-location-sdk/guide/create-project/get-key) 353 | * - [高德获取 Android key 文档地址](https://lbs.amap.com/api/android-location-sdk/guide/create-project/get-key) 354 | * 355 | * 注意:安卓设置 key 很重要,由于在 android 平台必须优先设置 ApiKey 才能初始化 地图实例。 356 | * 所以这个方法在android 平台下,还附带了初始化地图实例。 357 | */ 358 | export function setApiKey(scheme: string): void; 359 | /** 360 | * 开始连续定位 361 | */ 362 | export function start(): void; 363 | /** 364 | * 停止更新位置信息 365 | */ 366 | export function stop(): void; 367 | 368 | /** 369 | * 开始获取设备朝向,如果设备支持方向识别,则会通过代理回调方法-wx 370 | * @platform ios 371 | */ 372 | export function startUpdatingHeading(): void; 373 | 374 | /** 375 | * 停止获取设备朝向-wx 376 | * @platform ios 377 | */ 378 | export function stopUpdatingHeading(): void; 379 | 380 | /** 381 | * 是否已经开始持续定位了 382 | */ 383 | export function isStarted(): Promise; 384 | /** 385 | * 用于指定所需的精度级别。 386 | * 单位米,默认为 kCLLocationAccuracyBest。定位服务会尽可能去获取满足desiredAccuracy的定位结果,但不保证一定会得到满足期望的结果。 387 | * 注意:设置为 kCLLocationAccuracyBest 或 kCLLocationAccuracyBestForNavigation 时, 388 | * 单次定位会在达到 locationTimeout 设定的时间后,将时间内获取到的最高精度的定位结果返回。 389 | * 高德提供了 kCLLocationAccuracyBest 参数,设置该参数可以获取到精度在10m 左右的定位结果,但是相应的需要付出比较长的时间(10s左右), 390 | * 越高的精度需要持续定位时间越长。 391 | * 推荐:kCLLocationAccuracyHundredMeters,一次还不错的定位,偏差在百米左右,超时时间设置在2s-3s左右即可。 392 | * 393 | * @param {number} accuracy 1 394 | * - 0 => kCLLocationAccuracyBestForNavigation 395 | * - 1 => kCLLocationAccuracyBest 396 | * - 2 => kCLLocationAccuracyNearestTenMeters 397 | * - 3 => kCLLocationAccuracyHundredMeters 398 | * - 4 => kCLLocationAccuracyKilometer 399 | * - 5 => kCLLocationAccuracyThreeKilometers 400 | * @platform ios 401 | */ 402 | export function setDesiredAccuracy(accuracy: 0 | 1 | 2 | 3 | 4 | 5): void; 403 | /** 404 | * 坐标转换,支持将iOS自带定位 GPS/Google/MapBar/Baidu/MapABC 多种坐标系的坐标转换成高德坐标 405 | * 406 | * - -1 -> `AMapCoordinateTypeAMap` // `AMapCoordinateTypeBaidu` // `AMapCoordinateTypeMapBar` // `AMapCoordinateTypeMapABC` // `AMapCoordinateTypeSoSoMap` // `AMapCoordinateTypeAliYun` // `AMapCoordinateTypeGoogle` // `AMapCoordinateTypeGPS` // ; 419 | /** 420 | * 设置发起定位请求的时间间隔,单位:毫秒,默认值:2000毫秒 421 | * @platform android 422 | * @default 2000 423 | */ 424 | export function setInterval(interval: number): void; 425 | /** 426 | * 指定定位是否会被系统自动暂停。默认为 false 427 | * @platform ios 428 | * @param value false 429 | */ 430 | export function setPausesLocationUpdatesAutomatically(value: boolean): void; 431 | /** 432 | * 是否允许后台定位。默认为NO。只在iOS 9.0及之后起作用。 433 | * 设置为YES的时候必须保证 Background Modes 中的 Location updates 处于选中状态,否则会抛出异常。 434 | * @platform ios 435 | * @param value false 436 | */ 437 | export function setAllowsBackgroundLocationUpdates(value: boolean): void; 438 | /** 439 | * 设定定位的最小更新距离。单位米,默认,表示只要检测到设备位置发生变化就会更新位置信息。 440 | * @platform ios 441 | */ 442 | export function setDistanceFilter(time: number): void; 443 | /** 444 | * 定位超时时间,最低2s 445 | * @platform ios 446 | */ 447 | export function setLocationTimeout(number: number): void; 448 | /** 449 | * 逆地理请求超时时间,最低 2s,默认为2s 注意在单次定位请求前设置。 450 | * @platform ios 451 | */ 452 | export function setReGeocodeTimeout(number: number): void; 453 | /** 454 | * 获取当前定位 455 | * 默认只获取经纬度,`iOS` 通过 {@linkcode setLocatingWithReGeocode} 设置,是否返回逆地理信息 456 | */ 457 | export function getCurrentLocation(): Promise; 458 | /** 459 | * 定位是否返回逆地理信息,为了与 android 保持一致,默认 值为 true。 460 | * @platform ios 默认值:false, 返回地址信息,需要手动设置 461 | * @platform android 默认值:true, 返回地址信息 462 | * @default true 463 | */ 464 | export function setLocatingWithReGeocode(isReGeocode: boolean): void; 465 | /** 466 | * 设置定位模式。 467 | * 默认值:`Hight_Accuracy` 高精度模式 468 | * android 默认定位模式,目前支持三种定位模式 469 | * - 1 => `Hight_Accuracy` 高精度定位模式:在这种定位模式下,将同时使用高德网络定位和卫星定位,优先返回精度高的定位 470 | * - 2 => `Battery_Saving` 低功耗定位模式:在这种模式下,将只使用高德网络定位 471 | * - 3 => `Device_Sensors` 仅设备定位模式:在这种模式下,将只使用卫星定位。 472 | * @param {number} mode `1~3` 473 | * @platform android 474 | * @default 1 475 | */ 476 | export function setLocationMode(mode: 1 | 2 | 3): void; 477 | /** 478 | * 设置是否单次定位 479 | * @default false 480 | * @platform android 481 | */ 482 | export function setOnceLocation(isOnceLocation: boolean): void; 483 | /** 484 | * 设置是否使用设备传感器。是否开启设备传感器,当设置为true时,网络定位可以返回海拔、角度和速度。 485 | * @default false 486 | * @platform android 487 | */ 488 | export function setSensorEnable(sensorEnable: boolean): void; 489 | /** 490 | * 设置是否允许调用 WIFI 刷新。 491 | * 默认值为true,当设置为false时会停止主动调用WIFI刷新,将会极大程度影响定位精度,但可以有效的降低定位耗电 492 | * @platform android 493 | * @default true 494 | */ 495 | export function setWifiScan(isOnceLocation: boolean): void; 496 | /** 497 | * 设置逆地理信息的语言,目前之中中文和英文。 498 | * @default DEFAULT 499 | */ 500 | export function setGeoLanguage(language: 'DEFAULT' | 'EN' | 'ZH'): void; 501 | /** 502 | * 连续定位监听事件 503 | * @param {Function} listener 504 | */ 505 | export function addLocationListener(listener?: (location: Coordinates | ReGeocode) => void): void; 506 | 507 | /** 508 | * 设置是否gps优先-wx 509 | * 只有在单次定位高精度定位模式下有效 510 | * 设置为true时,会等待卫星定位结果返回,最多等待30秒,若30秒后仍无卫星定位结果返回,返回网络定位结果 511 | * @default false 512 | * @platform android 513 | */ 514 | export function setGpsFirst(isSetGpsFirst: boolean): void; 515 | 516 | /** 517 | * 设置定位是否等待WIFI列表刷新-wx 518 | * 定位精度会更高,但是定位速度会变慢1-3秒 519 | * 从3.7.0版本开始,支持连续定位(连续定位时首次会等待刷新) 3.7.0之前的版本,仅适用于单次定位,当设置为true时,连续定位会自动变为单次定位, 520 | * @default false 521 | * @platform android 522 | */ 523 | export function setOnceLocationLatest(isOnceLocationLatest: boolean): void; 524 | 525 | /** 526 | * 设置是否使用缓存策略, 默认为true 使用缓存策略 527 | * @default true 528 | * @platform android 529 | */ 530 | export function setLocationCacheEnable(isLocationCacheEnable: boolean): void; 531 | 532 | /** 533 | * 设置网络请求超时时间。默认为30秒。在仅设备模式下无效 534 | * @default 30000 535 | * @platform android 536 | */ 537 | export function setHttpTimeOut(httpTimeOut: number): void; 538 | 539 | /** 540 | * 设置网络请求的协议。默认为HTTP协议。可选HTTP或者HTTPS 541 | * @default HTTP 542 | * @platform android 543 | */ 544 | export function setLocationProtocol(amapLocationProtocol: 'HTTP' | 'HTTPS'): void; 545 | ``` 546 | 547 | ## 错误处理 548 | 549 | ```bash 550 | [NetworkInfo] Signal strength query returned error: Error Domain=NSPOSIXErrorDomain Code=13 "Permission denied", descriptor: 551 | ``` 552 | 553 | 在 `Product` -> `Scheme` -> `Edit Scheme` -> `Run` -> `Arguments` -> `Environment Variables` 添加 `OS_ACTIVITY_MODE` `disable` 554 | 555 | ```bash 556 | nehelper sent invalid result code [1] for Wi-Fi information request 557 | ``` 558 | 559 | 配置 WiFi 权限 560 | 561 | ## 其它 562 | 563 | 当前工程基于 [@brodybits/create-react-native-module](https://github.com/brodybits/create-react-native-module) 初始化。 564 | 565 | ```bash 566 | npx create-react-native-module --package-identifier com.uiwjs.example.geolocation --object-class-name RNAMapGeolocation --generate-example AMapGeolocation --example-react-native-version 0.63.0 --module-name @uiw/react-native-amap-geolocation --github-account uiwjs --author-name "Kenny Wong" --author-email "wowohoo@qq.com" 567 | ``` 568 | 569 | ## 开发 570 | 571 | ```bash 572 | cd example # 进入实例 example 工程,根目录不需要安装,会引发错误 573 | yarn install # 安装依赖 574 | 575 | cd ios # 进入 example/ios 目录安装依赖 576 | pod instll # 安装依赖 577 | ``` 578 | 579 | ## 相关连接 580 | 581 | - [高德地图:Android 端类文档](http://amappc.cn-hangzhou.oss-pub.aliyun-inc.com/lbs/static/unzip/Android_Location_Doc/index.html) 582 | - [高德地图:iOS定位SDK V2.6.5](https://lbs.amap.com/api/ios-location-sdk/download/) 583 | - [高德地图:Android 定位SDK V5.1.0](https://lbs.amap.com/api/android-location-sdk/download) 584 | - [高德地图:提交 AppStore 必读](https://lbs.amap.com/api/ios-location-sdk/guide/create-project/idfa-guide) 585 | -------------------------------------------------------------------------------- /android/README.md: -------------------------------------------------------------------------------- 1 | README 2 | ====== 3 | 4 | If you want to publish the lib as a maven dependency, follow these steps before publishing a new version to npm: 5 | 6 | 1. Be sure to have the Android [SDK](https://developer.android.com/studio/index.html) and [NDK](https://developer.android.com/ndk/guides/index.html) installed 7 | 2. Be sure to have a `local.properties` file in this folder that points to the Android SDK and NDK 8 | ``` 9 | ndk.dir=/Users/{username}/Library/Android/sdk/ndk-bundle 10 | sdk.dir=/Users/{username}/Library/Android/sdk 11 | ``` 12 | 3. Delete the `maven` folder 13 | 4. Run `./gradlew installArchives` 14 | 5. Verify that latest set of generated files is in the maven folder with the correct version number 15 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.compileSdkVersion 5 | 6 | defaultConfig { 7 | minSdkVersion rootProject.ext.minSdkVersion 8 | targetSdkVersion rootProject.ext.targetSdkVersion 9 | } 10 | } 11 | 12 | dependencies { 13 | compileOnly 'com.facebook.react:react-native:+' 14 | implementation 'com.amap.api:location:6.2.0' 15 | } -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /android/src/main/java/com/uiwjs/react/geolocation/RNAMapGeolocationModule.java: -------------------------------------------------------------------------------- 1 | package com.uiwjs.react.geolocation; 2 | 3 | import com.amap.api.location.AMapLocation; 4 | import com.amap.api.location.AMapLocationClient; 5 | import com.amap.api.location.AMapLocationClientOption; 6 | import com.amap.api.location.AMapLocationClientOption.AMapLocationMode; 7 | import com.amap.api.location.AMapLocationClientOption.AMapLocationProtocol; 8 | import com.amap.api.location.AMapLocationListener; 9 | import com.amap.api.location.AMapLocationQualityReport; 10 | import com.amap.api.location.CoordinateConverter; 11 | import com.amap.api.location.CoordinateConverter.CoordType; 12 | import com.amap.api.location.DPoint; 13 | import com.facebook.react.bridge.Callback; 14 | import com.facebook.react.bridge.ReactApplicationContext; 15 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 16 | import com.facebook.react.bridge.ReactMethod; 17 | import com.facebook.react.bridge.WritableMap; 18 | import com.facebook.react.bridge.Arguments; 19 | import com.facebook.react.bridge.Promise; 20 | import com.facebook.react.modules.core.DeviceEventManagerModule; 21 | import com.facebook.react.bridge.ReadableMap; 22 | 23 | public class RNAMapGeolocationModule extends ReactContextBaseJavaModule { 24 | 25 | private final ReactApplicationContext reactContext; 26 | private DeviceEventManagerModule.RCTDeviceEventEmitter eventEmitter; 27 | // 声明AMapLocationClient类对象 28 | private AMapLocationClient client; 29 | // 初始化 AMapLocationClientOption 对象 30 | private AMapLocationClientOption option = new AMapLocationClientOption(); 31 | private AMapLocation mLastAMapLocation; 32 | 33 | public RNAMapGeolocationModule(ReactApplicationContext reactContext) { 34 | super(reactContext); 35 | this.reactContext = reactContext; 36 | } 37 | 38 | @ReactMethod 39 | public void setApiKey(String key) throws Exception { 40 | if (client != null) { 41 | // client = null; 42 | client.onDestroy(); 43 | } 44 | // 通过SDK提供的 `setApiKey(String key);` 接口设置Key,注意Key设置要在SDK业务初始化之前。 45 | // 需要在初始化的额前面设置 key 46 | AMapLocationClient.setApiKey(key); 47 | /** 48 | * 设置包含隐私政策,并展示用户授权弹窗 `必须在 AmapLocationClient 实例化之前调用` 49 | * @param context 50 | * @param isContains: 是隐私权政策是否包含高德开平隐私权政策 true 是包含 51 | * @param isShow: 隐私权政策是否弹窗展示告知用户 true 是展示 52 | * @since 5.6.0 53 | */ 54 | AMapLocationClient.updatePrivacyShow(this.reactContext, true, true); 55 | /** 56 | * 设置是否同意用户授权政策 `必须在 AmapLocationClient 实例化之前调用 57 | * https://lbs.amap.com/api/android-location-sdk/guide/create-project/dev-attention#t1 58 | * @param context 59 | * @param isAgree:隐私权政策是否取得用户同意 true 是用户同意 60 | * 61 | * @since 5.6.0 62 | */ 63 | AMapLocationClient.updatePrivacyAgree(this.reactContext, true); 64 | // 初始化定位 65 | client = new AMapLocationClient(this.reactContext.getApplicationContext()); 66 | //设置定位参数 67 | client.setLocationOption(option); 68 | // 设置定位监听 69 | client.setLocationListener(locationListener); 70 | // 用于在 React Native 中获取 DeviceEventManagerModule.RCTDeviceEventEmitter 类的 JS Module 对象, 71 | // 以便在 JS 端发送和侦听基于设备事件的消息 72 | eventEmitter = reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class); 73 | } 74 | 75 | @Override 76 | public String getName() { 77 | return "RNAMapGeolocation"; 78 | } 79 | /** 80 | * 定位监听 81 | */ 82 | AMapLocationListener locationListener = new AMapLocationListener() { 83 | @Override 84 | public void onLocationChanged(AMapLocation location) { 85 | System.out.println("client.onLocationChanged()!~!!!!!!"); 86 | mLastAMapLocation = location; 87 | getDeviceEventEmitter().emit("AMapGeolocation", toJSON(location)); 88 | } 89 | }; 90 | 91 | @ReactMethod 92 | public void removeListeners(Integer count) { 93 | // client.stopLocation(); 94 | } 95 | 96 | @ReactMethod 97 | public void getCurrentLocation(final Promise promise) { 98 | try { 99 | if (client == null) { 100 | promise.reject("-1", "尚未调用 setApiKey() 进行初始化"); 101 | return; 102 | } 103 | if (!client.isStarted()) { 104 | client.startLocation(); 105 | } 106 | // System.out.println(mLastAMapLocation); 107 | promise.resolve(toJSON(mLastAMapLocation)); 108 | client.stopLocation(); 109 | } catch (Exception e) { 110 | promise.reject("-110", e.getMessage()); 111 | } 112 | } 113 | 114 | @ReactMethod 115 | public void getLastKnownLocation(Promise promise) { 116 | if (client == null) { 117 | promise.reject("-1", "尚未调用 setApiKey() 进行初始化"); 118 | return; 119 | } 120 | AMapLocation location = client.getLastKnownLocation(); 121 | promise.resolve(toJSON(location)); 122 | } 123 | 124 | @ReactMethod 125 | public void start() { 126 | if (client == null) { 127 | return; 128 | } 129 | client.startLocation(); 130 | } 131 | 132 | @ReactMethod 133 | public void stop() { 134 | if (client == null) { 135 | return; 136 | } 137 | client.stopLocation(); 138 | } 139 | @ReactMethod 140 | public void isStarted(Promise promise) { 141 | if (client == null) { 142 | promise.reject("-1", "尚未调用 setApiKey() 进行初始化"); 143 | return; 144 | } 145 | promise.resolve(client.isStarted()); 146 | } 147 | /** 148 | * 默认的定位参数 149 | */ 150 | // private AMapLocationClientOption getDefaultOption(){ 151 | // AMapLocationClientOption mOption = new AMapLocationClientOption(); 152 | // mOption.setLocationMode(AMapLocationMode.Hight_Accuracy);//可选,设置定位模式,可选的模式有高精度、仅设备、仅网络。默认为高精度模式 153 | // mOption.setGpsFirst(false);//可选,设置是否gps优先,只在高精度模式下有效。默认关闭 154 | // mOption.setHttpTimeOut(30000);//可选,设置网络请求超时时间。默认为30秒。在仅设备模式下无效 155 | // mOption.setInterval(2000); //可选,设置定位间隔。默认为2秒 156 | // mOption.setNeedAddress(true);//可选,设置是否返回逆地理地址信息。默认是true 157 | // mOption.setOnceLocation(false);//可选,设置是否单次定位。默认是false 158 | // mOption.setOnceLocationLatest(false);//可选,设置是否等待wifi刷新,默认为false.如果设置为true,会自动变为单次定位,持续定位时不要使用 159 | // AMapLocationClientOption.setLocationProtocol(AMapLocationProtocol.HTTP);//可选, 设置网络请求的协议。可选HTTP或者HTTPS。默认为HTTP 160 | // mOption.setSensorEnable(false);//可选,设置是否使用传感器。默认是false 161 | // mOption.setWifiScan(true); //可选,设置是否开启wifi扫描。默认为true,如果设置为false会同时停止主动刷新,停止以后完全依赖于系统刷新,定位位置可能存在误差 162 | // mOption.setLocationCacheEnable(true); //可选,设置是否使用缓存定位,默认为true 163 | // mOption.setGeoLanguage(AMapLocationClientOption.GeoLanguage.DEFAULT);//可选,设置逆地理信息的语言,默认值为默认语言(根据所在地区选择语言) 164 | // return mOption; 165 | // } 166 | 167 | /** 168 | * 设置网络请求的协议, 默认值:HTTP 169 | * @param amapLocationProtocol 可选协议类型:HTTP, HTTPS -wx 170 | */ 171 | public void setLocationProtocol(AMapLocationClientOption.AMapLocationProtocol 172 | amapLocationProtocol) { 173 | if (client == null) { 174 | return; 175 | } 176 | option.setLocationProtocol(amapLocationProtocol); 177 | client.setLocationOption(option); 178 | } 179 | 180 | /** 181 | * 设置网络请求超时时间,默认:30秒-wx 182 | */ 183 | @ReactMethod 184 | public void setHttpTimeOut(int httpTimeOut) { 185 | if (client == null) { 186 | return; 187 | } 188 | option.setHttpTimeOut(httpTimeOut); 189 | client.setLocationOption(option); 190 | } 191 | /** 192 | * 设置是否使用缓存策略-wx 193 | */ 194 | @ReactMethod 195 | public void setLocationCacheEnable(boolean isLocationCacheEnable) { 196 | if (client == null) { 197 | return; 198 | } 199 | option.setLocationCacheEnable(isLocationCacheEnable); 200 | client.setLocationOption(option); 201 | } 202 | /** 203 | * 设置是否等待wifi刷新-wx 204 | */ 205 | @ReactMethod 206 | public void setOnceLocationLatest(boolean isOnceLocationLatest) { 207 | if (client == null) { 208 | return; 209 | } 210 | option.setOnceLocationLatest(isOnceLocationLatest); 211 | client.setLocationOption(option); 212 | } 213 | /** 214 | * 设置是否gps优先-wx 215 | */ 216 | @ReactMethod 217 | public void setGpsFirst(boolean isSetGpsFirst) { 218 | if (client == null) { 219 | return; 220 | } 221 | option.setGpsFirst(isSetGpsFirst); 222 | client.setLocationOption(option); 223 | } 224 | /** 225 | * 设置发起定位请求的时间间隔,单位:毫秒,默认值:2000毫秒 226 | */ 227 | @ReactMethod 228 | public void setInterval(int interval) { 229 | if (client == null) { 230 | return; 231 | } 232 | option.setInterval(interval); 233 | client.setLocationOption(option); 234 | } 235 | 236 | /** 237 | * 设置是否单次定位 238 | */ 239 | @ReactMethod 240 | public void setOnceLocation(boolean isOnceLocation) { 241 | if (client == null) { 242 | return; 243 | } 244 | option.setOnceLocation(isOnceLocation); 245 | client.setLocationOption(option); 246 | } 247 | /** 248 | * 设置逆地理信息的语言,目前之中中文和英文 249 | */ 250 | @ReactMethod 251 | public void setGeoLanguage(String language) { 252 | if (client == null) { 253 | return; 254 | } 255 | option.setGeoLanguage(AMapLocationClientOption.GeoLanguage.valueOf(language)); 256 | client.setLocationOption(option); 257 | } 258 | 259 | /** 260 | * 设置定位模式。默认值:Hight_Accuracy 高精度模式 261 | * android 默认定位模式,目前支持三种定位模式 262 | * - `Hight_Accuracy` 高精度定位模式:在这种定位模式下,将同时使用高德网络定位和卫星定位,优先返回精度高的定位 263 | * - `Battery_Saving` 低功耗定位模式:在这种模式下,将只使用高德网络定位 264 | * - `Device_Sensors` 仅设备定位模式:在这种模式下,将只使用卫星定位。 265 | */ 266 | @ReactMethod 267 | public void setLocationMode(String mode) { 268 | if (client == null) { 269 | return; 270 | } 271 | option.setLocationMode(AMapLocationClientOption.AMapLocationMode.valueOf(mode)); 272 | client.setLocationOption(option); 273 | } 274 | /** 275 | * 设置是否返回地址信息,默认返回地址信息 276 | * 默认值:true, 返回地址信息 277 | */ 278 | @ReactMethod 279 | public void setNeedAddress(boolean value) { 280 | if (client == null) { 281 | return; 282 | } 283 | option.setNeedAddress(value); 284 | client.setLocationOption(option); 285 | } 286 | @ReactMethod 287 | public void coordinateConvert(ReadableMap point, int typer, final Promise promise) { 288 | if (client == null) { 289 | return; 290 | } 291 | try { 292 | // { latitude: 40.002172, longitude: 116.467357 } 293 | // 构造一个示例坐标,第一个参数是纬度,第二个参数是经度 294 | DPoint pointer = new DPoint(point.getDouble("latitude"), point.getDouble("longitude")); 295 | // DPoint pointer = new DPoint(39.911127, 116.433608); 296 | // 初始化坐标转换类 297 | CoordinateConverter converter = new CoordinateConverter(reactContext.getApplicationContext()); 298 | /** 299 | * 设置坐标来源,这里使用百度坐标作为示例 300 | * 可选的来源包括: 301 | * - CoordType.BAIDU: 百度坐标 302 | * - CoordType.MAPBAR: 图吧坐标 303 | * - CoordType.MAPABC: 图盟坐标 304 | * - CoordType.SOSOMAP: 搜搜坐标 305 | * - CoordType.ALIYUN: 阿里云坐标 306 | * - CoordType.GOOGLE: 谷歌坐标 307 | * - CoordType.GPS: GPS坐标 308 | */ 309 | switch (typer) { 310 | case 0: 311 | converter.from(CoordType.BAIDU); 312 | break; 313 | case 1: 314 | converter.from(CoordType.MAPBAR); 315 | break; 316 | case 2: 317 | converter.from(CoordType.MAPABC); 318 | break; 319 | case 3: 320 | converter.from(CoordType.SOSOMAP); 321 | break; 322 | case 4: 323 | converter.from(CoordType.ALIYUN); 324 | break; 325 | case 5: 326 | converter.from(CoordType.GOOGLE); 327 | break; 328 | case 6: 329 | converter.from(CoordType.GPS); 330 | break; 331 | default: break; 332 | } 333 | converter.coord(pointer); 334 | // 转换成高德坐标 335 | DPoint destPoint = converter.convert(); 336 | WritableMap map = Arguments.createMap(); 337 | map.putDouble("latitude", destPoint.getLatitude()); 338 | map.putDouble("longitude", destPoint.getLongitude()); 339 | promise.resolve(map); 340 | } catch (Exception e) { 341 | promise.reject("-11", e.getMessage()); 342 | } 343 | } 344 | 345 | /** 346 | * 设置是否允许调用WIFI刷新 默认值为true,当设置为false时会停止主动调用WIFI刷新,将会极大程度影响定位精度,但可以有效的降低定位耗电 347 | * 默认值:true, 返回地址信息 348 | */ 349 | @ReactMethod 350 | public void setWifiScan(boolean isWifiPassiveScan) { 351 | if (client == null) { 352 | return; 353 | } 354 | option.setWifiScan(isWifiPassiveScan); 355 | client.setLocationOption(option); 356 | } 357 | 358 | /** 359 | * 设置是否使用设备传感器。是否开启设备传感器,当设置为true时,网络定位可以返回海拔、角度和速度。 360 | * [高德地图 setSensorEnable 文档](http://amappc.cn-hangzhou.oss-pub.aliyun-inc.com/lbs/static/unzip/Android_Location_Doc/index.html) 361 | * @default false 362 | * @platform android 363 | */ 364 | @ReactMethod 365 | public void setSensorEnable(boolean sensorEnable) { 366 | if (client == null) { 367 | return; 368 | } 369 | option.setSensorEnable(sensorEnable); 370 | client.setLocationOption(option); 371 | } 372 | 373 | private DeviceEventManagerModule.RCTDeviceEventEmitter getDeviceEventEmitter() { 374 | if (eventEmitter == null) { 375 | eventEmitter = reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class); 376 | } 377 | return eventEmitter; 378 | }; 379 | private WritableMap toJSON(AMapLocation location) { 380 | if (location == null) { 381 | return null; 382 | } 383 | WritableMap map = Arguments.createMap(); 384 | map.putInt("errorCode", location.getErrorCode()); 385 | map.putString("errorInfo", location.getErrorInfo()); 386 | if (location.getErrorCode() == AMapLocation.LOCATION_SUCCESS) { 387 | map.putDouble("accuracy", location.getAccuracy()); 388 | map.putDouble("latitude", location.getLatitude()); 389 | map.putDouble("longitude", location.getLongitude()); 390 | map.putDouble("altitude", location.getAltitude()); 391 | map.putDouble("speed", location.getSpeed()); 392 | // 获取方向角(单位:度) 393 | map.putDouble("heading", location.getBearing()); 394 | map.putDouble("timestamp", location.getTime()); 395 | if (!location.getAddress().isEmpty()) { 396 | map.putString("address", location.getAddress()); 397 | map.putString("country", location.getCountry()); 398 | map.putString("province", location.getProvince()); 399 | map.putString("city", location.getCity()); 400 | map.putString("district", location.getDistrict()); 401 | map.putString("cityCode", location.getCityCode()); 402 | map.putString("adCode", location.getAdCode()); 403 | map.putString("street", location.getStreet()); 404 | map.putString("streetNumber", location.getStreetNum()); 405 | map.putString("poiName", location.getPoiName()); 406 | map.putString("aoiName", location.getAoiName()); 407 | } 408 | // -------------------- 409 | // 以上与 iOS 相同字段 410 | 411 | 412 | // 获取坐标系类型 高德定位sdk会返回两种坐标系: 413 | // 坐标系 AMapLocation.COORD_TYPE_GCJ02 -- GCJ02 414 | // 坐标系 AMapLocation.COORD_TYPE_WGS84 -- WGS84 415 | // 国外定位时返回的是WGS84坐标系 416 | map.putString("coordType", location.getCoordType()); 417 | // 获取位置语义信息 418 | map.putString("description", location.getDescription()); 419 | 420 | // 返回支持室内定位的建筑物ID信息 421 | map.putString("buildingId", location.getBuildingId()); 422 | // 室内外置信度 423 | // 室内:且置信度取值在[1 ~ 100],值越大在在室内的可能性越大 424 | // 室外:且置信度取值在[-100 ~ -1] ,值越小在在室内的可能性越大 425 | // 无法识别室内外:置信度返回值为 0 426 | map.putInt("conScenario", location.getConScenario()); 427 | // 获取室内定位的楼层信息 428 | map.putString("floor", location.getFloor()); 429 | // 获取卫星信号强度,仅在卫星定位时有效,值为: 430 | // #GPS_ACCURACY_BAD,#GPS_ACCURACY_GOOD,#GPS_ACCURACY_UNKNOWN 431 | map.putInt("gpsAccuracyStatus", location.getGpsAccuracyStatus()); 432 | // 获取定位信息描述 433 | map.putString("locationDetail", location.getLocationDetail()); 434 | 435 | map.putInt("locationType", location.getLocationType()); 436 | map.putString("provider", location.getProvider()); 437 | map.putInt("satellites", location.getSatellites()); 438 | map.putInt("trustedLevel", location.getTrustedLevel()); 439 | 440 | 441 | WritableMap qualityReportMap = Arguments.createMap(); 442 | // 获取定位质量 443 | AMapLocationQualityReport aMapLocationQualityReport = location.getLocationQualityReport(); 444 | String adviseMessage = aMapLocationQualityReport.getAdviseMessage(); 445 | if (adviseMessage != null) { 446 | adviseMessage = adviseMessage.replaceAll("[\\t\\n\\r/]", ""); 447 | } 448 | qualityReportMap.putString("adviseMessage", adviseMessage); 449 | qualityReportMap.putInt("gpsSatellites", aMapLocationQualityReport.getGPSSatellites()); 450 | qualityReportMap.putInt("gpsStatus", aMapLocationQualityReport.getGPSStatus()); 451 | qualityReportMap.putDouble("netUseTime", aMapLocationQualityReport.getNetUseTime()); 452 | qualityReportMap.putString("networkType", aMapLocationQualityReport.getNetworkType()); 453 | qualityReportMap.putBoolean("isInstalledHighDangerMockApp", aMapLocationQualityReport.isInstalledHighDangerMockApp()); 454 | qualityReportMap.putBoolean("isWifiAble", aMapLocationQualityReport.isWifiAble()); 455 | map.putMap("locationQualityReport", qualityReportMap); 456 | 457 | } 458 | return map; 459 | } 460 | } 461 | -------------------------------------------------------------------------------- /android/src/main/java/com/uiwjs/react/geolocation/RNAMapGeolocationPackage.java: -------------------------------------------------------------------------------- 1 | package com.uiwjs.react.geolocation; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import com.facebook.react.ReactPackage; 8 | import com.facebook.react.bridge.NativeModule; 9 | import com.facebook.react.bridge.ReactApplicationContext; 10 | import com.facebook.react.uimanager.ViewManager; 11 | import com.facebook.react.bridge.JavaScriptModule; 12 | 13 | public class RNAMapGeolocationPackage implements ReactPackage { 14 | @Override 15 | public List createNativeModules(ReactApplicationContext reactContext) { 16 | return Arrays.asList(new RNAMapGeolocationModule(reactContext)); 17 | } 18 | 19 | @Override 20 | public List createViewManagers(ReactApplicationContext reactContext) { 21 | return Collections.emptyList(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: '@react-native-community', 4 | parserOptions: { 5 | requireConfigFile: false, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | ios/.xcode.env.local 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | *.hprof 33 | .cxx/ 34 | *.keystore 35 | !debug.keystore 36 | 37 | # node.js 38 | # 39 | node_modules/ 40 | npm-debug.log 41 | yarn-error.log 42 | 43 | # fastlane 44 | # 45 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 46 | # screenshots whenever they are needed. 47 | # For more information about the recommended setup visit: 48 | # https://docs.fastlane.tools/best-practices/source-control/ 49 | 50 | **/fastlane/report.xml 51 | **/fastlane/Preview.html 52 | **/fastlane/screenshots 53 | **/fastlane/test_output 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # Ruby / CocoaPods 59 | /ios/Pods/ 60 | /vendor/bundle/ 61 | 62 | # Temporary files created by Metro to check the health of the file watcher 63 | .metro-health-check* 64 | -------------------------------------------------------------------------------- /example/.node-version: -------------------------------------------------------------------------------- 1 | 18 2 | -------------------------------------------------------------------------------- /example/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | arrowParens: 'avoid', 3 | bracketSameLine: true, 4 | bracketSpacing: false, 5 | singleQuote: true, 6 | trailingComma: 'all', 7 | }; 8 | -------------------------------------------------------------------------------- /example/.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.6 2 | -------------------------------------------------------------------------------- /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/App.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | */ 7 | 8 | import React, {useEffect, useState} from 'react'; 9 | import { 10 | SafeAreaView, 11 | ScrollView, 12 | StatusBar, 13 | StyleSheet, 14 | Text, 15 | useColorScheme, 16 | View, 17 | Platform, 18 | Button, 19 | } from 'react-native'; 20 | import AMapGeolocation from '@uiw/react-native-amap-geolocation'; 21 | import {Colors} from 'react-native/Libraries/NewAppScreen'; 22 | 23 | function App(): JSX.Element { 24 | const isDarkMode = useColorScheme() === 'dark'; 25 | const backgroundStyle = { 26 | backgroundColor: isDarkMode ? Colors.darker : Colors.lighter, 27 | }; 28 | const [data, setData] = useState({ 29 | location: '', 30 | isListener: false, 31 | isStarted: false, 32 | isGps: false, 33 | isLocationCacheEnable: true, 34 | }); 35 | useEffect(() => { 36 | let apiKey = ''; 37 | if (Platform.OS === 'ios') { 38 | apiKey = '00b74444d56a1f9e036b608a52f0da33'; 39 | } 40 | if (Platform.OS === 'android') { 41 | apiKey = '5084df66535c2663b89c60b11661b212'; 42 | } 43 | if (apiKey) { 44 | try { 45 | AMapGeolocation.setApiKey(apiKey); 46 | } catch (error) { 47 | console.log('error:', error); 48 | } 49 | } 50 | // iOS 指定所需的精度级别 51 | AMapGeolocation.setDesiredAccuracy(3); 52 | // Android 指定所需的精度级别,可选设置,默认 高精度定位模式 53 | AMapGeolocation.setLocationMode(1); 54 | // 定位是否返回逆地理信息 55 | AMapGeolocation.setLocatingWithReGeocode(true); 56 | // 当设备可以正常联网时,还可以返回该定位点的对应的中国境内位置信息(包括:省、市、区/县以及详细地址)。 57 | AMapGeolocation.addLocationListener(location => { 58 | console.log('返回定位信息', location); 59 | setData({...data, location: JSON.stringify(location, null, 2)}); 60 | }); 61 | // 开启监听 62 | AMapGeolocation.start(); 63 | console.log( 64 | 'AMapGeolocation.addLocationListener', 65 | AMapGeolocation.startUpdatingHeading, 66 | ); 67 | // eslint-disable-next-line react-hooks/exhaustive-deps 68 | }, []); 69 | 70 | const getLocationState = async () => { 71 | const isStarted = await AMapGeolocation.isStarted(); 72 | if (isStarted) { 73 | setData({...data, isStarted}); 74 | } 75 | }; 76 | const coordinateConvert = async () => { 77 | try { 78 | // 将百度地图转换为 高德地图 经纬度 79 | const result = await AMapGeolocation.coordinateConvert( 80 | { 81 | latitude: 40.002172, 82 | longitude: 116.467357, 83 | }, 84 | 0, 85 | ); 86 | console.log('~coordinateConvert~~', result); 87 | } catch (error) { 88 | console.log('~coordinateConvert:error~~', error); 89 | } 90 | }; 91 | const getCurrentLocation = async () => { 92 | try { 93 | console.log('json:-getCurrentLocation-2->>> 获取当前定位信息'); 94 | AMapGeolocation.start(); 95 | console.log('json:-getCurrentLocation-->>> 获取当前定位信息'); 96 | const json = await AMapGeolocation.getCurrentLocation(); 97 | console.log('json:-json-->>>', json); 98 | } catch (error) { 99 | console.log('json:-error-->>>', error); 100 | if (error instanceof Error) { 101 | console.log('json:-error-->>>', error.message); 102 | } 103 | } 104 | }; 105 | const locationListener = () => { 106 | setData({...data, isListener: !data.isListener}); 107 | }; 108 | useEffect(() => { 109 | if (data.isListener) { 110 | AMapGeolocation.start(); 111 | } else { 112 | AMapGeolocation.stop(); 113 | } 114 | }, [data.isListener]); 115 | /** Android 是否开启 gps 优先 */ 116 | const gpsFirst = () => { 117 | if (Platform.OS === 'android') { 118 | setData({...data, isGps: !data.isGps}); 119 | } 120 | }; 121 | useEffect(() => AMapGeolocation.setGpsFirst(data.isGps), [data.isGps]); 122 | /** 开启缓存定位 */ 123 | const setLocationCache = () => { 124 | if (Platform.OS === 'android') { 125 | setData({...data, isLocationCacheEnable: !data.isLocationCacheEnable}); 126 | } 127 | }; 128 | useEffect( 129 | () => AMapGeolocation.setLocationCacheEnable(data.isLocationCacheEnable), 130 | [data.isLocationCacheEnable], 131 | ); 132 | 133 | const gpsFirstLabel = `${data.isGps ? '关闭:' : '开启:'}androidGPS优先`; 134 | 135 | return ( 136 | 137 | 141 | 144 | 148 | ☆AMapGeolocation Example☆ 149 |