expo-gaode-map-navigation 1.1.5-next.1 → 1.1.5-next.3

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 (136) hide show
  1. package/android/build.gradle +10 -0
  2. package/android/src/main/cpp/CMakeLists.txt +24 -0
  3. package/android/src/main/cpp/cluster_jni.cpp +848 -0
  4. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapModule.kt +616 -92
  5. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapOfflineModule.kt +493 -0
  6. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapView.kt +230 -14
  7. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapViewModule.kt +37 -27
  8. package/android/src/main/java/expo/modules/gaodemap/map/MapPreloadManager.kt +494 -0
  9. package/android/src/main/java/expo/modules/gaodemap/map/companion/BitmapDescriptorCache.kt +30 -0
  10. package/android/src/main/java/expo/modules/gaodemap/map/companion/IconBitmapCache.kt +37 -0
  11. package/android/src/main/java/expo/modules/gaodemap/map/managers/UIManager.kt +76 -0
  12. package/android/src/main/java/expo/modules/gaodemap/map/modules/LocationManager.kt +15 -3
  13. package/android/src/main/java/expo/modules/gaodemap/map/modules/SDKInitializer.kt +4 -59
  14. package/android/src/main/java/expo/modules/gaodemap/map/overlays/CircleView.kt +9 -12
  15. package/android/src/main/java/expo/modules/gaodemap/map/overlays/CircleViewModule.kt +5 -6
  16. package/android/src/main/java/expo/modules/gaodemap/map/overlays/ClusterView.kt +539 -66
  17. package/android/src/main/java/expo/modules/gaodemap/map/overlays/ClusterViewModule.kt +17 -1
  18. package/android/src/main/java/expo/modules/gaodemap/map/overlays/HeatMapView.kt +165 -33
  19. package/android/src/main/java/expo/modules/gaodemap/map/overlays/HeatMapViewModule.kt +15 -3
  20. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerView.kt +1249 -672
  21. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerViewModule.kt +40 -17
  22. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MultiPointView.kt +177 -22
  23. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MultiPointViewModule.kt +11 -3
  24. package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolygonView.kt +57 -14
  25. package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolygonViewModule.kt +9 -5
  26. package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolylineView.kt +90 -63
  27. package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolylineViewModule.kt +7 -3
  28. package/android/src/main/java/expo/modules/gaodemap/map/services/LocationForegroundService.kt +3 -2
  29. package/android/src/main/java/expo/modules/gaodemap/map/utils/BitmapDescriptorCache.kt +20 -0
  30. package/android/src/main/java/expo/modules/gaodemap/map/utils/ClusterNative.kt +13 -0
  31. package/android/src/main/java/expo/modules/gaodemap/map/utils/ColorParser.kt +20 -0
  32. package/android/src/main/java/expo/modules/gaodemap/map/utils/GeometryUtils.kt +515 -0
  33. package/android/src/main/java/expo/modules/gaodemap/map/utils/LatLngParser.kt +91 -0
  34. package/android/src/main/java/expo/modules/gaodemap/map/utils/PermissionHelper.kt +248 -0
  35. package/build/ExpoGaodeMapNaviView.d.ts +7 -7
  36. package/build/ExpoGaodeMapNaviView.js +8 -8
  37. package/build/ExpoGaodeMapNavigationModule.d.ts +2 -1
  38. package/build/index.d.ts +35 -33
  39. package/build/index.js +70 -106
  40. package/build/map/ExpoGaodeMapModule.d.ts +2 -201
  41. package/build/map/ExpoGaodeMapModule.js +584 -14
  42. package/build/map/ExpoGaodeMapOfflineModule.d.ts +139 -0
  43. package/build/map/ExpoGaodeMapOfflineModule.js +8 -0
  44. package/build/map/ExpoGaodeMapView.js +66 -58
  45. package/build/map/components/FoldableMapView.d.ts +38 -0
  46. package/build/map/components/FoldableMapView.js +209 -0
  47. package/build/map/components/MapContext.d.ts +12 -0
  48. package/build/map/components/MapContext.js +54 -0
  49. package/build/map/components/MapUI.d.ts +18 -0
  50. package/build/map/components/MapUI.js +29 -0
  51. package/build/map/components/overlays/Circle.js +34 -3
  52. package/build/map/components/overlays/Cluster.d.ts +3 -1
  53. package/build/map/components/overlays/Cluster.js +31 -2
  54. package/build/map/components/overlays/HeatMap.d.ts +3 -1
  55. package/build/map/components/overlays/HeatMap.js +33 -3
  56. package/build/map/components/overlays/Marker.d.ts +1 -1
  57. package/build/map/components/overlays/Marker.js +37 -32
  58. package/build/map/components/overlays/MultiPoint.js +1 -1
  59. package/build/map/components/overlays/Polygon.js +30 -3
  60. package/build/map/components/overlays/Polyline.js +36 -3
  61. package/build/map/index.d.ts +25 -5
  62. package/build/map/index.js +59 -18
  63. package/build/map/types/common.types.d.ts +40 -0
  64. package/build/map/types/common.types.js +0 -4
  65. package/build/map/types/index.d.ts +3 -2
  66. package/build/map/types/map-view.types.d.ts +108 -3
  67. package/build/map/types/native-module.types.d.ts +363 -0
  68. package/build/map/types/native-module.types.js +5 -0
  69. package/build/map/types/offline.types.d.ts +132 -0
  70. package/build/map/types/offline.types.js +5 -0
  71. package/build/map/types/overlays.types.d.ts +137 -24
  72. package/build/map/utils/ErrorHandler.d.ts +110 -0
  73. package/build/map/utils/ErrorHandler.js +421 -0
  74. package/build/map/utils/GeoUtils.d.ts +20 -0
  75. package/build/map/utils/GeoUtils.js +76 -0
  76. package/build/map/utils/OfflineMapManager.d.ts +148 -0
  77. package/build/map/utils/OfflineMapManager.js +217 -0
  78. package/build/map/utils/PermissionUtils.d.ts +91 -0
  79. package/build/map/utils/PermissionUtils.js +255 -0
  80. package/build/map/utils/PlatformDetector.d.ts +102 -0
  81. package/build/map/utils/PlatformDetector.js +186 -0
  82. package/build/types/index.d.ts +1 -0
  83. package/build/types/index.js +1 -0
  84. package/build/types/native-module.types.d.ts +69 -0
  85. package/build/types/native-module.types.js +2 -0
  86. package/build/types/naviview.types.d.ts +1 -1
  87. package/expo-module.config.json +12 -10
  88. package/ios/ExpoGaodeMapNavigation.podspec +9 -0
  89. package/ios/map/ExpoGaodeMapModule.swift +485 -75
  90. package/ios/map/ExpoGaodeMapOfflineModule.swift +479 -0
  91. package/ios/map/ExpoGaodeMapView.swift +611 -62
  92. package/ios/map/ExpoGaodeMapViewModule.swift +48 -26
  93. package/ios/map/MapPreloadManager.swift +348 -0
  94. package/ios/map/cpp/ClusterEngine.cpp +110 -0
  95. package/ios/map/cpp/ClusterEngine.hpp +20 -0
  96. package/ios/map/cpp/ColorParser.cpp +135 -0
  97. package/ios/map/cpp/ColorParser.hpp +14 -0
  98. package/ios/map/cpp/GeometryEngine.cpp +574 -0
  99. package/ios/map/cpp/GeometryEngine.hpp +159 -0
  100. package/ios/map/cpp/QuadTree.cpp +92 -0
  101. package/ios/map/cpp/QuadTree.hpp +42 -0
  102. package/ios/map/cpp/README.md +55 -0
  103. package/ios/map/cpp/tests/benchmark_js.js +41 -0
  104. package/ios/map/cpp/tests/run.sh +17 -0
  105. package/ios/map/cpp/tests/test_main.cpp +276 -0
  106. package/ios/map/managers/UIManager.swift +72 -1
  107. package/ios/map/modules/LocationManager.swift +114 -165
  108. package/ios/map/overlays/CircleView.swift +16 -32
  109. package/ios/map/overlays/CircleViewModule.swift +12 -12
  110. package/ios/map/overlays/ClusterAnnotation.swift +32 -0
  111. package/ios/map/overlays/ClusterView.swift +331 -45
  112. package/ios/map/overlays/ClusterViewModule.swift +20 -6
  113. package/ios/map/overlays/HeatMapView.swift +135 -32
  114. package/ios/map/overlays/HeatMapViewModule.swift +20 -8
  115. package/ios/map/overlays/MarkerView.swift +613 -130
  116. package/ios/map/overlays/MarkerViewModule.swift +38 -18
  117. package/ios/map/overlays/MultiPointView.swift +168 -10
  118. package/ios/map/overlays/MultiPointViewModule.swift +27 -5
  119. package/ios/map/overlays/PolygonView.swift +62 -23
  120. package/ios/map/overlays/PolygonViewModule.swift +18 -12
  121. package/ios/map/overlays/PolylineView.swift +21 -13
  122. package/ios/map/overlays/PolylineViewModule.swift +18 -12
  123. package/ios/map/utils/ClusterNative.h +96 -0
  124. package/ios/map/utils/ClusterNative.mm +377 -0
  125. package/ios/map/utils/ColorParser.swift +12 -1
  126. package/ios/map/utils/CppBridging.mm +13 -0
  127. package/ios/map/utils/GeometryUtils.swift +34 -0
  128. package/ios/map/utils/LatLngParser.swift +87 -0
  129. package/ios/map/utils/PermissionManager.swift +135 -6
  130. package/package.json +1 -1
  131. package/build/map/ExpoGaodeMap.types.d.ts +0 -41
  132. package/build/map/ExpoGaodeMap.types.js +0 -24
  133. package/build/map/utils/EventManager.d.ts +0 -10
  134. package/build/map/utils/EventManager.js +0 -26
  135. package/build/map/utils/ModuleLoader.d.ts +0 -73
  136. package/build/map/utils/ModuleLoader.js +0 -112
@@ -0,0 +1,159 @@
1
+ #pragma once
2
+
3
+ #include <vector>
4
+ #include <string>
5
+
6
+ namespace gaodemap {
7
+
8
+ struct GeoPoint {
9
+ double lat;
10
+ double lon;
11
+ };
12
+
13
+ double calculateDistance(double lat1, double lon1, double lat2, double lon2);
14
+ bool isPointInCircle(double pointLat, double pointLon, double centerLat, double centerLon, double radiusMeters);
15
+ bool isPointInPolygon(double pointLat, double pointLon, const std::vector<GeoPoint>& polygon);
16
+ double calculatePolygonArea(const std::vector<GeoPoint>& polygon);
17
+ double calculateRectangleArea(double swLat, double swLon, double neLat, double neLon);
18
+
19
+ /**
20
+ * Ramer-Douglas-Peucker 轨迹抽稀算法
21
+ * @param points 原始轨迹点
22
+ * @param toleranceMeters 允许的误差(米),值越大点越少
23
+ * @return 简化后的轨迹点
24
+ */
25
+ std::vector<GeoPoint> simplifyPolyline(const std::vector<GeoPoint>& points, double toleranceMeters);
26
+
27
+ /**
28
+ * 计算路径总长度(米)
29
+ */
30
+ double calculatePathLength(const std::vector<GeoPoint>& points);
31
+
32
+ /**
33
+ * 获取路径上指定距离的点和方向
34
+ * @param points 路径点
35
+ * @param distanceMeters 距离起点的米数
36
+ * @param outLat 输出纬度
37
+ * @param outLon 输出经度
38
+ * @param outAngle 输出方向角度 (0-360)
39
+ * @return 是否成功找到点
40
+ */
41
+ bool getPointAtDistance(const std::vector<GeoPoint>& points, double distanceMeters, double* outLat, double* outLon, double* outAngle);
42
+
43
+ // Result structure for nearest point calculation
44
+ struct NearestPointResult {
45
+ double latitude;
46
+ double longitude;
47
+ int index; // Index of the point in the path that starts the segment (or the point itself)
48
+ double distanceMeters;
49
+ };
50
+
51
+ // Find the nearest point on the path to a target point
52
+ // Returns the nearest point on the polyline segments
53
+ NearestPointResult getNearestPointOnPath(const std::vector<GeoPoint>& path, const GeoPoint& target);
54
+
55
+ /**
56
+ * 计算多边形的质心
57
+ * @param polygon 多边形点集
58
+ * @return 质心坐标
59
+ */
60
+ GeoPoint calculateCentroid(const std::vector<GeoPoint>& polygon);
61
+
62
+ /**
63
+ * GeoHash 编码
64
+ * @param lat 纬度
65
+ * @param lon 经度
66
+ * @param precision 精度 (1-12)
67
+ * @return GeoHash 字符串
68
+ */
69
+ std::string encodeGeoHash(double lat, double lon, int precision);
70
+
71
+ /**
72
+ * 解析高德地图 API 返回的 Polyline 字符串
73
+ * 格式: "lng,lat;lng,lat;..."
74
+ * @param polylineStr 高德原始 polyline 字符串
75
+ * @return 解析后的点集
76
+ */
77
+ std::vector<GeoPoint> parsePolyline(const std::string& polylineStr);
78
+
79
+ struct PathBounds {
80
+ double north;
81
+ double south;
82
+ double east;
83
+ double west;
84
+ double centerLat;
85
+ double centerLon;
86
+ };
87
+
88
+ /**
89
+ * 计算路径的边界和中心点
90
+ * @param points 路径点
91
+ * @return 边界信息
92
+ */
93
+ PathBounds calculatePathBounds(const std::vector<GeoPoint>& points);
94
+
95
+ // --- 瓦片与坐标转换 ---
96
+
97
+ struct TileResult {
98
+ int x;
99
+ int y;
100
+ int z;
101
+ };
102
+
103
+ struct PixelResult {
104
+ double x;
105
+ double y;
106
+ };
107
+
108
+ /**
109
+ * 经纬度转瓦片坐标
110
+ */
111
+ TileResult latLngToTile(double lat, double lon, int zoom);
112
+
113
+ /**
114
+ * 瓦片坐标转经纬度
115
+ */
116
+ GeoPoint tileToLatLng(int x, int y, int zoom);
117
+
118
+ /**
119
+ * 经纬度转像素坐标
120
+ */
121
+ PixelResult latLngToPixel(double lat, double lon, int zoom);
122
+
123
+ /**
124
+ * 像素坐标转经纬度
125
+ */
126
+ GeoPoint pixelToLatLng(double x, double y, int zoom);
127
+
128
+ // --- 批量地理围栏与热力图 ---
129
+
130
+ /**
131
+ * 批量判断点是否在多个多边形内 (优化版)
132
+ * @param pointLat 点纬度
133
+ * @param pointLon 点经度
134
+ * @param polygons 多个多边形的集合
135
+ * @return 所在的第一个多边形的索引,若不在任何多边形内返回 -1
136
+ */
137
+ int findPointInPolygons(double pointLat, double pointLon, const std::vector<std::vector<GeoPoint>>& polygons);
138
+
139
+ struct HeatmapPoint {
140
+ double lat;
141
+ double lon;
142
+ double weight;
143
+ };
144
+
145
+ struct HeatmapGridCell {
146
+ double lat;
147
+ double lon;
148
+ double intensity;
149
+ };
150
+
151
+ /**
152
+ * 生成热力图网格数据
153
+ * @param points 原始点集
154
+ * @param gridSizeMeters 网格大小(米)
155
+ * @return 网格中心点及其强度
156
+ */
157
+ std::vector<HeatmapGridCell> generateHeatmapGrid(const std::vector<HeatmapPoint>& points, double gridSizeMeters);
158
+
159
+ } // namespace gaodemap
@@ -0,0 +1,92 @@
1
+ #include "QuadTree.hpp"
2
+
3
+ namespace gaodemap {
4
+
5
+ bool BoundingBox::contains(double lat, double lon) const {
6
+ return lat >= minLat && lat <= maxLat && lon >= minLon && lon <= maxLon;
7
+ }
8
+
9
+ bool BoundingBox::intersects(const BoundingBox& other) const {
10
+ return !(other.minLat > maxLat || other.maxLat < minLat ||
11
+ other.minLon > maxLon || other.maxLon < minLon);
12
+ }
13
+
14
+ QuadTree::QuadTree(const BoundingBox& bounds, int capacity)
15
+ : bounds(bounds), capacity(capacity) {}
16
+
17
+ bool QuadTree::insert(const ClusterPoint& point) {
18
+ if (!bounds.contains(point.lat, point.lon)) {
19
+ return false;
20
+ }
21
+
22
+ if (points.size() < capacity && !subdivided) {
23
+ points.push_back(point);
24
+ return true;
25
+ }
26
+
27
+ if (!subdivided) {
28
+ subdivide();
29
+ }
30
+
31
+ if (northWest->insert(point)) return true;
32
+ if (northEast->insert(point)) return true;
33
+ if (southWest->insert(point)) return true;
34
+ if (southEast->insert(point)) return true;
35
+
36
+ // Point might be exactly on the boundary of the QuadTree but not accepted by children?
37
+ // In our logic, children cover the full range of the parent.
38
+ // However, floating point precision might be tricky.
39
+ // As a fallback, if we are subdivided but children didn't take it (rare),
40
+ // we can keep it here or force insert.
41
+ // Given the subdivision logic, it should cover all space.
42
+ return false;
43
+ }
44
+
45
+ void QuadTree::subdivide() {
46
+ double midLat = (bounds.minLat + bounds.maxLat) / 2.0;
47
+ double midLon = (bounds.minLon + bounds.maxLon) / 2.0;
48
+
49
+ northWest = std::make_unique<QuadTree>(BoundingBox{midLat, bounds.minLon, bounds.maxLat, midLon}, capacity);
50
+ northEast = std::make_unique<QuadTree>(BoundingBox{midLat, midLon, bounds.maxLat, bounds.maxLon}, capacity);
51
+ southWest = std::make_unique<QuadTree>(BoundingBox{bounds.minLat, bounds.minLon, midLat, midLon}, capacity);
52
+ southEast = std::make_unique<QuadTree>(BoundingBox{bounds.minLat, midLon, midLat, bounds.maxLon}, capacity);
53
+
54
+ subdivided = true;
55
+
56
+ // Redistribute existing points
57
+ for (const auto& p : points) {
58
+ northWest->insert(p) || northEast->insert(p) ||
59
+ southWest->insert(p) || southEast->insert(p);
60
+ }
61
+ points.clear();
62
+ }
63
+
64
+ void QuadTree::query(const BoundingBox& range, std::vector<ClusterPoint>& found) const {
65
+ if (!bounds.intersects(range)) {
66
+ return;
67
+ }
68
+
69
+ for (const auto& p : points) {
70
+ if (range.contains(p.lat, p.lon)) {
71
+ found.push_back(p);
72
+ }
73
+ }
74
+
75
+ if (subdivided) {
76
+ northWest->query(range, found);
77
+ northEast->query(range, found);
78
+ southWest->query(range, found);
79
+ southEast->query(range, found);
80
+ }
81
+ }
82
+
83
+ void QuadTree::clear() {
84
+ points.clear();
85
+ northWest.reset();
86
+ northEast.reset();
87
+ southWest.reset();
88
+ southEast.reset();
89
+ subdivided = false;
90
+ }
91
+
92
+ }
@@ -0,0 +1,42 @@
1
+ #pragma once
2
+
3
+ #include <vector>
4
+ #include <memory>
5
+ #include "ClusterEngine.hpp"
6
+
7
+ namespace gaodemap {
8
+
9
+ struct BoundingBox {
10
+ double minLat;
11
+ double minLon;
12
+ double maxLat;
13
+ double maxLon;
14
+
15
+ bool contains(double lat, double lon) const;
16
+ bool intersects(const BoundingBox& other) const;
17
+ };
18
+
19
+ class QuadTree {
20
+ public:
21
+ QuadTree(const BoundingBox& bounds, int capacity = 20);
22
+ ~QuadTree() = default;
23
+
24
+ bool insert(const ClusterPoint& point);
25
+ void query(const BoundingBox& range, std::vector<ClusterPoint>& found) const;
26
+ void clear();
27
+
28
+ private:
29
+ BoundingBox bounds;
30
+ int capacity;
31
+ std::vector<ClusterPoint> points;
32
+ bool subdivided = false;
33
+
34
+ std::unique_ptr<QuadTree> northWest;
35
+ std::unique_ptr<QuadTree> northEast;
36
+ std::unique_ptr<QuadTree> southWest;
37
+ std::unique_ptr<QuadTree> southEast;
38
+
39
+ void subdivide();
40
+ };
41
+
42
+ }
@@ -0,0 +1,55 @@
1
+ # C++ 共享核心库 (Shared Core)
2
+
3
+ 该目录包含了 `expo-gaode-map` 跨平台核心逻辑的 C++ 实现。这些代码被 Android 和 iOS 平台共享,以确保计算逻辑的一致性并提升高性能计算场景(如点聚合、几何计算)的处理速度。
4
+
5
+ ## 模块说明
6
+
7
+ ### 1. GeometryEngine (几何引擎)
8
+ [GeometryEngine.hpp](file:///Users/wangqiang/Desktop/expo-gaode-map/packages/core/shared/cpp/GeometryEngine.hpp)
9
+ 提供地理空间相关的数学计算:
10
+ - **距离计算**: 基于 Haversine 公式计算经纬度点之间的球面距离。
11
+ - **点位判断**: 判断点是否在多边形 (Point-in-Polygon) 或圆形内。
12
+ - **面积计算**: 计算多边形或矩形的地理面积。
13
+ - **轨迹处理**:
14
+ - **抽稀算法**: 实现 Ramer-Douglas-Peucker 算法,用于简化复杂的折线轨迹。
15
+ - **路径长度**: 计算折线段的总长度。
16
+ - **路径插值**: 获取路径上指定距离的点坐标及其切线方向。
17
+ - **GeoHash**: 支持经纬度到 GeoHash 字符串的编码。
18
+ - **质心计算**: 计算多边形的几何质心。
19
+
20
+ ### 2. ClusterEngine (点聚合引擎)
21
+ [ClusterEngine.hpp](file:///Users/wangqiang/Desktop/expo-gaode-map/packages/core/shared/cpp/ClusterEngine.hpp)
22
+ 负责大规模地图标记点的聚合计算:
23
+ - 使用 **QuadTree** 进行空间索引优化。
24
+ - 支持基于半径的聚合逻辑。
25
+ - 高性能处理,适用于数千甚至数万个点的实时聚合。
26
+
27
+ ### 3. QuadTree (四叉树)
28
+ [QuadTree.hpp](file:///Users/wangqiang/Desktop/expo-gaode-map/packages/core/shared/cpp/QuadTree.hpp)
29
+ 为地理坐标提供高效的空间索引结构:
30
+ - **空间分割**: 自动将空间划分为四个象限。
31
+ - **范围查询**: 快速检索指定矩形区域内的所有点位。
32
+ - 被 `ClusterEngine` 用于加速近邻点搜索。
33
+
34
+ ### 4. ColorParser (颜色解析器)
35
+ [ColorParser.hpp](file:///Users/wangqiang/Desktop/expo-gaode-map/packages/core/shared/cpp/ColorParser.hpp)
36
+ 跨平台的颜色字符串解析工具:
37
+ - 支持 **Hex** 格式 (如 `#RRGGBB`, `#AARRGGBB`, `#RGB`)。
38
+ - 支持 **RGB/RGBA** 函数格式 (如 `rgba(255, 0, 0, 0.5)`)。
39
+ - 支持 **命名颜色** (如 `red`, `blue`, `transparent`)。
40
+ - 统一输出为 `0xAARRGGBB` 格式的 32 位整数。
41
+
42
+ ## 测试
43
+
44
+ 测试用例位于 `tests/` 目录。
45
+
46
+ ### 运行测试
47
+ 在 macOS/Linux 环境下,可以通过以下命令运行 C++ 单元测试:
48
+
49
+ ```bash
50
+ cd tests
51
+ chmod +x run.sh
52
+ ./run.sh
53
+ ```
54
+
55
+ 该脚本会使用 `clang++` 编译源代码并运行生成的测试二进制文件,验证各核心模块的逻辑准确性。
@@ -0,0 +1,41 @@
1
+
2
+ const parsePolylineJS = (polylineStr) => {
3
+ if (!polylineStr || typeof polylineStr !== 'string') return [];
4
+ try {
5
+ const points = polylineStr.split(';');
6
+ return points.map(point => {
7
+ const [lng, lat] = point.split(',').map(Number);
8
+ return { latitude: lat, longitude: lng };
9
+ }).filter(p => !isNaN(p.latitude) && !isNaN(p.longitude));
10
+ } catch (error) {
11
+ return [];
12
+ }
13
+ };
14
+
15
+ const benchmark = () => {
16
+ console.log("Running JS benchmark (10,000 points)...");
17
+
18
+ // 生成 10,000 个点的测试字符串
19
+ let largePoly = "";
20
+ for (let i = 0; i < 10000; i++) {
21
+ largePoly += "116.4074,39.9042;";
22
+ }
23
+
24
+ const start = Date.now();
25
+
26
+ // 运行 100 次以获得更准确的平均值
27
+ for (let i = 0; i < 100; i++) {
28
+ const result = parsePolylineJS(largePoly);
29
+ if (result.length !== 10000) {
30
+ console.error(`Error: Expected 10000 points, got ${result.length}`);
31
+ }
32
+ }
33
+
34
+ const end = Date.now();
35
+ const totalTime = end - start;
36
+
37
+ console.log(`Total time for 100 iterations: ${totalTime} ms`);
38
+ console.log(`Average time per parse (10,000 points): ${totalTime / 100} ms`);
39
+ };
40
+
41
+ benchmark();
@@ -0,0 +1,17 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ # Compile the test
5
+ clang++ -std=c++17 \
6
+ test_main.cpp \
7
+ ../GeometryEngine.cpp \
8
+ ../ColorParser.cpp \
9
+ ../ClusterEngine.cpp \
10
+ ../QuadTree.cpp \
11
+ -o test_runner
12
+
13
+ # Run the test
14
+ ./test_runner
15
+
16
+ # Clean up
17
+ rm test_runner
@@ -0,0 +1,276 @@
1
+ #include <iostream>
2
+ #include <vector>
3
+ #include <cmath>
4
+ #include <cassert>
5
+ #include <iomanip>
6
+ #include <algorithm>
7
+ #include <chrono>
8
+
9
+ #include "../GeometryEngine.hpp"
10
+ #include "../ColorParser.hpp"
11
+ #include "../QuadTree.hpp"
12
+ #include "../ClusterEngine.hpp"
13
+
14
+ using namespace gaodemap;
15
+
16
+ // Helper to compare doubles with epsilon
17
+ bool approxEqual(double a, double b, double epsilon = 1e-6) {
18
+ return std::abs(a - b) < epsilon;
19
+ }
20
+
21
+ void testDistance() {
22
+ std::cout << "Running testDistance..." << std::endl;
23
+ // Beijing
24
+ double lat1 = 39.9042;
25
+ double lon1 = 116.4074;
26
+ // ~1km East
27
+ double lat2 = 39.9042;
28
+ double lon2 = 116.4191;
29
+
30
+ double dist = calculateDistance(lat1, lon1, lat2, lon2);
31
+ std::cout << "Distance: " << dist << " meters" << std::endl;
32
+
33
+ // Expect roughly 1000m.
34
+ assert(dist > 990 && dist < 1010);
35
+
36
+ // Same point should be 0
37
+ assert(approxEqual(calculateDistance(lat1, lon1, lat1, lon1), 0.0));
38
+
39
+ std::cout << "PASSED" << std::endl;
40
+ }
41
+
42
+ void testColorParser() {
43
+ std::cout << "Running testColorParser..." << std::endl;
44
+
45
+ // Red
46
+ assert(parseColor("red") == 0xFFFF0000);
47
+ assert(parseColor("#FF0000") == 0xFFFF0000);
48
+ assert(parseColor("#ff0000") == 0xFFFF0000);
49
+
50
+ // Blue
51
+ assert(parseColor("blue") == 0xFF0000FF);
52
+
53
+ // RGB
54
+ assert(parseColor("rgb(0, 255, 0)") == 0xFF00FF00);
55
+
56
+ // RGBA
57
+ uint32_t rgba = parseColor("rgba(255, 0, 0, 0.5)");
58
+ assert((rgba & 0x00FFFFFF) == 0x00FF0000);
59
+ uint32_t alpha = (rgba >> 24) & 0xFF;
60
+ assert(alpha >= 127 && alpha <= 128);
61
+
62
+ // Invalid colors
63
+ assert(parseColor("notacolor") == 0);
64
+ assert(parseColor("#XYZ") == 0);
65
+ assert(parseColor("") == 0);
66
+
67
+ std::cout << "PASSED" << std::endl;
68
+ }
69
+
70
+ void testPointInPolygon() {
71
+ std::cout << "Running testPointInPolygon..." << std::endl;
72
+
73
+ // Simple square (0,0) to (1,1)
74
+ std::vector<GeoPoint> polygon = {
75
+ {0, 0},
76
+ {1, 0},
77
+ {1, 1},
78
+ {0, 1}
79
+ };
80
+
81
+ assert(isPointInPolygon(0.5, 0.5, polygon) == true);
82
+ assert(isPointInPolygon(1.5, 0.5, polygon) == false);
83
+ assert(isPointInPolygon(-0.1, 0.5, polygon) == false);
84
+
85
+ // Triangle
86
+ std::vector<GeoPoint> triangle = {
87
+ {0, 0},
88
+ {2, 0},
89
+ {1, 2}
90
+ };
91
+ assert(isPointInPolygon(1, 1, triangle) == true);
92
+ assert(isPointInPolygon(1, 2.1, triangle) == false);
93
+
94
+ std::cout << "PASSED" << std::endl;
95
+ }
96
+
97
+ void testGeometryEngineExtended() {
98
+ std::cout << "Running testGeometryEngineExtended..." << std::endl;
99
+
100
+ // 1. testPointInCircle
101
+ assert(isPointInCircle(39.9, 116.4, 39.9, 116.4, 100) == true);
102
+ assert(isPointInCircle(40.0, 116.4, 39.9, 116.4, 1000) == false); // ~11km diff
103
+
104
+ // 2. testArea
105
+ std::vector<GeoPoint> rect = {
106
+ {0, 0}, {0, 1}, {1, 1}, {1, 0}
107
+ };
108
+ double area = calculatePolygonArea(rect);
109
+ std::cout << "Polygon Area: " << area << " m^2" << std::endl;
110
+ assert(area > 0);
111
+
112
+ double rectArea = calculateRectangleArea(0, 0, 1, 1);
113
+ assert(approxEqual(area, rectArea));
114
+
115
+ // 3. simplifyPolyline
116
+ std::vector<GeoPoint> line = {
117
+ {0, 0}, {0.1, 0.0001}, {0.2, 0}, {0.3, 0.0001}, {0.4, 0}
118
+ };
119
+ auto simplified = simplifyPolyline(line, 1000); // 1km tolerance
120
+ assert(simplified.size() < line.size());
121
+ assert(simplified.front().lat == 0);
122
+ assert(simplified.back().lat == 0.4);
123
+
124
+ // 4. calculatePathLength
125
+ double length = calculatePathLength({{0,0}, {0,1}});
126
+ assert(length > 111000 && length < 112000); // 1 degree lat is ~111km
127
+
128
+ // 5. getPointAtDistance
129
+ double outLat, outLon, outAngle;
130
+ bool found = getPointAtDistance({{0,0}, {1,0}}, 55500, &outLat, &outLon, &outAngle);
131
+ assert(found);
132
+ assert(outLat > 0.4 && outLat < 0.6);
133
+ assert(approxEqual(outLon, 0.0));
134
+
135
+ // 6. getNearestPointOnPath
136
+ std::vector<GeoPoint> path = {{0,0}, {2,0}};
137
+ GeoPoint target = {1, 1};
138
+ auto nearest = getNearestPointOnPath(path, target);
139
+ assert(approxEqual(nearest.latitude, 1.0));
140
+ assert(approxEqual(nearest.longitude, 0.0));
141
+ assert(nearest.index == 0);
142
+
143
+ // 7. calculateCentroid
144
+ std::vector<GeoPoint> square = {{0,0}, {1,0}, {1,1}, {0,1}};
145
+ auto centroid = calculateCentroid(square);
146
+ assert(approxEqual(centroid.lat, 0.5));
147
+ assert(approxEqual(centroid.lon, 0.5));
148
+
149
+ // 8. encodeGeoHash
150
+ std::string hash = encodeGeoHash(39.9042, 116.4074, 5);
151
+ assert(hash.length() == 5);
152
+ assert(hash == "wx4g0"); // Common geohash for Beijing center
153
+
154
+ // 9. parsePolyline
155
+ std::string polyStr = "116.4074,39.9042;116.4191,39.9042";
156
+ auto parsed = parsePolyline(polyStr);
157
+ assert(parsed.size() == 2);
158
+ assert(approxEqual(parsed[0].lat, 39.9042));
159
+ assert(approxEqual(parsed[0].lon, 116.4074));
160
+ assert(approxEqual(parsed[1].lat, 39.9042));
161
+ assert(approxEqual(parsed[1].lon, 116.4191));
162
+
163
+ // Test empty
164
+ assert(parsePolyline("").empty());
165
+ // Test invalid
166
+ assert(parsePolyline("invalid").empty());
167
+ // Test trailing semicolon
168
+ assert(parsePolyline("116.4074,39.9042;").size() == 1);
169
+
170
+ std::cout << "PASSED" << std::endl;
171
+ }
172
+
173
+ void benchmarkParsePolyline() {
174
+ std::cout << "Running benchmarkParsePolyline (10,000 points)..." << std::endl;
175
+
176
+ // 生成 10,000 个点的测试字符串
177
+ std::string largePoly;
178
+ largePoly.reserve(300000); // 预分配约 300KB
179
+ for (int i = 0; i < 10000; ++i) {
180
+ largePoly += "116.4074,39.9042;";
181
+ }
182
+
183
+ auto start = std::chrono::high_resolution_clock::now();
184
+
185
+ // 运行 100 次以获得更准确的平均值
186
+ for (int i = 0; i < 100; ++i) {
187
+ auto result = parsePolyline(largePoly);
188
+ if (result.size() != 10000) {
189
+ std::cerr << "Error: Expected 10000 points, got " << result.size() << std::endl;
190
+ }
191
+ }
192
+
193
+ auto end = std::chrono::high_resolution_clock::now();
194
+ std::chrono::duration<double, std::milli> duration = end - start;
195
+
196
+ std::cout << "Total time for 100 iterations: " << duration.count() << " ms" << std::endl;
197
+ std::cout << "Average time per parse (10,000 points): " << duration.count() / 100.0 << " ms" << std::endl;
198
+ std::cout << "PASSED" << std::endl;
199
+ }
200
+
201
+ void testQuadTree() {
202
+ std::cout << "Running testQuadTree..." << std::endl;
203
+
204
+ BoundingBox bounds{0, 0, 10, 10};
205
+ QuadTree tree(bounds, 2); // Small capacity to force subdivision
206
+
207
+ tree.insert({1, 1, 1});
208
+ tree.insert({2, 2, 2});
209
+ tree.insert({8, 8, 3});
210
+ tree.insert({9, 9, 4});
211
+
212
+ std::vector<ClusterPoint> found;
213
+ tree.query({0, 0, 3, 3}, found);
214
+ assert(found.size() == 2);
215
+
216
+ found.clear();
217
+ tree.query({7, 7, 10, 10}, found);
218
+ assert(found.size() == 2);
219
+
220
+ found.clear();
221
+ tree.query({4, 4, 6, 6}, found);
222
+ assert(found.size() == 0);
223
+
224
+ std::cout << "PASSED" << std::endl;
225
+ }
226
+
227
+ void testClusterEngine() {
228
+ std::cout << "Running testClusterEngine..." << std::endl;
229
+
230
+ std::vector<ClusterPoint> points = {
231
+ {39.9042, 116.4074, 0}, // Beijing 1
232
+ {39.9043, 116.4075, 1}, // Beijing 2 (very close)
233
+ {31.2304, 121.4737, 2}, // Shanghai 1
234
+ {31.2305, 121.4738, 3} // Shanghai 2 (very close)
235
+ };
236
+
237
+ // Cluster with 1km radius
238
+ auto clusters = clusterPoints(points, 1000);
239
+
240
+ // Should have 2 clusters
241
+ assert(clusters.size() == 2);
242
+
243
+ for (const auto& c : clusters) {
244
+ assert(c.indices.size() == 2);
245
+ }
246
+
247
+ // Cluster with 1m radius (should be 4 clusters)
248
+ auto clustersSmall = clusterPoints(points, 1);
249
+ assert(clustersSmall.size() == 4);
250
+
251
+ std::cout << "PASSED" << std::endl;
252
+ }
253
+
254
+ int main() {
255
+ std::cout << "========================================" << std::endl;
256
+ std::cout << "STARTING C++ SHARED CODE TESTS" << std::endl;
257
+ std::cout << "========================================" << std::endl;
258
+
259
+ try {
260
+ testDistance();
261
+ testColorParser();
262
+ testPointInPolygon();
263
+ testGeometryEngineExtended();
264
+ benchmarkParsePolyline();
265
+ testQuadTree();
266
+ testClusterEngine();
267
+
268
+ std::cout << "========================================" << std::endl;
269
+ std::cout << "ALL TESTS PASSED SUCCESSFULLY" << std::endl;
270
+ std::cout << "========================================" << std::endl;
271
+ return 0;
272
+ } catch (const std::exception& e) {
273
+ std::cerr << "TEST FAILED: " << e.what() << std::endl;
274
+ return 1;
275
+ }
276
+ }