expo-gaode-map-navigation 1.1.5 → 1.1.7

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.
Files changed (146) hide show
  1. package/README.md +213 -73
  2. package/android/build.gradle +10 -0
  3. package/android/src/main/cpp/CMakeLists.txt +24 -0
  4. package/android/src/main/cpp/cluster_jni.cpp +848 -0
  5. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapModule.kt +616 -92
  6. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapOfflineModule.kt +493 -0
  7. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapView.kt +230 -14
  8. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapViewModule.kt +37 -27
  9. package/android/src/main/java/expo/modules/gaodemap/map/MapPreloadManager.kt +494 -0
  10. package/android/src/main/java/expo/modules/gaodemap/map/companion/BitmapDescriptorCache.kt +30 -0
  11. package/android/src/main/java/expo/modules/gaodemap/map/companion/IconBitmapCache.kt +37 -0
  12. package/android/src/main/java/expo/modules/gaodemap/map/managers/UIManager.kt +76 -0
  13. package/android/src/main/java/expo/modules/gaodemap/map/modules/LocationManager.kt +15 -3
  14. package/android/src/main/java/expo/modules/gaodemap/map/modules/SDKInitializer.kt +4 -59
  15. package/android/src/main/java/expo/modules/gaodemap/map/overlays/CircleView.kt +9 -12
  16. package/android/src/main/java/expo/modules/gaodemap/map/overlays/CircleViewModule.kt +5 -6
  17. package/android/src/main/java/expo/modules/gaodemap/map/overlays/ClusterView.kt +539 -66
  18. package/android/src/main/java/expo/modules/gaodemap/map/overlays/ClusterViewModule.kt +17 -1
  19. package/android/src/main/java/expo/modules/gaodemap/map/overlays/HeatMapView.kt +165 -33
  20. package/android/src/main/java/expo/modules/gaodemap/map/overlays/HeatMapViewModule.kt +15 -3
  21. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerView.kt +1249 -672
  22. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerViewModule.kt +40 -17
  23. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MultiPointView.kt +177 -22
  24. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MultiPointViewModule.kt +11 -3
  25. package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolygonView.kt +57 -14
  26. package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolygonViewModule.kt +9 -5
  27. package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolylineView.kt +90 -63
  28. package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolylineViewModule.kt +7 -3
  29. package/android/src/main/java/expo/modules/gaodemap/map/services/LocationForegroundService.kt +3 -2
  30. package/android/src/main/java/expo/modules/gaodemap/map/utils/BitmapDescriptorCache.kt +20 -0
  31. package/android/src/main/java/expo/modules/gaodemap/map/utils/ClusterNative.kt +13 -0
  32. package/android/src/main/java/expo/modules/gaodemap/map/utils/ColorParser.kt +20 -0
  33. package/android/src/main/java/expo/modules/gaodemap/map/utils/GeometryUtils.kt +515 -0
  34. package/android/src/main/java/expo/modules/gaodemap/map/utils/LatLngParser.kt +91 -0
  35. package/android/src/main/java/expo/modules/gaodemap/map/utils/PermissionHelper.kt +248 -0
  36. package/build/ExpoGaodeMapNaviView.d.ts +7 -7
  37. package/build/ExpoGaodeMapNaviView.js +10 -11
  38. package/build/ExpoGaodeMapNavigationModule.d.ts +2 -1
  39. package/build/index.d.ts +35 -33
  40. package/build/index.js +70 -106
  41. package/build/map/ExpoGaodeMapModule.d.ts +2 -201
  42. package/build/map/ExpoGaodeMapModule.js +586 -18
  43. package/build/map/ExpoGaodeMapOfflineModule.d.ts +139 -0
  44. package/build/map/ExpoGaodeMapOfflineModule.js +8 -0
  45. package/build/map/ExpoGaodeMapView.js +66 -58
  46. package/build/map/components/FoldableMapView.d.ts +38 -0
  47. package/build/map/components/FoldableMapView.js +209 -0
  48. package/build/map/components/MapContext.d.ts +12 -0
  49. package/build/map/components/MapContext.js +54 -0
  50. package/build/map/components/MapUI.d.ts +18 -0
  51. package/build/map/components/MapUI.js +29 -0
  52. package/build/map/components/overlays/Circle.js +34 -3
  53. package/build/map/components/overlays/Cluster.d.ts +3 -1
  54. package/build/map/components/overlays/Cluster.js +31 -2
  55. package/build/map/components/overlays/HeatMap.d.ts +3 -1
  56. package/build/map/components/overlays/HeatMap.js +33 -3
  57. package/build/map/components/overlays/Marker.d.ts +1 -1
  58. package/build/map/components/overlays/Marker.js +37 -32
  59. package/build/map/components/overlays/MultiPoint.js +1 -1
  60. package/build/map/components/overlays/Polygon.js +30 -3
  61. package/build/map/components/overlays/Polyline.js +36 -3
  62. package/build/map/index.d.ts +25 -5
  63. package/build/map/index.js +59 -18
  64. package/build/map/types/common.types.d.ts +40 -0
  65. package/build/map/types/common.types.js +0 -4
  66. package/build/map/types/index.d.ts +3 -2
  67. package/build/map/types/map-view.types.d.ts +108 -3
  68. package/build/map/types/native-module.types.d.ts +363 -0
  69. package/build/map/types/native-module.types.js +5 -0
  70. package/build/map/types/offline.types.d.ts +132 -0
  71. package/build/map/types/offline.types.js +5 -0
  72. package/build/map/types/overlays.types.d.ts +137 -24
  73. package/build/map/utils/ErrorHandler.d.ts +110 -0
  74. package/build/map/utils/ErrorHandler.js +421 -0
  75. package/build/map/utils/GeoUtils.d.ts +20 -0
  76. package/build/map/utils/GeoUtils.js +76 -0
  77. package/build/map/utils/OfflineMapManager.d.ts +148 -0
  78. package/build/map/utils/OfflineMapManager.js +217 -0
  79. package/build/map/utils/PermissionUtils.d.ts +91 -0
  80. package/build/map/utils/PermissionUtils.js +255 -0
  81. package/build/map/utils/PlatformDetector.d.ts +102 -0
  82. package/build/map/utils/PlatformDetector.js +186 -0
  83. package/build/types/index.d.ts +1 -0
  84. package/build/types/index.js +1 -0
  85. package/build/types/native-module.types.d.ts +69 -0
  86. package/build/types/native-module.types.js +2 -0
  87. package/build/types/naviview.types.d.ts +1 -1
  88. package/expo-module.config.json +12 -10
  89. package/ios/ExpoGaodeMapNavigation.podspec +9 -0
  90. package/ios/map/ExpoGaodeMapModule.swift +485 -75
  91. package/ios/map/ExpoGaodeMapOfflineModule.swift +479 -0
  92. package/ios/map/ExpoGaodeMapView.swift +611 -62
  93. package/ios/map/ExpoGaodeMapViewModule.swift +48 -26
  94. package/ios/map/MapPreloadManager.swift +348 -0
  95. package/ios/map/cpp/ClusterEngine.cpp +110 -0
  96. package/ios/map/cpp/ClusterEngine.hpp +20 -0
  97. package/ios/map/cpp/ColorParser.cpp +135 -0
  98. package/ios/map/cpp/ColorParser.hpp +14 -0
  99. package/ios/map/cpp/GeometryEngine.cpp +574 -0
  100. package/ios/map/cpp/GeometryEngine.hpp +159 -0
  101. package/ios/map/cpp/QuadTree.cpp +92 -0
  102. package/ios/map/cpp/QuadTree.hpp +42 -0
  103. package/ios/map/cpp/README.md +55 -0
  104. package/ios/map/managers/UIManager.swift +72 -1
  105. package/ios/map/modules/LocationManager.swift +123 -166
  106. package/ios/map/overlays/CircleView.swift +16 -32
  107. package/ios/map/overlays/CircleViewModule.swift +12 -12
  108. package/ios/map/overlays/ClusterAnnotation.swift +32 -0
  109. package/ios/map/overlays/ClusterView.swift +331 -45
  110. package/ios/map/overlays/ClusterViewModule.swift +20 -6
  111. package/ios/map/overlays/HeatMapView.swift +135 -32
  112. package/ios/map/overlays/HeatMapViewModule.swift +20 -8
  113. package/ios/map/overlays/MarkerView.swift +613 -130
  114. package/ios/map/overlays/MarkerViewModule.swift +38 -18
  115. package/ios/map/overlays/MultiPointView.swift +168 -10
  116. package/ios/map/overlays/MultiPointViewModule.swift +27 -5
  117. package/ios/map/overlays/PolygonView.swift +62 -23
  118. package/ios/map/overlays/PolygonViewModule.swift +18 -12
  119. package/ios/map/overlays/PolylineView.swift +21 -13
  120. package/ios/map/overlays/PolylineViewModule.swift +18 -12
  121. package/ios/map/utils/ClusterNative.h +96 -0
  122. package/ios/map/utils/ClusterNative.mm +377 -0
  123. package/ios/map/utils/ColorParser.swift +12 -1
  124. package/ios/map/utils/CppBridging.mm +13 -0
  125. package/ios/map/utils/GeometryUtils.swift +34 -0
  126. package/ios/map/utils/LatLngParser.swift +87 -0
  127. package/ios/map/utils/PermissionManager.swift +135 -6
  128. package/package.json +3 -2
  129. package/shared/cpp/ClusterEngine.cpp +110 -0
  130. package/shared/cpp/ClusterEngine.hpp +20 -0
  131. package/shared/cpp/ColorParser.cpp +135 -0
  132. package/shared/cpp/ColorParser.hpp +14 -0
  133. package/shared/cpp/GeometryEngine.cpp +574 -0
  134. package/shared/cpp/GeometryEngine.hpp +159 -0
  135. package/shared/cpp/QuadTree.cpp +92 -0
  136. package/shared/cpp/QuadTree.hpp +42 -0
  137. package/shared/cpp/README.md +55 -0
  138. package/shared/cpp/tests/benchmark_js.js +41 -0
  139. package/shared/cpp/tests/run.sh +17 -0
  140. package/shared/cpp/tests/test_main.cpp +276 -0
  141. package/build/map/ExpoGaodeMap.types.d.ts +0 -41
  142. package/build/map/ExpoGaodeMap.types.js +0 -24
  143. package/build/map/utils/EventManager.d.ts +0 -10
  144. package/build/map/utils/EventManager.js +0 -26
  145. package/build/map/utils/ModuleLoader.d.ts +0 -73
  146. package/build/map/utils/ModuleLoader.js +0 -112
@@ -0,0 +1,574 @@
1
+ #include "GeometryEngine.hpp"
2
+
3
+ #include <cmath>
4
+ #include <map>
5
+ #include <algorithm>
6
+
7
+ namespace gaodemap {
8
+
9
+ static constexpr double kEarthRadiusMeters = 6371000.0;
10
+ static constexpr double kPi = 3.14159265358979323846;
11
+ static constexpr double kDegreesToRadians = kPi / 180.0;
12
+ static constexpr double kRadiansToDegrees = 180.0 / kPi;
13
+
14
+ static inline double geo_toRadians(double degrees) {
15
+ return degrees * kDegreesToRadians;
16
+ }
17
+
18
+ static inline double geo_toDegrees(double radians) {
19
+ return radians * kRadiansToDegrees;
20
+ }
21
+
22
+ double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
23
+ const double radLat1 = geo_toRadians(lat1);
24
+ const double radLat2 = geo_toRadians(lat2);
25
+ const double dLat = radLat2 - radLat1;
26
+ const double dLon = geo_toRadians(lon2 - lon1);
27
+
28
+ const double sinHalfLat = std::sin(dLat * 0.5);
29
+ const double sinHalfLon = std::sin(dLon * 0.5);
30
+ const double h = sinHalfLat * sinHalfLat + std::cos(radLat1) * std::cos(radLat2) * sinHalfLon * sinHalfLon;
31
+ const double c = 2.0 * std::atan2(std::sqrt(h), std::sqrt(1.0 - h));
32
+
33
+ return kEarthRadiusMeters * c;
34
+ }
35
+
36
+ static double haversineMeters(double lat1, double lon1, double lat2, double lon2) {
37
+ return calculateDistance(lat1, lon1, lat2, lon2);
38
+ }
39
+
40
+ // 计算方位角 (Bearing)
41
+ static double calculateBearing(double lat1, double lon1, double lat2, double lon2) {
42
+ double phi1 = geo_toRadians(lat1);
43
+ double phi2 = geo_toRadians(lat2);
44
+ double lam1 = geo_toRadians(lon1);
45
+ double lam2 = geo_toRadians(lon2);
46
+
47
+ double y = std::sin(lam2 - lam1) * std::cos(phi2);
48
+ double x = std::cos(phi1) * std::sin(phi2) -
49
+ std::sin(phi1) * std::cos(phi2) * std::cos(lam2 - lam1);
50
+ double theta = std::atan2(y, x);
51
+
52
+ return std::fmod(geo_toDegrees(theta) + 360.0, 360.0);
53
+ }
54
+
55
+ bool isPointInCircle(double pointLat, double pointLon, double centerLat, double centerLon, double radiusMeters) {
56
+ if (radiusMeters <= 0.0) {
57
+ return false;
58
+ }
59
+ const double distance = haversineMeters(pointLat, pointLon, centerLat, centerLon);
60
+ return distance <= radiusMeters;
61
+ }
62
+
63
+ bool isPointInPolygon(double pointLat, double pointLon, const std::vector<GeoPoint>& polygon) {
64
+ const size_t n = polygon.size();
65
+ if (n < 3) {
66
+ return false;
67
+ }
68
+
69
+ bool inside = false;
70
+ size_t j = n - 1;
71
+ for (size_t i = 0; i < n; ++i) {
72
+ const double xi = polygon[i].lat;
73
+ const double yi = polygon[i].lon;
74
+ const double xj = polygon[j].lat;
75
+ const double yj = polygon[j].lon;
76
+
77
+ const double intersect = ((yi > pointLon) != (yj > pointLon)) &&
78
+ (pointLat < (xj - xi) * (pointLon - yi) / (yj - yi) + xi);
79
+ if (intersect) {
80
+ inside = !inside;
81
+ }
82
+ j = i;
83
+ }
84
+
85
+ return inside;
86
+ }
87
+
88
+ double calculatePolygonArea(const std::vector<GeoPoint>& polygon) {
89
+ const size_t n = polygon.size();
90
+ if (n < 3) {
91
+ return 0.0;
92
+ }
93
+
94
+ double total = 0.0;
95
+ for (size_t i = 0; i < n; ++i) {
96
+ const GeoPoint& p1 = polygon[i];
97
+ const GeoPoint& p2 = polygon[(i + 1) % n];
98
+
99
+ const double lat1 = geo_toRadians(p1.lat);
100
+ const double lat2 = geo_toRadians(p2.lat);
101
+ const double lon1 = geo_toRadians(p1.lon);
102
+ const double lon2 = geo_toRadians(p2.lon);
103
+
104
+ total += (lon2 - lon1) * (2.0 + std::sin(lat1) + std::sin(lat2));
105
+ }
106
+
107
+ const double area = std::abs(total) * (kEarthRadiusMeters * kEarthRadiusMeters) * 0.5;
108
+ return area;
109
+ }
110
+
111
+ double calculateRectangleArea(double swLat, double swLon, double neLat, double neLon) {
112
+ std::vector<GeoPoint> rectangle;
113
+ rectangle.reserve(4);
114
+ rectangle.push_back({swLat, swLon});
115
+ rectangle.push_back({swLat, neLon});
116
+ rectangle.push_back({neLat, neLon});
117
+ rectangle.push_back({neLat, swLon});
118
+ return calculatePolygonArea(rectangle);
119
+ }
120
+
121
+ // 简单的 2D 点结构,用于本地坐标投影计算
122
+ struct Point2D {
123
+ double x;
124
+ double y;
125
+ size_t index; // 原始索引
126
+ };
127
+
128
+ // 计算点到线段的垂直距离的平方
129
+ static double getSqSegDist(const Point2D& p, const Point2D& p1, const Point2D& p2) {
130
+ double x = p1.x;
131
+ double y = p1.y;
132
+ double dx = p2.x - x;
133
+ double dy = p2.y - y;
134
+
135
+ if (dx != 0 || dy != 0) {
136
+ double t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy);
137
+ if (t > 1) {
138
+ x = p2.x;
139
+ y = p2.y;
140
+ } else if (t > 0) {
141
+ x += dx * t;
142
+ y += dy * t;
143
+ }
144
+ }
145
+
146
+ dx = p.x - x;
147
+ dy = p.y - y;
148
+
149
+ return dx * dx + dy * dy;
150
+ }
151
+
152
+ // RDP 递归函数
153
+ static void simplifyDPStep(const std::vector<Point2D>& points, size_t first, size_t last, double sqTolerance, std::vector<size_t>& simplified) {
154
+ double maxSqDist = sqTolerance;
155
+ size_t index = 0;
156
+
157
+ for (size_t i = first + 1; i < last; i++) {
158
+ double sqDist = getSqSegDist(points[i], points[first], points[last]);
159
+ if (sqDist > maxSqDist) {
160
+ index = i;
161
+ maxSqDist = sqDist;
162
+ }
163
+ }
164
+
165
+ if (maxSqDist > sqTolerance) {
166
+ if (index - first > 1) simplifyDPStep(points, first, index, sqTolerance, simplified);
167
+ simplified.push_back(index);
168
+ if (last - index > 1) simplifyDPStep(points, index, last, sqTolerance, simplified);
169
+ }
170
+ }
171
+
172
+ std::vector<GeoPoint> simplifyPolyline(const std::vector<GeoPoint>& points, double toleranceMeters) {
173
+ if (points.size() <= 2) {
174
+ return points;
175
+ }
176
+
177
+ // 1. 投影到平面坐标 (Equirectangular Projection approximation)
178
+ // 以第一个点为原点
179
+ double refLat = points[0].lat;
180
+ double refLon = points[0].lon;
181
+ double refLatRad = geo_toRadians(refLat);
182
+ double metersPerDegreeLat = 111319.9;
183
+ double metersPerDegreeLon = 111319.9 * std::cos(refLatRad);
184
+
185
+ std::vector<Point2D> projectedPoints;
186
+ projectedPoints.reserve(points.size());
187
+ for (size_t i = 0; i < points.size(); ++i) {
188
+ double x = (points[i].lon - refLon) * metersPerDegreeLon;
189
+ double y = (points[i].lat - refLat) * metersPerDegreeLat;
190
+ projectedPoints.push_back({x, y, i});
191
+ }
192
+
193
+ // 2. 运行 RDP 算法
194
+ double sqTolerance = toleranceMeters * toleranceMeters;
195
+ std::vector<size_t> simplifiedIndices;
196
+ simplifiedIndices.push_back(0); // 总是保留第一个点
197
+
198
+ simplifyDPStep(projectedPoints, 0, projectedPoints.size() - 1, sqTolerance, simplifiedIndices);
199
+
200
+ simplifiedIndices.push_back(projectedPoints.size() - 1); // 总是保留最后一个点
201
+
202
+ // 3. 构建结果
203
+ std::vector<GeoPoint> result;
204
+ result.reserve(simplifiedIndices.size());
205
+
206
+ for (size_t idx : simplifiedIndices) {
207
+ result.push_back(points[idx]);
208
+ }
209
+
210
+ return result;
211
+ }
212
+
213
+ double calculatePathLength(const std::vector<GeoPoint>& points) {
214
+ if (points.size() < 2) return 0.0;
215
+
216
+ double total = 0.0;
217
+ for (size_t i = 0; i < points.size() - 1; ++i) {
218
+ total += calculateDistance(points[i].lat, points[i].lon, points[i+1].lat, points[i+1].lon);
219
+ }
220
+ return total;
221
+ }
222
+
223
+ bool getPointAtDistance(const std::vector<GeoPoint>& points, double distanceMeters, double* outLat, double* outLon, double* outAngle) {
224
+ if (points.size() < 2 || distanceMeters < 0) return false;
225
+
226
+ if (distanceMeters == 0) {
227
+ *outLat = points[0].lat;
228
+ *outLon = points[0].lon;
229
+ *outAngle = calculateBearing(points[0].lat, points[0].lon, points[1].lat, points[1].lon);
230
+ return true;
231
+ }
232
+
233
+ double covered = 0.0;
234
+ for (size_t i = 0; i < points.size() - 1; ++i) {
235
+ double d = calculateDistance(points[i].lat, points[i].lon, points[i+1].lat, points[i+1].lon);
236
+ if (covered + d >= distanceMeters) {
237
+ double remaining = distanceMeters - covered;
238
+ double fraction = remaining / d;
239
+
240
+ // 简单的线性插值 (Linear Interpolation)
241
+ // 对于小距离段,这比球形插值快得多且误差可忽略
242
+ *outLat = points[i].lat + (points[i+1].lat - points[i].lat) * fraction;
243
+ *outLon = points[i].lon + (points[i+1].lon - points[i].lon) * fraction;
244
+ *outAngle = calculateBearing(points[i].lat, points[i].lon, points[i+1].lat, points[i+1].lon);
245
+ return true;
246
+ }
247
+ covered += d;
248
+ }
249
+
250
+ // 如果超出总长度,返回最后一个点
251
+ const GeoPoint& last = points.back();
252
+ const GeoPoint& prev = points[points.size() - 2];
253
+ *outLat = last.lat;
254
+ *outLon = last.lon;
255
+ *outAngle = calculateBearing(prev.lat, prev.lon, last.lat, last.lon);
256
+ return true;
257
+ }
258
+
259
+ // Helper: Square of Euclidean distance
260
+ static double distSq(double x1, double y1, double x2, double y2) {
261
+ double dx = x1 - x2;
262
+ double dy = y1 - y2;
263
+ return dx * dx + dy * dy;
264
+ }
265
+
266
+ NearestPointResult getNearestPointOnPath(const std::vector<GeoPoint>& path, const GeoPoint& target) {
267
+ NearestPointResult result = {0.0, 0.0, 0, std::numeric_limits<double>::max()};
268
+
269
+ if (path.empty()) {
270
+ return result;
271
+ }
272
+
273
+ if (path.size() == 1) {
274
+ result.latitude = path[0].lat;
275
+ result.longitude = path[0].lon;
276
+ result.index = 0;
277
+ result.distanceMeters = calculateDistance(target.lat, target.lon, path[0].lat, path[0].lon);
278
+ return result;
279
+ }
280
+
281
+ double minDistance = std::numeric_limits<double>::max();
282
+
283
+ for (size_t i = 0; i < path.size() - 1; ++i) {
284
+ double ax = path[i].lat;
285
+ double ay = path[i].lon;
286
+ double bx = path[i+1].lat;
287
+ double by = path[i+1].lon;
288
+
289
+ // Project target (px, py) onto segment AB
290
+ // Note: This treats lat/lon as cartesian for projection, which is an approximation
291
+ // but generally sufficient for "snapping" to a path.
292
+ // For distance calculation, we use Haversine.
293
+
294
+ double px = target.lat;
295
+ double py = target.lon;
296
+
297
+ double l2 = distSq(ax, ay, bx, by);
298
+ double t = 0.0;
299
+ if (l2 > 0) {
300
+ t = ((px - ax) * (bx - ax) + (py - ay) * (by - ay)) / l2;
301
+ if (t < 0) t = 0;
302
+ else if (t > 1) t = 1;
303
+ }
304
+
305
+ double projLat = ax + t * (bx - ax);
306
+ double projLon = ay + t * (by - ay);
307
+
308
+ double dist = calculateDistance(target.lat, target.lon, projLat, projLon);
309
+
310
+ if (dist < minDistance) {
311
+ minDistance = dist;
312
+ result.latitude = projLat;
313
+ result.longitude = projLon;
314
+ result.index = static_cast<int>(i);
315
+ result.distanceMeters = dist;
316
+ }
317
+ }
318
+
319
+ return result;
320
+ }
321
+
322
+ GeoPoint calculateCentroid(const std::vector<GeoPoint>& polygon) {
323
+ if (polygon.empty()) {
324
+ return {0.0, 0.0};
325
+ }
326
+
327
+ // 简单多边形质心公式
328
+ // Cx = (1/6A) * Σ (xi + xi+1) * (xi * yi+1 - xi+1 * yi)
329
+ // Cy = (1/6A) * Σ (yi + yi+1) * (xi * yi+1 - xi+1 * yi)
330
+
331
+ double signedArea = 0.0;
332
+ double cx = 0.0;
333
+ double cy = 0.0;
334
+
335
+ size_t n = polygon.size();
336
+ // 确保多边形闭合
337
+ bool closed = (polygon[0].lat == polygon[n-1].lat && polygon[0].lon == polygon[n-1].lon);
338
+ size_t limit = closed ? n - 1 : n;
339
+
340
+ for (size_t i = 0; i < limit; ++i) {
341
+ double x0 = polygon[i].lat;
342
+ double y0 = polygon[i].lon;
343
+ double x1 = polygon[(i + 1) % n].lat;
344
+ double y1 = polygon[(i + 1) % n].lon;
345
+
346
+ double a = x0 * y1 - x1 * y0;
347
+ signedArea += a;
348
+ cx += (x0 + x1) * a;
349
+ cy += (y0 + y1) * a;
350
+ }
351
+
352
+ if (std::abs(signedArea) < 1e-9) {
353
+ // 退化为计算所有点的平均值
354
+ double sumLat = 0.0;
355
+ double sumLon = 0.0;
356
+ for(const auto& p : polygon) {
357
+ sumLat += p.lat;
358
+ sumLon += p.lon;
359
+ }
360
+ return {sumLat / n, sumLon / n};
361
+ }
362
+
363
+ signedArea *= 0.5;
364
+ cx /= (6.0 * signedArea);
365
+ cy /= (6.0 * signedArea);
366
+
367
+ return {cx, cy};
368
+ }
369
+
370
+ std::string encodeGeoHash(double lat, double lon, int precision) {
371
+ if (precision < 1) precision = 1;
372
+ if (precision > 12) precision = 12;
373
+
374
+ static const char BASE32[] = "0123456789bcdefghjkmnpqrstuvwxyz";
375
+ std::string hash = "";
376
+
377
+ double minLat = -90.0, maxLat = 90.0;
378
+ double minLon = -180.0, maxLon = 180.0;
379
+
380
+ int bit = 0;
381
+ int ch = 0;
382
+ bool isEven = true;
383
+
384
+ while (hash.length() < precision) {
385
+ if (isEven) {
386
+ double mid = (minLon + maxLon) / 2.0;
387
+ if (lon > mid) {
388
+ ch |= (1 << (4 - bit));
389
+ minLon = mid;
390
+ } else {
391
+ maxLon = mid;
392
+ }
393
+ } else {
394
+ double mid = (minLat + maxLat) / 2.0;
395
+ if (lat > mid) {
396
+ ch |= (1 << (4 - bit));
397
+ minLat = mid;
398
+ } else {
399
+ maxLat = mid;
400
+ }
401
+ }
402
+
403
+ isEven = !isEven;
404
+
405
+ if (bit < 4) {
406
+ bit++;
407
+ } else {
408
+ hash += BASE32[ch];
409
+ bit = 0;
410
+ ch = 0;
411
+ }
412
+ }
413
+
414
+ return hash;
415
+ }
416
+
417
+ std::vector<GeoPoint> parsePolyline(const std::string& polylineStr) {
418
+ std::vector<GeoPoint> points;
419
+ if (polylineStr.empty()) {
420
+ return points;
421
+ }
422
+
423
+ size_t start = 0;
424
+ size_t end = polylineStr.find(';');
425
+
426
+ while (true) {
427
+ std::string segment = polylineStr.substr(start, end - start);
428
+ if (!segment.empty()) {
429
+ size_t comma = segment.find(',');
430
+ if (comma != std::string::npos) {
431
+ try {
432
+ double lon = std::stod(segment.substr(0, comma));
433
+ double lat = std::stod(segment.substr(comma + 1));
434
+ points.push_back({lat, lon});
435
+ } catch (...) {
436
+ // 忽略无效的坐标对
437
+ }
438
+ }
439
+ }
440
+
441
+ if (end == std::string::npos) {
442
+ break;
443
+ }
444
+
445
+ start = end + 1;
446
+ end = polylineStr.find(';', start);
447
+ }
448
+
449
+ return points;
450
+ }
451
+
452
+ PathBounds calculatePathBounds(const std::vector<GeoPoint>& points) {
453
+ PathBounds bounds = { -90.0, 90.0, -180.0, 180.0, 0.0, 0.0 };
454
+
455
+ if (points.empty()) {
456
+ return bounds;
457
+ }
458
+
459
+ double minLat = 90.0;
460
+ double maxLat = -90.0;
461
+ double minLon = 180.0;
462
+ double maxLon = -180.0;
463
+
464
+ for (const auto& p : points) {
465
+ if (p.lat < minLat) minLat = p.lat;
466
+ if (p.lat > maxLat) maxLat = p.lat;
467
+ if (p.lon < minLon) minLon = p.lon;
468
+ if (p.lon > maxLon) maxLon = p.lon;
469
+ }
470
+
471
+ bounds.north = maxLat;
472
+ bounds.south = minLat;
473
+ bounds.east = maxLon;
474
+ bounds.west = minLon;
475
+ bounds.centerLat = (maxLat + minLat) / 2.0;
476
+ bounds.centerLon = (maxLon + minLon) / 2.0;
477
+
478
+ return bounds;
479
+ }
480
+
481
+ // --- 瓦片与坐标转换 ---
482
+
483
+ TileResult latLngToTile(double lat, double lon, int zoom) {
484
+ double n = std::pow(2.0, zoom);
485
+ int x = static_cast<int>((lon + 180.0) / 360.0 * n);
486
+ double latRad = geo_toRadians(lat);
487
+ int y = static_cast<int>((1.0 - std::asinh(std::tan(latRad)) / kPi) / 2.0 * n);
488
+ return {x, y, zoom};
489
+ }
490
+
491
+ GeoPoint tileToLatLng(int x, int y, int zoom) {
492
+ double n = std::pow(2.0, zoom);
493
+ double lon = static_cast<double>(x) / n * 360.0 - 180.0;
494
+ double latRad = std::atan(std::sinh(kPi * (1.0 - 2.0 * static_cast<double>(y) / n)));
495
+ double lat = latRad * kRadiansToDegrees;
496
+ return {lat, lon};
497
+ }
498
+
499
+ PixelResult latLngToPixel(double lat, double lon, int zoom) {
500
+ double n = std::pow(2.0, zoom) * 256.0; // 假设瓦片大小为 256x256
501
+ double x = (lon + 180.0) / 360.0 * n;
502
+ double latRad = geo_toRadians(lat);
503
+ double y = (1.0 - std::asinh(std::tan(latRad)) / kPi) / 2.0 * n;
504
+ return {x, y};
505
+ }
506
+
507
+ GeoPoint pixelToLatLng(double x, double y, int zoom) {
508
+ double n = std::pow(2.0, zoom) * 256.0;
509
+ double lon = x / n * 360.0 - 180.0;
510
+ double latRad = std::atan(std::sinh(kPi * (1.0 - 2.0 * y / n)));
511
+ double lat = latRad * kRadiansToDegrees;
512
+ return {lat, lon};
513
+ }
514
+
515
+ // --- 批量地理围栏与热力图 ---
516
+
517
+ int findPointInPolygons(double pointLat, double pointLon, const std::vector<std::vector<GeoPoint>>& polygons) {
518
+ for (size_t i = 0; i < polygons.size(); ++i) {
519
+ if (isPointInPolygon(pointLat, pointLon, polygons[i])) {
520
+ return static_cast<int>(i);
521
+ }
522
+ }
523
+ return -1;
524
+ }
525
+
526
+ std::vector<HeatmapGridCell> generateHeatmapGrid(const std::vector<HeatmapPoint>& points, double gridSizeMeters) {
527
+ if (points.empty()) return {};
528
+
529
+ // 1. 计算范围
530
+ double minLat = 90.0, maxLat = -90.0, minLon = 180.0, maxLon = -180.0;
531
+ for (const auto& p : points) {
532
+ if (p.lat < minLat) minLat = p.lat;
533
+ if (p.lat > maxLat) maxLat = p.lat;
534
+ if (p.lon < minLon) minLon = p.lon;
535
+ if (p.lon > maxLon) maxLon = p.lon;
536
+ }
537
+
538
+ // 2. 将米转换为大概的经纬度步长
539
+ // (这是一个近似值,但在热力图分桶中通常足够)
540
+ double latDegreeDist = 111320.0; // 每纬度度数大约 111km
541
+ double lonDegreeDist = 111320.0 * std::cos(geo_toRadians((minLat + maxLat) / 2.0));
542
+
543
+ double latStep = gridSizeMeters / latDegreeDist;
544
+ double lonStep = gridSizeMeters / lonDegreeDist;
545
+
546
+ if (latStep <= 0 || lonStep <= 0) return {};
547
+
548
+ // 3. 将点分配到网格中
549
+ std::map<std::pair<int, int>, double> grid;
550
+ for (const auto& p : points) {
551
+ int latIdx = static_cast<int>((p.lat - minLat) / latStep);
552
+ int lonIdx = static_cast<int>((p.lon - minLon) / lonStep);
553
+ grid[{latIdx, lonIdx}] += p.weight;
554
+ }
555
+
556
+ // 4. 创建结果单元格
557
+ std::vector<HeatmapGridCell> cells;
558
+ cells.reserve(grid.size());
559
+ for (const auto& entry : grid) {
560
+ int latIdx = entry.first.first;
561
+ int lonIdx = entry.first.second;
562
+ double intensity = entry.second;
563
+
564
+ cells.push_back({
565
+ minLat + (latIdx + 0.5) * latStep,
566
+ minLon + (lonIdx + 0.5) * lonStep,
567
+ intensity
568
+ });
569
+ }
570
+
571
+ return cells;
572
+ }
573
+
574
+ } // namespace gaodemap