From: Caisking Date: Thu, 3 Jul 2025 09:25:40 +0000 (+0800) Subject: 修改轨迹绘制:待实际测试 X-Git-Url: http://git.snicker.com.cn/css/static/gitweb.css?a=commitdiff_plain;h=e9ff17b8bc78b57623be512409ad6d9d9d4cc830;p=mirrors%2FFD230.git 修改轨迹绘制:待实际测试 --- diff --git a/code/sdk/application/wearable/fd230app/nativeui/traing/src/TraingModel.cpp b/code/sdk/application/wearable/fd230app/nativeui/traing/src/TraingModel.cpp index ed95b1e94..8719fbe16 100644 --- a/code/sdk/application/wearable/fd230app/nativeui/traing/src/TraingModel.cpp +++ b/code/sdk/application/wearable/fd230app/nativeui/traing/src/TraingModel.cpp @@ -191,14 +191,15 @@ static void Training_Notify(void *args, const Topic topic, Request *request) { } #if 0 //使用随机数来进行测试 - srand(time(NULL)); + srand(time(NULL)); // 纬度范围: float lat = (rand() % 420) / 10.0f + 224.5; lat = lat / 10.0f; - // 经度范围: + // 经度范围: float lng = (rand() % 890) / 10.0f + 1137.5; lng = lng / 10.0f; - TraingModel::GetInstance()->AddGPSData(0, lat, lng); + uint32_t time = TraingModel::GetInstance()->get_current_time(); + TraingModel::GetInstance()->AddGPSData(time, lat, lng); return; #endif #if defined(SUPPORT_GNSS) @@ -2573,46 +2574,50 @@ bool TraingModel::LoadDataFormJson(std::vector &sportDataVector) { */ static double getmin(double a, double b) { return a < b ? a : b; } - static double getmax(double a, double b) { return a > b ? a : b; } - -// 计算点p到由p1和p2确定的线段的垂直距离 +// 计算点p到由p1和p2确定的线段的垂直距离(优化版) static double perpendicularDistance(const Point &p, const Point &p1, const Point &p2) { + // 计算向量 p1->p2 double dx = p2.x - p1.x; double dy = p2.y - p1.y; // 线段长度的平方 double len2 = dx * dx + dy * dy; - // 如果线段实际上是一个点,返回点p到p1的距离 - if (len2 == 0) - return std::sqrt((p.x - p1.x) * (p.x - p1.x) + (p.y - p1.y) * (p.y - p1.y)); + // 如果线段退化为一个点,直接计算点到点的距离 + if (len2 == 0) { + return std::hypot(p.x - p1.x, p.y - p1.y); + } - // 考虑从p1到p的向量在从p1到p2的向量上的投影 + // 计算投影比例 t double t = ((p.x - p1.x) * dx + (p.y - p1.y) * dy) / len2; if (t < 0) { - // 投影在p1之前 - return std::sqrt((p.x - p1.x) * (p.x - p1.x) + (p.y - p1.y) * (p.y - p1.y)); + // 投影在p1之前,返回p到p1的距离 + return std::hypot(p.x - p1.x, p.y - p1.y); } + if (t > 1) { - // 投影在p2之后 - return std::sqrt((p.x - p2.x) * (p.x - p2.x) + (p.y - p2.y) * (p.y - p2.y)); + // 投影在p2之后,返回p到p2的距离 + return std::hypot(p.x - p2.x, p.y - p2.y); } - // 投影在线段上 - Point projection; - projection.x = p1.x + t * dx; - projection.y = p1.y + t * dy; - - return std::sqrt((p.x - projection.x) * (p.x - projection.x) + - (p.y - projection.y) * (p.y - projection.y)); + // 投影在线段上,计算投影点并返回距离 + double projX = p1.x + t * dx; + double projY = p1.y + t * dy; + return std::hypot(p.x - projX, p.y - projY); } -// Douglas-Peucker算法实现 +// Douglas-Peucker算法实现(优化版) static void douglasPeucker(const std::vector &points, double epsilon, std::vector &result) { + if (points.size() <= 2) { + // 点数量不足,直接返回 + result = points; + return; + } + double dmax = 0; int index = 0; int end = points.size() - 1; @@ -2631,88 +2636,147 @@ static void douglasPeucker(const std::vector &points, double epsilon, std::vector recResults1, recResults2; // 递归处理前半部分 - std::vector firstSegment(points.begin(), points.begin() + index + 1); - douglasPeucker(firstSegment, epsilon, recResults1); + douglasPeucker( + std::vector(points.begin(), points.begin() + index + 1), epsilon, + recResults1); // 递归处理后半部分 - std::vector secondSegment(points.begin() + index, points.end()); - douglasPeucker(secondSegment, epsilon, recResults2); + douglasPeucker(std::vector(points.begin() + index, points.end()), + epsilon, recResults2); - // 合并结果 + // 合并结果(避免重复添加端点) result.assign(recResults1.begin(), recResults1.end() - 1); result.insert(result.end(), recResults2.begin(), recResults2.end()); } else { // 否则保留首尾点 - result.clear(); - result.push_back(points[0]); - result.push_back(points[end]); + result = {points[0], points[end]}; } } -// 修改后的GetPointData函数 +// 改进的GetPointData函数(优化版) void TraingModel::GetPointData() { + WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, + "Generating point data from %zu GPS points", gpsData.size()); + std::vector sampledData; - // 使用Douglas-Peucker算法简化轨迹 + // 处理GPS数据 if (gpsData.size() > 100) { - // 先将GPS数据转换为屏幕坐标 - std::vector screenPoints; - // 计算经纬度范围 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; } - // 转换为屏幕坐标 - for (const auto &point : gpsData) { - double normLat = (point.latitude - minLat) / (maxLat - minLat); - double normLon = (point.longitude - minLon) / (maxLon - minLon); - screenPoints.push_back({normLon * 300 + 50, (1.0 - normLat) * 300 + 50}); - } + // 计算经纬度范围和平均值 + double rangeLat = maxLat - minLat; + double rangeLon = maxLon - minLon; + double avgLat = sumLat / gpsData.size(); + double avgLon = sumLon / gpsData.size(); - // 使用Douglas-Peucker算法简化 - std::vector simplifiedPoints; - douglasPeucker(screenPoints, 2.0, simplifiedPoints); // 阈值2.0可以调整 + WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, + "GPS range: lat=[%.6f, %.6f], lon=[%.6f, %.6f]", minLat, + maxLat, minLon, maxLon); - // 如果简化后的点仍然超过100个,则进行均匀采样 - if (simplifiedPoints.size() > 100) { - double step = static_cast(simplifiedPoints.size() - 1) / 99; + // 检查数据是否集中在极小区域或几乎相同 + 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(gpsData.size() - 1) / 99; for (int i = 0; i < 100; i++) { int index = static_cast(i * step); - // 这里需要将屏幕坐标反推回GPS坐标 - double normX = (simplifiedPoints[index].x - 50) / 300; - double normY = (simplifiedPoints[index].y - 50) / 300; - double lat = minLat + (1.0 - normY) * (maxLat - minLat); - double lon = minLon + normX * (maxLon - minLon); - sampledData.push_back({lat, lon}); + sampledData.push_back(gpsData[index]); } } else { - // 简化后的点少于100个,直接使用 - for (const auto &point : simplifiedPoints) { - double normX = (point.x - 50) / 300; - double normY = (point.y - 50) / 300; - double lat = minLat + (1.0 - normY) * (maxLat - minLat); - double lon = minLon + normX * (maxLon - minLon); - sampledData.push_back({lat, lon}); + // 转换GPS坐标到屏幕坐标 + std::vector 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 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(gpsData.size() - 1) / 99; + for (int i = 0; i < 100; i++) { + int index = static_cast(i * step); + sampledData.push_back(gpsData[index]); + } + } else if (simplifiedPoints.size() > 100) { + // 简化后仍超过100点,再次均匀采样 + double step = static_cast(simplifiedPoints.size() - 1) / 99; + for (int i = 0; i < 100; i++) { + int index = static_cast(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(); - const double canvasSize = 300.0; - const double offset = 50.0; + 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); @@ -2720,14 +2784,36 @@ void TraingModel::GetPointData() { 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) / (maxLat - minLat); - double normLon = (point.longitude - minLon) / (maxLon - minLon); - double x = normLon * canvasSize + offset; - double y = (1.0 - normLat) * canvasSize + offset; + 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()); } // 添加经纬度数据集 @@ -2735,18 +2821,25 @@ void TraingModel::AddGPSData(uint32_t timestamp, double latitude, double longitude) { // 判断数据是否合理 if (latitude < -90 || latitude > 90 || longitude < -180 || longitude > 180) { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Invalid GPS data"); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, + "Invalid GPS data: lat=%.6f, lon=%.6f", latitude, longitude); return; } - // 校验时间戳是不是重复 比较最后一个? - if (gpsData.size() > 0 && timestamp <= gpsData.back().timestamp) { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Timestamp is not increasing"); + + // 校验时间戳是否递增 + if (!gpsData.empty() && timestamp <= gpsData.back().timestamp) { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, + "Timestamp not increasing: current=%u, last=%u", timestamp, + gpsData.back().timestamp); return; } + // 添加数据 gpsData.push_back({timestamp, latitude, longitude}); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, + "Added GPS data #%zu: ts=%u, (%.6f, %.6f)", gpsData.size(), + timestamp, latitude, longitude); } - //保存gps数据 bool TraingModel::SaveCanvasData() {