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.
- package/android/build.gradle +10 -0
- package/android/src/main/cpp/CMakeLists.txt +24 -0
- package/android/src/main/cpp/cluster_jni.cpp +848 -0
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapModule.kt +616 -92
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapOfflineModule.kt +493 -0
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapView.kt +230 -14
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapViewModule.kt +37 -27
- package/android/src/main/java/expo/modules/gaodemap/map/MapPreloadManager.kt +494 -0
- package/android/src/main/java/expo/modules/gaodemap/map/companion/BitmapDescriptorCache.kt +30 -0
- package/android/src/main/java/expo/modules/gaodemap/map/companion/IconBitmapCache.kt +37 -0
- package/android/src/main/java/expo/modules/gaodemap/map/managers/UIManager.kt +76 -0
- package/android/src/main/java/expo/modules/gaodemap/map/modules/LocationManager.kt +15 -3
- package/android/src/main/java/expo/modules/gaodemap/map/modules/SDKInitializer.kt +4 -59
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/CircleView.kt +9 -12
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/CircleViewModule.kt +5 -6
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/ClusterView.kt +539 -66
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/ClusterViewModule.kt +17 -1
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/HeatMapView.kt +165 -33
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/HeatMapViewModule.kt +15 -3
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerView.kt +1249 -672
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerViewModule.kt +40 -17
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MultiPointView.kt +177 -22
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MultiPointViewModule.kt +11 -3
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolygonView.kt +57 -14
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolygonViewModule.kt +9 -5
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolylineView.kt +90 -63
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolylineViewModule.kt +7 -3
- package/android/src/main/java/expo/modules/gaodemap/map/services/LocationForegroundService.kt +3 -2
- package/android/src/main/java/expo/modules/gaodemap/map/utils/BitmapDescriptorCache.kt +20 -0
- package/android/src/main/java/expo/modules/gaodemap/map/utils/ClusterNative.kt +13 -0
- package/android/src/main/java/expo/modules/gaodemap/map/utils/ColorParser.kt +20 -0
- package/android/src/main/java/expo/modules/gaodemap/map/utils/GeometryUtils.kt +515 -0
- package/android/src/main/java/expo/modules/gaodemap/map/utils/LatLngParser.kt +91 -0
- package/android/src/main/java/expo/modules/gaodemap/map/utils/PermissionHelper.kt +248 -0
- package/build/ExpoGaodeMapNaviView.d.ts +7 -7
- package/build/ExpoGaodeMapNaviView.js +8 -8
- package/build/ExpoGaodeMapNavigationModule.d.ts +2 -1
- package/build/index.d.ts +35 -33
- package/build/index.js +70 -106
- package/build/map/ExpoGaodeMapModule.d.ts +2 -201
- package/build/map/ExpoGaodeMapModule.js +584 -14
- package/build/map/ExpoGaodeMapOfflineModule.d.ts +139 -0
- package/build/map/ExpoGaodeMapOfflineModule.js +8 -0
- package/build/map/ExpoGaodeMapView.js +66 -58
- package/build/map/components/FoldableMapView.d.ts +38 -0
- package/build/map/components/FoldableMapView.js +209 -0
- package/build/map/components/MapContext.d.ts +12 -0
- package/build/map/components/MapContext.js +54 -0
- package/build/map/components/MapUI.d.ts +18 -0
- package/build/map/components/MapUI.js +29 -0
- package/build/map/components/overlays/Circle.js +34 -3
- package/build/map/components/overlays/Cluster.d.ts +3 -1
- package/build/map/components/overlays/Cluster.js +31 -2
- package/build/map/components/overlays/HeatMap.d.ts +3 -1
- package/build/map/components/overlays/HeatMap.js +33 -3
- package/build/map/components/overlays/Marker.d.ts +1 -1
- package/build/map/components/overlays/Marker.js +37 -32
- package/build/map/components/overlays/MultiPoint.js +1 -1
- package/build/map/components/overlays/Polygon.js +30 -3
- package/build/map/components/overlays/Polyline.js +36 -3
- package/build/map/index.d.ts +25 -5
- package/build/map/index.js +59 -18
- package/build/map/types/common.types.d.ts +40 -0
- package/build/map/types/common.types.js +0 -4
- package/build/map/types/index.d.ts +3 -2
- package/build/map/types/map-view.types.d.ts +108 -3
- package/build/map/types/native-module.types.d.ts +363 -0
- package/build/map/types/native-module.types.js +5 -0
- package/build/map/types/offline.types.d.ts +132 -0
- package/build/map/types/offline.types.js +5 -0
- package/build/map/types/overlays.types.d.ts +137 -24
- package/build/map/utils/ErrorHandler.d.ts +110 -0
- package/build/map/utils/ErrorHandler.js +421 -0
- package/build/map/utils/GeoUtils.d.ts +20 -0
- package/build/map/utils/GeoUtils.js +76 -0
- package/build/map/utils/OfflineMapManager.d.ts +148 -0
- package/build/map/utils/OfflineMapManager.js +217 -0
- package/build/map/utils/PermissionUtils.d.ts +91 -0
- package/build/map/utils/PermissionUtils.js +255 -0
- package/build/map/utils/PlatformDetector.d.ts +102 -0
- package/build/map/utils/PlatformDetector.js +186 -0
- package/build/types/index.d.ts +1 -0
- package/build/types/index.js +1 -0
- package/build/types/native-module.types.d.ts +69 -0
- package/build/types/native-module.types.js +2 -0
- package/build/types/naviview.types.d.ts +1 -1
- package/expo-module.config.json +12 -10
- package/ios/ExpoGaodeMapNavigation.podspec +9 -0
- package/ios/map/ExpoGaodeMapModule.swift +485 -75
- package/ios/map/ExpoGaodeMapOfflineModule.swift +479 -0
- package/ios/map/ExpoGaodeMapView.swift +611 -62
- package/ios/map/ExpoGaodeMapViewModule.swift +48 -26
- package/ios/map/MapPreloadManager.swift +348 -0
- package/ios/map/cpp/ClusterEngine.cpp +110 -0
- package/ios/map/cpp/ClusterEngine.hpp +20 -0
- package/ios/map/cpp/ColorParser.cpp +135 -0
- package/ios/map/cpp/ColorParser.hpp +14 -0
- package/ios/map/cpp/GeometryEngine.cpp +574 -0
- package/ios/map/cpp/GeometryEngine.hpp +159 -0
- package/ios/map/cpp/QuadTree.cpp +92 -0
- package/ios/map/cpp/QuadTree.hpp +42 -0
- package/ios/map/cpp/README.md +55 -0
- package/ios/map/cpp/tests/benchmark_js.js +41 -0
- package/ios/map/cpp/tests/run.sh +17 -0
- package/ios/map/cpp/tests/test_main.cpp +276 -0
- package/ios/map/managers/UIManager.swift +72 -1
- package/ios/map/modules/LocationManager.swift +114 -165
- package/ios/map/overlays/CircleView.swift +16 -32
- package/ios/map/overlays/CircleViewModule.swift +12 -12
- package/ios/map/overlays/ClusterAnnotation.swift +32 -0
- package/ios/map/overlays/ClusterView.swift +331 -45
- package/ios/map/overlays/ClusterViewModule.swift +20 -6
- package/ios/map/overlays/HeatMapView.swift +135 -32
- package/ios/map/overlays/HeatMapViewModule.swift +20 -8
- package/ios/map/overlays/MarkerView.swift +613 -130
- package/ios/map/overlays/MarkerViewModule.swift +38 -18
- package/ios/map/overlays/MultiPointView.swift +168 -10
- package/ios/map/overlays/MultiPointViewModule.swift +27 -5
- package/ios/map/overlays/PolygonView.swift +62 -23
- package/ios/map/overlays/PolygonViewModule.swift +18 -12
- package/ios/map/overlays/PolylineView.swift +21 -13
- package/ios/map/overlays/PolylineViewModule.swift +18 -12
- package/ios/map/utils/ClusterNative.h +96 -0
- package/ios/map/utils/ClusterNative.mm +377 -0
- package/ios/map/utils/ColorParser.swift +12 -1
- package/ios/map/utils/CppBridging.mm +13 -0
- package/ios/map/utils/GeometryUtils.swift +34 -0
- package/ios/map/utils/LatLngParser.swift +87 -0
- package/ios/map/utils/PermissionManager.swift +135 -6
- package/package.json +1 -1
- package/build/map/ExpoGaodeMap.types.d.ts +0 -41
- package/build/map/ExpoGaodeMap.types.js +0 -24
- package/build/map/utils/EventManager.d.ts +0 -10
- package/build/map/utils/EventManager.js +0 -26
- package/build/map/utils/ModuleLoader.d.ts +0 -73
- 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
|
+
}
|