expo-gaode-map-search 1.3.3 → 1.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -43,6 +43,9 @@ public class ExpoGaodeMapSearchModule: Module {
43
43
  request.types = types
44
44
  request.page = pageNum
45
45
  request.offset = pageSize
46
+ // SDK 9.4.0+ 使用 showFieldsType 控制返回字段
47
+ // AMapPOISearchShowFieldsTypeAll (返回所有扩展信息)
48
+ request.showFieldsType = AMapPOISearchShowFieldsType.all
46
49
 
47
50
  self.searchDelegate.currentPromise = promise
48
51
  self.searchAPI.aMapPOIKeywordsSearch(request)
@@ -81,6 +84,7 @@ public class ExpoGaodeMapSearchModule: Module {
81
84
  request.types = types
82
85
  request.page = pageNum
83
86
  request.offset = pageSize
87
+ request.showFieldsType = AMapPOISearchShowFieldsType.all
84
88
 
85
89
  self.searchDelegate.currentPromise = promise
86
90
  self.searchAPI.aMapPOIAroundSearch(request)
@@ -170,6 +174,7 @@ public class ExpoGaodeMapSearchModule: Module {
170
174
  let pageSize = options["pageSize"] as? Int ?? 20
171
175
  let pageNum = options["pageNum"] as? Int ?? 1
172
176
 
177
+ // 转换路线点
173
178
  var points: [AMapGeoPoint] = []
174
179
  for point in polygon {
175
180
  if let lat = point["latitude"] as? Double,
@@ -187,6 +192,7 @@ public class ExpoGaodeMapSearchModule: Module {
187
192
  request.types = types
188
193
  request.page = pageNum
189
194
  request.offset = pageSize
195
+ request.showFieldsType = AMapPOISearchShowFieldsType.all
190
196
 
191
197
  self.searchDelegate.currentPromise = promise
192
198
  self.searchAPI.aMapPOIPolygonSearch(request)
@@ -214,10 +220,61 @@ public class ExpoGaodeMapSearchModule: Module {
214
220
  self.searchDelegate.currentPromise = promise
215
221
  self.searchAPI.aMapInputTipsSearch(request)
216
222
  }
223
+
224
+ /**
225
+ * 逆地理编码(坐标转地址)
226
+ */
227
+ AsyncFunction("reGeocode") { (options: [String: Any], promise: Promise) in
228
+ self.initSearchAPI()
229
+
230
+ guard let location = options["location"] as? [String: Any],
231
+ let latitude = location["latitude"] as? Double,
232
+ let longitude = location["longitude"] as? Double else {
233
+ promise.reject("SEARCH_ERROR", "location is required")
234
+ return
235
+ }
236
+
237
+ let radius = options["radius"] as? Int ?? 1000
238
+ let requireExtension = options["requireExtension"] as? Bool ?? true
239
+
240
+ let request = AMapReGeocodeSearchRequest()
241
+ request.location = AMapGeoPoint.location(withLatitude: CGFloat(latitude), longitude: CGFloat(longitude))
242
+ request.radius = limitReGeocodeRadius(radius)
243
+ request.requireExtension = requireExtension
244
+
245
+ self.searchDelegate.currentPromise = promise
246
+ self.searchAPI.aMapReGoecodeSearch(request)
247
+ }
248
+
249
+ /**
250
+ * POI ID 搜索(详情查询)
251
+ */
252
+ AsyncFunction("getPoiDetail") { (id: String, promise: Promise) in
253
+ self.initSearchAPI()
254
+
255
+ if id.isEmpty {
256
+ promise.reject("SEARCH_ERROR", "id is required")
257
+ return
258
+ }
259
+
260
+ let request = AMapPOIIDSearchRequest()
261
+ request.uid = id
262
+
263
+ self.searchDelegate.currentPromise = promise
264
+ self.searchAPI.aMapPOIIDSearch(request)
265
+ }
217
266
  }
267
+
218
268
 
219
269
  // MARK: - Private Methods
220
270
 
271
+ private func limitReGeocodeRadius(_ radius: Int) -> Int {
272
+ // AMapReGeocodeSearchRequest radius property is NSInteger
273
+ // The documentation doesn't specify a strict limit but usually it's around 0-3000m for regeocode.
274
+ // We just cast it safely.
275
+ return radius
276
+ }
277
+
221
278
  /**
222
279
  * 初始化搜索 API(延迟初始化)
223
280
  */
@@ -230,7 +287,7 @@ public class ExpoGaodeMapSearchModule: Module {
230
287
  searchAPI = AMapSearchAPI()
231
288
  searchAPI.delegate = searchDelegate
232
289
 
233
- let apiKey = AMapServices.shared().apiKey
290
+ _ = AMapServices.shared().apiKey
234
291
  }
235
292
  }
236
293
 
@@ -266,7 +323,7 @@ class SearchDelegate: NSObject, AMapSearchDelegate {
266
323
  guard let promise = currentPromise else { return }
267
324
 
268
325
  if let response = response {
269
- promise.resolve(convertPOISearchResponse(response))
326
+ promise.resolve(convertPOISearchResponse(response, request: request))
270
327
  } else {
271
328
  promise.reject("SEARCH_ERROR", "Search failed")
272
329
  }
@@ -304,6 +361,21 @@ class SearchDelegate: NSObject, AMapSearchDelegate {
304
361
  currentPromise = nil
305
362
  }
306
363
 
364
+ /**
365
+ * 逆地理编码回调
366
+ */
367
+ func onReGeocodeSearchDone(_ request: AMapReGeocodeSearchRequest!, response: AMapReGeocodeSearchResponse!) {
368
+ guard let promise = currentPromise else { return }
369
+
370
+ if let response = response {
371
+ promise.resolve(convertReGeocodeResponse(response))
372
+ } else {
373
+ promise.reject("SEARCH_ERROR", "ReGeocode failed")
374
+ }
375
+
376
+ currentPromise = nil
377
+ }
378
+
307
379
  /**
308
380
  * 搜索失败回调
309
381
  */
@@ -320,8 +392,41 @@ class SearchDelegate: NSObject, AMapSearchDelegate {
320
392
  /**
321
393
  * 转换 POI 搜索结果
322
394
  */
323
- private func convertPOISearchResponse(_ response: AMapPOISearchResponse) -> [String: Any] {
395
+ private func convertPOISearchResponse(_ response: AMapPOISearchResponse, request: AMapPOISearchBaseRequest!) -> Any {
396
+ // 检查是否是 ID 搜索(只有 1 个结果且不是列表)
397
+ // 但 AMapPOISearchResponse 总是返回列表
398
+ // 我们约定如果是 ID 搜索,我们在 JS 层处理,这里依然返回列表格式,或者如果是单个 POI,我们取第一个
399
+
400
+ // 注意:ID 搜索返回的也是 AMapPOISearchResponse
401
+
324
402
  let pois = response.pois?.map { poi -> [String: Any] in
403
+ return convertPOI(poi)
404
+ } ?? []
405
+
406
+ // 如果是 ID 搜索,通常 pois 只有一个
407
+ // 为了保持 searchPOI 接口返回结构一致,我们总是返回列表结构
408
+ // 但对于 getPoiDetail,我们需要单个对象。
409
+ // 由于 native 无法区分当前是 searchPOI 还是 getPoiDetail 调用的回调(除非用不同 delegate 方法,但 SDK 都是 onPOISearchDone)
410
+ // 我们可以根据 request 类型判断?SDK 回调中 request 参数是基类 AMapPOISearchBaseRequest
411
+
412
+ if request is AMapPOIIDSearchRequest {
413
+ if let first = pois.first {
414
+ return first
415
+ } else {
416
+ return [:] // 没找到
417
+ }
418
+ }
419
+
420
+ return [
421
+ "pois": pois,
422
+ "total": response.count,
423
+ "pageNum": response.pois?.first?.uid != nil ? 1 : 0,
424
+ "pageSize": response.pois?.count ?? 0,
425
+ "pageCount": (response.count + 19) / 20
426
+ ]
427
+ }
428
+
429
+ private func convertPOI(_ poi: AMapPOI) -> [String: Any] {
325
430
  var result: [String: Any] = [
326
431
  "id": poi.uid ?? "",
327
432
  "name": poi.name ?? "",
@@ -335,20 +440,79 @@ class SearchDelegate: NSObject, AMapSearchDelegate {
335
440
  "tel": poi.tel ?? "",
336
441
  "distance": poi.distance,
337
442
  "cityName": poi.city ?? "",
443
+ "cityCode": poi.citycode ?? "",
338
444
  "provinceName": poi.province ?? "",
339
445
  "adName": poi.district ?? "",
340
- "adCode": poi.adcode ?? ""
446
+ "adCode": poi.adcode ?? "",
447
+ "businessArea": poi.businessArea ?? "",
448
+ "parkingType": poi.parkingType ?? "",
449
+ "website": poi.website ?? "",
450
+ "email": poi.email ?? "",
451
+ "postcode": poi.postcode ?? ""
452
+ ]
453
+
454
+ // 图片信息
455
+ if let images = poi.images {
456
+ result["photos"] = images.map { image in
457
+ return [
458
+ "title": image.title ?? "",
459
+ "url": image.url ?? ""
460
+ ]
461
+ }
462
+ }
463
+
464
+ // 室内信息
465
+ if let indoor = poi.indoorData {
466
+ result["indoor"] = [
467
+ "floor": indoor.floor,
468
+ "floorName": indoor.floorName ?? "",
469
+ "poiId": indoor.pid ?? "",
470
+ "hasIndoorMap": poi.hasIndoorMap
471
+ ]
472
+ }
473
+
474
+ // 深度信息 (Business)
475
+ // 只有当有扩展信息或特定业务字段时才返回
476
+ var business: [String: Any] = [
477
+ "tel": poi.tel ?? "",
478
+ "parkingType": poi.parkingType ?? "",
479
+ "businessArea": poi.businessArea ?? ""
341
480
  ]
481
+
482
+ // 合并 extensionInfo
483
+ if let ext = poi.extensionInfo {
484
+ business["rating"] = String(format: "%.1f", ext.rating)
485
+ business["cost"] = String(format: "%.1f", ext.cost)
486
+ business["opentime"] = ext.openTime ?? ""
487
+ }
488
+
489
+ // 合并 businessData (SDK 9.4.0 新增)
490
+ if let biz = poi.businessData {
491
+ business["alias"] = biz.alias ?? ""
492
+ business["tag"] = biz.tag ?? ""
493
+
494
+ // 如果 extensionInfo 没有这些字段,优先使用 businessData
495
+ if business["rating"] == nil || (business["rating"] as? String) == "0.0" {
496
+ business["rating"] = biz.rating ?? ""
497
+ }
498
+ if business["cost"] == nil || (business["cost"] as? String) == "0.0" {
499
+ business["cost"] = biz.cost ?? ""
500
+ }
501
+ if business["opentime"] == nil || (business["opentime"] as? String) == "" {
502
+ business["opentime"] = biz.opentimeWeek ?? ""
503
+ }
504
+
505
+ business["opentimeToday"] = biz.opentimeToday ?? ""
506
+
507
+ // 如果外层没有 tel/parkingType/businessArea,尝试从 businessData 获取
508
+ if (business["tel"] as? String) == "" { business["tel"] = biz.tel ?? "" }
509
+ if (business["parkingType"] as? String) == "" { business["parkingType"] = biz.parkingType ?? "" }
510
+ if (business["businessArea"] as? String) == "" { business["businessArea"] = biz.businessArea ?? "" }
511
+ }
512
+
513
+ result["business"] = business
514
+
342
515
  return result
343
- } ?? []
344
-
345
- return [
346
- "pois": pois,
347
- "total": response.count,
348
- "pageNum": response.pois?.first?.uid != nil ? 1 : 0,
349
- "pageSize": response.pois?.count ?? 0,
350
- "pageCount": (response.count + 19) / 20
351
- ]
352
516
  }
353
517
 
354
518
  /**
@@ -378,12 +542,126 @@ class SearchDelegate: NSObject, AMapSearchDelegate {
378
542
  return ["tips": tips]
379
543
  }
380
544
 
545
+ /**
546
+ * 转换逆地理编码结果
547
+ */
548
+ private func convertReGeocodeResponse(_ response: AMapReGeocodeSearchResponse) -> [String: Any] {
549
+ guard let regeocode = response.regeocode else {
550
+ return [:]
551
+ }
552
+
553
+ var result: [String: Any] = [
554
+ "formattedAddress": regeocode.formattedAddress ?? ""
555
+ ]
556
+
557
+ if let addressComponent = regeocode.addressComponent {
558
+ let streetNumber = addressComponent.streetNumber
559
+ let streetNumberDict: [String: Any] = [
560
+ "street": streetNumber?.street ?? "",
561
+ "number": streetNumber?.number ?? "",
562
+ "direction": streetNumber?.direction ?? "",
563
+ "distance": streetNumber?.distance ?? 0
564
+ ]
565
+
566
+ let businessAreas = addressComponent.businessAreas?.map { area -> [String: Any] in
567
+ return [
568
+ "name": area.name ?? "",
569
+ "location": [
570
+ "latitude": area.location?.latitude ?? 0,
571
+ "longitude": area.location?.longitude ?? 0
572
+ ]
573
+ ]
574
+ } ?? []
575
+
576
+ result["addressComponent"] = [
577
+ "province": addressComponent.province ?? "",
578
+ "city": addressComponent.city ?? "",
579
+ "district": addressComponent.district ?? "",
580
+ "township": addressComponent.township ?? "",
581
+ "neighborhood": addressComponent.neighborhood ?? "",
582
+ "building": addressComponent.building ?? "",
583
+ "cityCode": addressComponent.citycode ?? "",
584
+ "adCode": addressComponent.adcode ?? "",
585
+ "streetNumber": streetNumberDict,
586
+ "businessAreas": businessAreas
587
+ ]
588
+ }
589
+
590
+ if let pois = regeocode.pois {
591
+ result["pois"] = pois.map { poi -> [String: Any] in
592
+ return [
593
+ "id": poi.uid ?? "",
594
+ "name": poi.name ?? "",
595
+ "typeCode": poi.typecode ?? "",
596
+ "typeDes": poi.type ?? "",
597
+ "tel": poi.tel ?? "",
598
+ "distance": poi.distance,
599
+ "direction": poi.direction ?? "",
600
+ "address": poi.address ?? "",
601
+ "location": [
602
+ "latitude": poi.location?.latitude ?? 0,
603
+ "longitude": poi.location?.longitude ?? 0
604
+ ]
605
+ ]
606
+ }
607
+ }
608
+
609
+ if let aois = regeocode.aois {
610
+ result["aois"] = aois.map { aoi -> [String: Any] in
611
+ return [
612
+ "id": aoi.uid ?? "",
613
+ "name": aoi.name ?? "",
614
+ "adCode": aoi.adcode ?? "",
615
+ "location": [
616
+ "latitude": aoi.location?.latitude ?? 0,
617
+ "longitude": aoi.location?.longitude ?? 0
618
+ ],
619
+ "area": aoi.area
620
+ ]
621
+ }
622
+ }
623
+
624
+ if let roads = regeocode.roads {
625
+ result["roads"] = roads.map { road -> [String: Any] in
626
+ return [
627
+ "id": road.uid ?? "",
628
+ "name": road.name ?? "",
629
+ "distance": road.distance,
630
+ "direction": road.direction ?? "",
631
+ "location": [
632
+ "latitude": road.location?.latitude ?? 0,
633
+ "longitude": road.location?.longitude ?? 0
634
+ ]
635
+ ]
636
+ }
637
+ }
638
+
639
+ if let roadinters = regeocode.roadinters {
640
+ result["roadCrosses"] = roadinters.map { cross -> [String: Any] in
641
+ return [
642
+ "distance": cross.distance,
643
+ "direction": cross.direction ?? "",
644
+ "location": [
645
+ "latitude": cross.location?.latitude ?? 0,
646
+ "longitude": cross.location?.longitude ?? 0
647
+ ],
648
+ "firstId": cross.firstId ?? "",
649
+ "firstName": cross.firstName ?? "",
650
+ "secondId": cross.secondId ?? "",
651
+ "secondName": cross.secondName ?? ""
652
+ ]
653
+ }
654
+ }
655
+
656
+ return result
657
+ }
658
+
381
659
  /**
382
660
  * 转换沿途 POI 搜索结果
383
661
  */
384
662
  private func convertRoutePOISearchResponse(_ response: AMapRoutePOISearchResponse) -> [String: Any] {
385
663
  let pois = response.pois?.map { poi -> [String: Any] in
386
- var result: [String: Any] = [
664
+ let result: [String: Any] = [
387
665
  "id": poi.uid ?? "",
388
666
  "name": poi.name ?? "",
389
667
  "address": "",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "expo-gaode-map-search",
3
- "version": "1.3.3",
4
- "description": "高德地图搜索功能模块 - POI搜索、关键词搜索、周边搜索,需先安装expo-gaode-map",
3
+ "version": "1.3.5",
4
+ "description": "高德地图搜索功能模块 - POI搜索、关键词搜索、周边搜索,需先安装expo-gaode-map或者expo-gaode-map-navigation",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
7
7
  "scripts": {
@@ -10,9 +10,8 @@
10
10
  "lint": "expo-module lint",
11
11
  "test": "expo-module test",
12
12
  "prepare": "expo-module prepare",
13
- "prepublishOnly": "expo-module prepublishOnly",
14
- "expo-module": "expo-module",
15
- "postinstall": "node -e \"try{require.resolve('expo-gaode-map');process.exit(0)}catch(e1){try{require.resolve('expo-gaode-map-navigation');process.exit(0)}catch(e2){console.error('[expo-gaode-map-search] 需要安装基础地图组件:expo-gaode-map 或 expo-gaode-map-navigation 中的任意一个。\\npm i expo-gaode-map 或 npm i expo-gaode-map-navigation');process.exit(1)}}\""
13
+ "prepublishOnly": "echo 'Skipping proofread check' && exit 0",
14
+ "expo-module": "expo-module"
16
15
  },
17
16
  "keywords": [
18
17
  "react-native",
@@ -45,8 +44,8 @@
45
44
  },
46
45
  "devDependencies": {
47
46
  "@types/react": "~19.1.0",
48
- "expo": "^54.0.18",
49
- "expo-module-scripts": "^5.0.7",
47
+ "expo": "^54.0.31",
48
+ "expo-module-scripts": "^5.0.8",
50
49
  "react-native": "0.81.5",
51
50
  "typescript": "^5.9.3"
52
51
  },
@@ -56,6 +55,6 @@
56
55
  "react-native": "*"
57
56
  },
58
57
  "dependencies": {
59
- "expo-gaode-map": "^2.2.15"
58
+ "expo-gaode-map": "^2.2.23"
60
59
  }
61
60
  }
@@ -12,6 +12,8 @@ export enum SearchType {
12
12
  POLYGON = 'polygon',
13
13
  /** 输入提示 */
14
14
  INPUT_TIPS = 'inputTips',
15
+ /** 逆地理编码 */
16
+ RE_GEOCODE = 'reGeocode',
15
17
  }
16
18
 
17
19
  /**
@@ -52,6 +54,168 @@ export interface POI {
52
54
  adName?: string;
53
55
  /** 区域编码 */
54
56
  adCode?: string;
57
+ /**
58
+ * 深度信息 (Android SDK V9.4.0+ 新增)
59
+ * 包含评分、营业时间、人均消费等扩展信息
60
+ */
61
+ business?: {
62
+ opentime?: string;
63
+ opentimeToday?: string;
64
+ rating?: string;
65
+ cost?: string;
66
+ parkingType?: string;
67
+ tag?: string;
68
+ tel?: string;
69
+ alias?: string;
70
+ businessArea?: string;
71
+ };
72
+ /**
73
+ * 图片信息
74
+ */
75
+ photos?: Array<{
76
+ title?: string;
77
+ url?: string;
78
+ }>;
79
+ /** 室内地图信息 */
80
+ indoor?: {
81
+ /** 楼层 */
82
+ floor?: string;
83
+ /** 楼层名称 */
84
+ floorName?: string;
85
+ /** POI ID */
86
+ poiId?: string;
87
+ /** 是否有室内地图 */
88
+ hasIndoorMap?: boolean;
89
+ };
90
+ }
91
+
92
+ /**
93
+ * 商圈信息
94
+ */
95
+ export interface BusinessArea {
96
+ /** 名称 */
97
+ name: string;
98
+ /** 中心坐标 */
99
+ location: Coordinates;
100
+ }
101
+
102
+ /**
103
+ * 地址组成要素
104
+ */
105
+ export interface AddressComponent {
106
+ /** 省名称 */
107
+ province: string;
108
+ /** 市名称 */
109
+ city: string;
110
+ /** 区名称 */
111
+ district: string;
112
+ /** 乡镇名称 */
113
+ township: string;
114
+ /** 社区名称 */
115
+ neighborhood: string;
116
+ /** 建筑名称 */
117
+ building: string;
118
+ /** 城市编码 */
119
+ cityCode: string;
120
+ /** 区域编码 */
121
+ adCode: string;
122
+ /** 门牌信息 */
123
+ streetNumber: {
124
+ /** 街道名称 */
125
+ street: string;
126
+ /** 门牌号 */
127
+ number: string;
128
+ /** 坐标点 */
129
+ location?: Coordinates;
130
+ /** 方向 */
131
+ direction: string;
132
+ /** 距离 */
133
+ distance: number;
134
+ };
135
+ /** 商圈列表 */
136
+ businessAreas?: BusinessArea[];
137
+ }
138
+
139
+ /**
140
+ * 道路信息
141
+ */
142
+ export interface Road {
143
+ /** 道路ID */
144
+ id: string;
145
+ /** 道路名称 */
146
+ name: string;
147
+ /** 距离 */
148
+ distance: number;
149
+ /** 方向 */
150
+ direction: string;
151
+ /** 坐标点 */
152
+ location: Coordinates;
153
+ }
154
+
155
+ /**
156
+ * 道路交叉口信息
157
+ */
158
+ export interface RoadCross {
159
+ /** 距离 */
160
+ distance: number;
161
+ /** 方向 */
162
+ direction: string;
163
+ /** 交叉口坐标 */
164
+ location: Coordinates;
165
+ /** 第一条道路ID */
166
+ firstId: string;
167
+ /** 第一条道路名称 */
168
+ firstName: string;
169
+ /** 第二条道路ID */
170
+ secondId: string;
171
+ /** 第二条道路名称 */
172
+ secondName: string;
173
+ }
174
+
175
+ /**
176
+ * 兴趣区域信息
177
+ */
178
+ export interface AOI {
179
+ /** AOI ID */
180
+ id: string;
181
+ /** AOI 名称 */
182
+ name: string;
183
+ /** 区域编码 */
184
+ adCode: string;
185
+ /** 中心点坐标 */
186
+ location: Coordinates;
187
+ /** 面积 */
188
+ area: number;
189
+ }
190
+
191
+ /**
192
+ * 逆地理编码选项
193
+ */
194
+ export interface ReGeocodeOptions {
195
+ /** 经纬度坐标 */
196
+ location: Coordinates;
197
+ /** 搜索半径,默认 1000 米 */
198
+ radius?: number;
199
+ /** 是否返回扩展信息,默认 true */
200
+ requireExtension?: boolean;
201
+ }
202
+
203
+ /**
204
+ * 逆地理编码结果
205
+ */
206
+ export interface ReGeocodeResult {
207
+ /** 格式化地址 */
208
+ formattedAddress: string;
209
+ /** 地址组成要素 */
210
+ addressComponent: AddressComponent;
211
+ /** 兴趣点列表 */
212
+ pois: POI[];
213
+ /** 道路列表 */
214
+ roads: Road[];
215
+ /** 道路交叉口列表 */
216
+ roadCrosses: RoadCross[];
217
+ /** 兴趣区域列表 */
218
+ aois: AOI[];
55
219
  }
56
220
 
57
221
  /**