return true;
}
- //////////////////////////////////////GPS///////////////////////////////////////////////
- /*
- Latitude(纬度):范围:-90° 至 +90°(或南纬 90° 至北纬 90°);
- Longitude(经度):范围:-180° 至 +180°(或西经 180° 至东经 180°);
-
- -画布数据集最多是100个定位坐标,超过按比例进行拿取100个点位;
- -通过映射关系,将经纬度转换为400*400画布上的坐标;
- -但是不能靠近400,以免显示不全等问题,350*350;
- -如果处理负数,需要将其转换为正数,然后再进行映射?
- -如果有正数又有负数,应该怎么处理?
+//////////////////////////////////////GPS///////////////////////////////////////////////
+/*
+ Latitude(纬度):范围:-90° 至 +90°(或南纬 90° 至北纬 90°);
+ Longitude(经度):范围:-180° 至 +180°(或西经 180° 至东经 180°);
+
+ -画布数据集使用所有GPS定位坐标,不进行点位限制,确保绘制完整;
+ -通过映射关系,将经纬度转换为400*400画布上的坐标;
+ -但是不能靠近400,以免显示不全等问题,350*350;
+ -如果处理负数,需要将其转换为正数,然后再进行映射?
+ -如果有正数又有负数,应该怎么处理?
*/
static double getmin(double a, double b) { return a < b ? a : b; }
}
}
- // 改进的GetPointData函数(优化版)
- void TraingModel::GetPointData() {
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
- "Generating point data from %zu GPS points", gpsData.size());
-
- std::vector<GPSData> sampledData;
-
- // 处理GPS数据
- if (gpsData.size() > 100) {
- // 计算经纬度范围
- double minLat = 90.0, maxLat = -90.0;
- double minLon = 180.0, maxLon = -180.0;
-
- // 同时计算平均值用于判断数据分布
- double sumLat = 0, sumLon = 0;
-
- for (const auto &point : gpsData) {
- minLat = getmin(minLat, point.latitude);
- maxLat = getmax(maxLat, point.latitude);
- minLon = getmin(minLon, point.longitude);
- maxLon = getmax(maxLon, point.longitude);
- sumLat += point.latitude;
- sumLon += point.longitude;
- }
-
- // 计算经纬度范围和平均值
- double rangeLat = maxLat - minLat;
- double rangeLon = maxLon - minLon;
- double avgLat = sumLat / gpsData.size();
- double avgLon = sumLon / gpsData.size();
-
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
- "GPS range: lat=[%.6f, %.6f], lon=[%.6f, %.6f]", minLat,
- maxLat, minLon, maxLon);
-
- // 检查数据是否集中在极小区域或几乎相同
- const double MIN_SIGNIFICANT_RANGE = 0.01; // 根据实际情况调整
- bool isDataDegenerate =
- (rangeLat < MIN_SIGNIFICANT_RANGE && rangeLon < MIN_SIGNIFICANT_RANGE);
-
- if (isDataDegenerate) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "GPS data is degenerate (very small range)");
- // 使用均匀采样,避免算法失效
- double step = static_cast<double>(gpsData.size() - 1) / 99;
- for (int i = 0; i < 100; i++) {
- int index = static_cast<int>(i * step);
- sampledData.push_back(gpsData[index]);
- }
- } else {
- // 转换GPS坐标到屏幕坐标
- std::vector<Point> screenPoints;
- screenPoints.reserve(gpsData.size());
-
- for (const auto &point : gpsData) {
- double normLat = (point.latitude - minLat) / rangeLat;
- double normLon = (point.longitude - minLon) / rangeLon;
- screenPoints.push_back(
- {normLon * 300 + 50, (1.0 - normLat) * 300 + 50});
- }
-
- // 使用Douglas-Peucker算法简化轨迹
- std::vector<Point> simplifiedPoints;
- douglasPeucker(screenPoints, 0.5, simplifiedPoints);
-
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
- "After Douglas-Peucker: %zu points",
- simplifiedPoints.size());
-
- // 处理简化结果
- if (simplifiedPoints.size() <= 2) {
- WEARABLE_LOGE(
- WEARABLE_LOG_MODULE_APP,
- "Trajectory over-simplified! Using uniform sampling instead.");
- // 过度简化时回退到均匀采样
- double step = static_cast<double>(gpsData.size() - 1) / 99;
- for (int i = 0; i < 100; i++) {
- int index = static_cast<int>(i * step);
- sampledData.push_back(gpsData[index]);
- }
- } else if (simplifiedPoints.size() > 100) {
- // 简化后仍超过100点,再次均匀采样
- double step = static_cast<double>(simplifiedPoints.size() - 1) / 99;
- for (int i = 0; i < 100; i++) {
- int index = static_cast<int>(i * step);
- // 屏幕坐标反推回GPS坐标
- double normX = (simplifiedPoints[index].x - 50) / 300;
- double normY = (simplifiedPoints[index].y - 50) / 300;
- double lat = minLat + (1.0 - normY) * rangeLat;
- double lon = minLon + normX * rangeLon;
- sampledData.push_back({0, lat, lon});
- }
- } else {
- // 简化后点数量合适,直接使用
- for (const auto &point : simplifiedPoints) {
- double normX = (point.x - 50) / 300;
- double normY = (point.y - 50) / 300;
- double lat = minLat + (1.0 - normY) * rangeLat;
- double lon = minLon + normX * rangeLon;
- sampledData.push_back({0, lat, lon});
- }
- }
- }
- } else {
- // GPS点数不足100,直接使用全部数据
- sampledData = gpsData;
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "Using all %zu GPS points (no sampling)", sampledData.size());
- }
-
- // 映射到画布坐标
- canvasData.clear();
- canvasData.reserve(sampledData.size());
-
- // 如果没有采样数据,直接返回
- if (sampledData.empty()) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "No sampled data to map!");
- return;
- }
-
- // 基于采样数据重新计算经纬度范围
- double minLat = 90.0, maxLat = -90.0;
- double minLon = 180.0, maxLon = -180.0;
-
- for (const auto &point : sampledData) {
- minLat = getmin(minLat, point.latitude);
- maxLat = getmax(maxLat, point.latitude);
- minLon = getmin(minLon, point.longitude);
- maxLon = getmax(maxLon, point.longitude);
- }
-
- double rangeLat = maxLat - minLat;
- double rangeLon = maxLon - minLon;
-
- // 避免除零错误
- if (rangeLat == 0)
- rangeLat = 1e-10;
- if (rangeLon == 0)
- rangeLon = 1e-10;
-
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
- "Sampled GPS range: lat=[%.6f, %.6f], lon=[%.6f, %.6f]", minLat,
- maxLat, minLon, maxLon);
-
- // 映射每个点到画布
- for (const auto &point : sampledData) {
- double normLat = (point.latitude - minLat) / rangeLat;
- double normLon = (point.longitude - minLon) / rangeLon;
-
- // 转换到画布坐标
- double x = normLon * 300 + 50;
- double y = (1.0 - normLat) * 300 + 50;
-
- canvasData.push_back({x, y});
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
- "Mapped point: (%.6f, %.6f) -> (%.2f, %.2f)", point.latitude,
- point.longitude, x, y);
- }
-
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Generated %zu canvas points",
- canvasData.size());
- }
+/**
+ * @brief GPS点位映射到画布坐标的核心函数
+ *
+ * 映射原理(三步法):
+ * 1. 归一化:将GPS经纬度转换为0-1之间的相对位置
+ * 2. 缩放:将归一化坐标缩放到画布可用区域(300x300,留50像素边距)
+ * 3. 翻转:Y轴需要翻转(GPS纬度:北纬大,画布Y:上小下大)
+ *
+ * 画布布局:
+ * - 总大小:400x400像素
+ * - 可用区域:300x300像素(从50,50到350,350)
+ * - 边距:上下左右各50像素(避免轨迹贴边显示不全)
+ *
+ * 示例:
+ * 假设GPS轨迹范围:纬度[39.9, 40.0],经度[116.3, 116.4]
+ * - 点A: (39.95, 116.35) -> 归一化(0.5, 0.5) -> 画布(200, 200)
+ * - 点B: (39.9, 116.3) -> 归一化(0.0, 0.0) -> 画布(50, 350) [左下角]
+ * - 点C: (40.0, 116.4) -> 归一化(1.0, 1.0) -> 画布(350, 50) [右上角]
+ */
+void TraingModel::GetPointData() {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "开始GPS到画布映射,GPS点数: %zu", gpsData.size());
+
+ // 步骤0:数据检查
+ if (gpsData.empty()) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS数据为空,无法映射");
+ canvasData.clear();
+ return;
+ }
+
+ // 步骤1:计算GPS轨迹的边界框(Bounding Box)
+ // 找到所有GPS点中的最小和最大经纬度
+ double minLat = 90.0; // 纬度最小值(初始化为最大值,确保会被替换)
+ double maxLat = -90.0; // 纬度最大值(初始化为最小值,确保会被替换)
+ double minLon = 180.0; // 经度最小值(初始化为最大值,确保会被替换)
+ double maxLon = -180.0; // 经度最大值(初始化为最小值,确保会被替换)
+
+ for (const auto &point : gpsData) {
+ minLat = getmin(minLat, point.latitude);
+ maxLat = getmax(maxLat, point.latitude);
+ minLon = getmin(minLon, point.longitude);
+ maxLon = getmax(maxLon, point.longitude);
+ }
+
+ // 计算经纬度的范围(跨度)
+ double rangeLat = maxLat - minLat; // 纬度跨度
+ double rangeLon = maxLon - minLon; // 经度跨度
+
+ // 处理特殊情况:所有GPS点几乎在同一个位置(范围接近0)
+ const double MIN_RANGE = 1e-6; // 最小有效范围(约0.000001度,约0.1米)
+ if (rangeLat < MIN_RANGE) {
+ WEARABLE_LOGW(WEARABLE_LOG_MODULE_APP,
+ "纬度范围过小(%.9f),使用默认值", rangeLat);
+ rangeLat = MIN_RANGE; // 避免除零,同时保持轨迹可见
+ }
+ if (rangeLon < MIN_RANGE) {
+ WEARABLE_LOGW(WEARABLE_LOG_MODULE_APP,
+ "经度范围过小(%.9f),使用默认值", rangeLon);
+ rangeLon = MIN_RANGE; // 避免除零,同时保持轨迹可见
+ }
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "GPS边界框: 纬度[%.6f, %.6f] 跨度=%.6f, 经度[%.6f, %.6f] 跨度=%.6f",
+ minLat, maxLat, rangeLat, minLon, maxLon, rangeLon);
+
+ // 步骤2:定义画布参数(常量)
+ const double CANVAS_SIZE = 400.0; // 画布总大小:400x400
+ const double CANVAS_MARGIN = 50.0; // 边距:50像素(避免轨迹贴边)
+ const double CANVAS_USABLE = 300.0; // 可用区域:300x300(400-50*2)
+
+ // 步骤3:映射所有GPS点到画布坐标
+ canvasData.clear();
+ canvasData.reserve(gpsData.size()); // 预分配内存,提高性能
+
+ for (const auto &point : gpsData) {
+ // 3.1 归一化:将GPS坐标转换为0-1之间的相对位置
+ // normLat = 0.0 表示最南边(minLat),1.0 表示最北边(maxLat)
+ // normLon = 0.0 表示最西边(minLon),1.0 表示最东边(maxLon)
+ double normLat = (point.latitude - minLat) / rangeLat;
+ double normLon = (point.longitude - minLon) / rangeLon;
+
+ // 3.2 缩放到画布可用区域
+ // X坐标:直接映射(经度从左到右对应X从左到右)
+ // Y坐标:需要翻转(纬度从南到北对应Y从下到上,但画布Y是从上到下)
+ double x = normLon * CANVAS_USABLE + CANVAS_MARGIN;
+ double y = (1.0 - normLat) * CANVAS_USABLE + CANVAS_MARGIN;
+ // 注意:Y轴翻转使用 (1.0 - normLat),因为:
+ // - GPS纬度:北纬大(如40.0)应该显示在画布上方(Y小,如50)
+ // - GPS纬度:南纬小(如39.9)应该显示在画布下方(Y大,如350)
+
+ // 3.3 边界检查(确保坐标在画布范围内)
+ if (x < CANVAS_MARGIN) x = CANVAS_MARGIN;
+ if (x > CANVAS_SIZE - CANVAS_MARGIN) x = CANVAS_SIZE - CANVAS_MARGIN;
+ if (y < CANVAS_MARGIN) y = CANVAS_MARGIN;
+ if (y > CANVAS_SIZE - CANVAS_MARGIN) y = CANVAS_SIZE - CANVAS_MARGIN;
+
+ canvasData.push_back({x, y});
+ }
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "映射完成:%zu个GPS点 -> %zu个画布点,画布范围[%.1f, %.1f]到[%.1f, %.1f]",
+ gpsData.size(), canvasData.size(),
+ CANVAS_MARGIN, CANVAS_MARGIN,
+ CANVAS_SIZE - CANVAS_MARGIN, CANVAS_SIZE - CANVAS_MARGIN);
+}
// 添加经纬度数据集
void TraingModel::AddGPSData(uint32_t timestamp, double latitude,
return filePath; // 返回找到的文件路径
}
}
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "未找到与时间戳相关的找到gps表。");
- return "";
- }
-
- //获取GPS连接状态
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "未找到与时间戳相关的找到gps表。");
+ return "";
+}
+
+// 加载GPS数据
+bool TraingModel::LoadGPSData(const std::string &filePath) {
+ std::ifstream gpsFile(filePath, std::ios::binary);
+ if (!gpsFile) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "无法打开GPS文件: %s",
+ filePath.c_str());
+ return false;
+ }
+
+ // 清空现有数据
+ gpsData.clear();
+
+ // 动态读取GPS数据
+ GPSData data;
+ while (gpsFile.read(reinterpret_cast<char *>(&data), sizeof(GPSData))) {
+ gpsData.push_back(data);
+ }
+
+ gpsFile.close();
+
+ if (gpsData.empty()) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "未读取到有效GPS数据: %s",
+ filePath.c_str());
+ return false;
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "LoadGPSData Success. 加载了 %zu 个GPS点", gpsData.size());
+
+ // 加载GPS数据后,重新生成canvas数据(使用所有GPS点)
+ GetPointData();
+
+ return true;
+}
+
+//获取GPS连接状态
bool TraingModel::GetGPSConnectState() { return gpsconnect; }
//设置GPS连接状态
void TraingModel::SetGPSConnectState(bool state) {