Browse Source

路径压缩更新

wangyingjie 1 month ago
parent
commit
570c54ad07

+ 70 - 71
line_vacuate/studio_geo_algo_c.c

@@ -37,11 +37,13 @@ double point_line_dist_square_c(const studio_point_c *p, const studio_point_c *s
     {
         xx = start->x;
         yy = start->y;
-    } else if (param > 1)
+    }
+    else if (param > 1)
     {
         xx = end->x;
         yy = end->y;
-    } else
+    }
+    else
     {
         xx = start->x + param * C;
         yy = start->y + param * D;
@@ -51,7 +53,6 @@ double point_line_dist_square_c(const studio_point_c *p, const studio_point_c *s
     return dist_square;
 }
 
-
 double azimuth_angle_c(const studio_point_c *p_from, const studio_point_c *p_to)
 {
     double delta_x = p_to->x - p_from->x;
@@ -67,7 +68,6 @@ double azimuth_angle_c(const studio_point_c *p_from, const studio_point_c *p_to)
     return theta_deg;
 }
 
-
 double distance_squared_gcs_c(const studio_point_c *p1, const studio_point_c *p2)
 {
     // 经纬度转高斯投影
@@ -126,7 +126,7 @@ studio_point_c lonlat_move_by(const studio_point_c *pt, double delta_x, double d
     lonlat_to_gauss(central, pt->x, pt->y, &gx, &gy);
 
     // 应用平面坐标偏移(东向加delta_x,北向加delta_y)
-    const double new_gx = gx + delta_x; // 单位:米
+    const double new_gx = gx + delta_x;  // 单位:米
     const double new_gy = gy + delta_y;
 
     // 转回经纬度
@@ -144,24 +144,32 @@ studio_point_c lonlat_move_by(const studio_point_c *pt, double delta_x, double d
     return new_point;
 }
 
-
 /////////////////// 矢量(线)抽稀算法 ///////////////////
 
-
-void douglas_peucker_c(const studio_line_c *points, size_t start, size_t end, double epsilon, unsigned int *indices,
-                       unsigned int *index_count)
+void douglas_peucker_c(const studio_line_c *points, size_t start, size_t end, double epsilon, unsigned int *indices, unsigned int *index_count, unsigned int max_indices)
 {
-    // 计算最大距离
+    if (end <= start + 1)
+    {
+        if (*index_count < max_indices)
+        {
+            indices[*index_count] = start;
+            (*index_count)++;
+        }
+        if (start != end && *index_count < max_indices)
+        {
+            indices[*index_count] = end;
+            (*index_count)++;
+        }
+        return;
+    }
+
     double max_dist = 0;
     size_t index = start;
     double epsilon_square = epsilon * epsilon;
 
-    // 遍历从start+1到end-1的点
     for (size_t i = start + 1; i < end; ++i)
     {
-        // 计算点到线段的距离的平方
         double dist_square = point_line_dist_square_c(&points->data[i], &points->data[start], &points->data[end]);
-        // 如果距离大于最大距离,则更新最大距离和索引
         if (dist_square > max_dist)
         {
             index = i;
@@ -169,18 +177,19 @@ void douglas_peucker_c(const studio_line_c *points, size_t start, size_t end, do
         }
     }
 
-    // 如果最大距离大于epsilon的平方,并且索引不是start和end,则递归调用Douglas-Peucker算法
-    if (max_dist > epsilon_square && index != start && index != end)
+    if (max_dist > epsilon_square && index > start && index < end)
     {
-        douglas_peucker_c(points, start, index, epsilon, indices, index_count);
-        douglas_peucker_c(points, index, end, epsilon, indices, index_count);
+        douglas_peucker_c(points, start, index, epsilon, indices, index_count, max_indices);
+        douglas_peucker_c(points, index, end, epsilon, indices, index_count, max_indices);
     }
-    // 否则,将start和end的索引添加到indices数组中
     else
     {
-        indices[*index_count] = start;
-        (*index_count)++;
-        if (start != end)
+        if (*index_count < max_indices)
+        {
+            indices[*index_count] = start;
+            (*index_count)++;
+        }
+        if (*index_count < max_indices && start != end)
         {
             indices[*index_count] = end;
             (*index_count)++;
@@ -188,53 +197,37 @@ void douglas_peucker_c(const studio_line_c *points, size_t start, size_t end, do
     }
 }
 
-
-unsigned int hash(unsigned int value, unsigned int size)
-{
-    return value % size;
-}
-
-void remove_duplicates(unsigned int **indices, unsigned int *size)
+void remove_duplicates(unsigned int *indices, unsigned int *index_count)
 {
-    if (*size == 0)
+    if (*index_count == 0)
     {
         return;
     }
-
-    unsigned int *unique_indices = (unsigned int *) malloc(*size * sizeof(unsigned int));
-    unsigned int unique_count = 0;
-
-    // 创建一个哈希表
-    unsigned int hash_table_size = *size * 4; // 哈希表大小可以稍微增大以减少冲突 这个可能有问题
-    bool *hash_table = (bool *) malloc(hash_table_size * sizeof(bool));
-    for (unsigned int i = 0; i < hash_table_size; i++)
+    unsigned int j = 0;  // 指向去重后的位置
+    for (unsigned int i = 0; i < *index_count; i++)
     {
-        hash_table[i] = false; // 初始化哈希表
-    }
+        bool is_duplicate = false;
 
-    for (unsigned int i = 0; i < *size; i++)
-    {
-        unsigned int current_value = (*indices)[i];
-        unsigned int hash_index = hash(current_value, hash_table_size);
+        // 检查 indices[i] 是否已经在 indices[0..j-1] 中出现过
+        for (unsigned int k = 0; k < j; k++)
+        {
+            if (indices[i] == indices[k])
+            {
+                is_duplicate = true;
+                break;
+            }
+        }
 
-        // 检查该元素是否已经在哈希表中
-        if (!hash_table[hash_index])
+        // 如果不是重复元素,则加入去重后的数组
+        if (!is_duplicate)
         {
-            hash_table[hash_index] = true;
-            unique_indices[unique_count++] = current_value;
+            indices[j++] = indices[i];
         }
     }
 
-    // 释放原有的 indices 和哈希表
-    free(*indices);
-    free(hash_table);
-
-    // 更新 indices 指针和大小
-    *indices = unique_indices;
-    *size = unique_count;
+    *index_count = j;  // 更新数组长度
 }
 
-
 bool line_vacuate_c(const studio_line_c *line, const int max_points, const double epsilon, studio_line_c *vacuate_line)
 {
     // 初始化状态变量
@@ -258,8 +251,14 @@ bool line_vacuate_c(const studio_line_c *line, const int max_points, const doubl
     }
 
     // 分配内存空间存储线段点的索引
-    unsigned int *indices = (unsigned int *) malloc(line->size * sizeof(unsigned int));
+    unsigned int *indices = (unsigned int *)malloc(line->size * sizeof(unsigned int));
+    if (!indices)
+    {
+        // 内存分配失败处理
+        return false;
+    }
     unsigned int index_count = 0;
+    const unsigned int max_indices = line->size;
 
     double t_epsilon = epsilon;
 
@@ -267,11 +266,12 @@ bool line_vacuate_c(const studio_line_c *line, const int max_points, const doubl
     while (1)
     {
         index_count = 0;
-        douglas_peucker_c(line, 0, line->size - 1, t_epsilon, indices, &index_count);
-        // 将indices 中的数据去重
-        remove_duplicates(&indices, &index_count);
+        // douglas_peucker_c(line, 0, line->size - 1, t_epsilon, indices, &index_count);
+        douglas_peucker_c(line, 0, line->size - 1, t_epsilon, indices, &index_count, max_indices);
+        // 将indices 中的数据去重 保持顺序
+        remove_duplicates(indices, &index_count);
 
-        if (index_count <= (unsigned int) max_points)
+        if (index_count <= (unsigned int)max_points)
         {
             break;
         }
@@ -290,11 +290,10 @@ bool line_vacuate_c(const studio_line_c *line, const int max_points, const doubl
     return status;
 }
 
-bool line_vacuate_gcs_c(const studio_line_c *line, const int max_points, const double epsilon,
-                        studio_line_c *vacuate_line)
+bool line_vacuate_gcs_c(const studio_line_c *line, const int max_points, const double epsilon, studio_line_c *vacuate_line)
 {
     bool status = false;
-    if (!line || !vacuate_line || line->size <= 1) // 添加空指针和长度校验
+    if (!line || !vacuate_line || line->size <= 1)  // 添加空指针和长度校验
     {
         printf("line or vacuate_line is null or line size <= 1");
         return status;
@@ -331,11 +330,10 @@ bool line_vacuate_gcs_c(const studio_line_c *line, const int max_points, const d
     return status;
 }
 
-
 bool remove_outliers_c(const studio_line_c *line, double distance, studio_line_c *outliers_line)
 {
     bool status = false;
-    if (!line || !outliers_line || line->size <= 1) // 添加空指针和长度校验
+    if (!line || !outliers_line || line->size <= 1)  // 添加空指针和长度校验
     {
         printf("line or outliers_line is null or line size <= 1");
         return status;
@@ -359,20 +357,21 @@ bool remove_outliers_c(const studio_line_c *line, double distance, studio_line_c
         printf("gauss_line size <= 1");
         return status;
     }
-    studio_line_c gs_filtered_line = studio_line_c_init(); // 新建过滤后的线段
-    studio_line_c_add_point(&gs_filtered_line, gauss_line.data[0]); // 保留第一个点
-    for (unsigned int i = 1; i < gauss_line.size; i++) // 遍历相邻点对
+    studio_line_c gs_filtered_line = studio_line_c_init();           // 新建过滤后的线段
+    studio_line_c_add_point(&gs_filtered_line, gauss_line.data[0]);  // 保留第一个点
+    for (unsigned int i = 1; i < gauss_line.size; i++)               // 遍历相邻点对
     {
         double d_sq = distance_squared_c(&gauss_line.data[i - 1], &gauss_line.data[i]);
         if (d_sq <= dist_square)
         {
             studio_line_c_add_point(&gs_filtered_line, gauss_line.data[i]);
-        } else
+        }
+        else
         {
             int a = 0;
         }
     }
-    studio_line_c_destroy(&gauss_line); // 释放原始数据的高斯投影
+    studio_line_c_destroy(&gauss_line);  // 释放原始数据的高斯投影
 
     // 高斯克吕格转经纬度
     for (unsigned int i = 0; i < gs_filtered_line.size; i++)
@@ -383,7 +382,7 @@ bool remove_outliers_c(const studio_line_c *line, double distance, studio_line_c
         studio_line_c_add_point(outliers_line, point);
     }
 
-    studio_line_c_destroy(&gs_filtered_line); // 释放移除离群点后的高斯投影
+    studio_line_c_destroy(&gs_filtered_line);  // 释放移除离群点后的高斯投影
 
     status = true;
     return status;

+ 7 - 11
line_vacuate/studio_geo_algo_c.h

@@ -75,7 +75,7 @@ studio_point_c lonlat_move_by(const studio_point_c *pt, double delta_x, double d
 /// \param epsilon 抽稀容差,若某点到当前线段的最大垂直距离超过epsilon,则保留该点,单位与适量坐标系的单位一致
 /// \param indices 存储简化后的点索引
 /// \param index_count 存储简化后的点索引数量
-void douglas_peucker_c(const studio_line_c *points, size_t start, size_t end, double epsilon, unsigned int *indices, unsigned int *index_count);
+void douglas_peucker_c(const studio_line_c *points, size_t start, size_t end, double epsilon, unsigned int *indices, unsigned int *index_count, unsigned int max_indices);
 
 /// 矢量线段抽稀算法
 /// \param line 原始线段
@@ -95,16 +95,12 @@ bool line_vacuate_gcs_c(const studio_line_c *line, const int max_points, const d
 
 /////////////////// 移除线段中的离群点 ///////////////////
 
-/// 计算哈希值
-/// \param value
-/// \param size
-/// \return
-unsigned int hash(unsigned int value, unsigned int size);
-
-/// 移除重复点 保持原有顺序
-/// \param indices 点索引
-/// \param size 点索引数量
-void remove_duplicates(unsigned int **indices, unsigned int *size);
+/**
+ * @brief 去除 indices 数组中的重复元素(保持顺序,原地修改)
+ * @param indices 输入数组(可能包含重复)
+ * @param index_count 输入数组长度(会被更新为去重后的长度)
+ */
+void remove_duplicates(unsigned int *indices, unsigned int *index_count);
 
 /// 移除线段中的离群点
 /// @param line 原始线段

+ 0 - 230
line_vacuate/task_algo_c.cpp

@@ -1,230 +0,0 @@
-/**
-******************************************************************************
-* @file           : task_alog_c.cpp
-* @author         : wyj
-* @brief          : C语言语法测试
-* @attention      : None
-* @date           : 2025/5/9
-******************************************************************************
-*/
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-#include "geography/studio_proj_c.h"
-#include "geometry/studio_geo_algo_c.h"
-
-#ifdef __cplusplus
-}
-#endif
-
-#include <stdio.h>
-#include <stdio.h>
-#include "geometry/studio_geo_utils.h"
-
-void studio_line_to_c_line(const studio_line &line, studio_line_c *c_line)
-{
-    for (size_t i = 0; i < line.size(); ++i)
-    {
-        studio_point_c p = studio_point_init(line[i].x, line[i].y);
-        studio_line_c_add_point(c_line, p);
-    }
-}
-
-void c_line_to_studio_line(const studio_line_c *c_line, studio_line &line)
-{
-    for (size_t i = 0; i < c_line->size; ++i)
-    {
-        const studio_point_c *p = studio_line_c_get_point(c_line, i);
-        line.push_back(studio_point(p->x, p->y));
-    }
-}
-
-// ///////////////////// 移除离群点 ////////////////////////////////
-// int main()
-// {
-//     printf("\n\n===================== %s =====================\n\n", __FILE__);
-//     silly::geo::utils::init_gdal_env();
-//     std::string path;
-// #ifdef IS_WINDOWS
-//     path = "D:/5_file/2_readfile/geojson/multi_point/fitting_examples.geojson";
-// #else
-//     path = "/home/wyj/myself/2_data/2_geojson/multi_point/fitting_examples.geojson";
-// #endif
-//
-//     std::cout << "path: " << path << std::endl;
-//     std::vector<studio_geo_coll> res_collections;
-//     std::vector<studio_geo_coll> collections;
-//     silly::geo::utils::read_geo_coll(path, collections);
-//
-//     for (auto &coll: collections)
-//     {
-//         studio_line_c line_c = studio_line_c_init();
-//         studio_line_to_c_line(coll.m_line, &line_c);
-//         std::cout << "原始数据量:" << coll.m_line.size() << std::endl;
-//
-//         studio_line_c outliers_line = studio_line_c_init();
-//         remove_outliers_c(&line_c, 1, &outliers_line);
-//
-//         studio_line simplified_line;
-//         c_line_to_studio_line(&outliers_line, simplified_line);
-//         std::cout << "简化后数据量:" << simplified_line.size() << std::endl;
-//
-//         studio_geo_coll temp;
-//         temp.m_type = enum_geometry_type::egtLineString;
-//         temp.m_line = simplified_line;
-//         res_collections.push_back(temp);
-//
-//         studio_line_c_destroy(&line_c);
-//         studio_line_c_destroy(&outliers_line);
-//
-//         break;
-//     }
-//
-//
-//     std::string output_path;
-// #ifdef IS_WINDOWS
-//     output_path = "D:/5_file/2_readfile/geojson/multi_point/remove_outliers.geojson";
-// #else
-//     output_path = "/home/wyj/myself/2_data/2_geojson/multi_point/remove_outliers.geojson";
-// #endif
-//     silly::geo::utils::write_geo_coll(output_path, res_collections);
-//      silly::geo::utils::destroy_gdal_env();
-//     return 0;
-// }
-
-
-// //////////////////// 路径压缩算法测试 //////////////////////
-int main()
-{
-    printf("\n\n===================== %s =====================\n\n", __FILE__);
-    silly::geo::utils::init_gdal_env();
-    std::string path;
-#ifdef IS_WINDOWS
-    path = "D:/5_file/2_readfile/geojson/multi_point/fitting_examples.geojson";
-#else
-    path = "/home/wyj/myself/2_data/2_geojson/multi_point/fitting_examples.geojson";
-#endif
-
-    std::cout << "path: " << path << std::endl;
-    std::vector<studio_geo_coll> res_collections;
-    std::vector<studio_geo_coll> collections;
-    silly::geo::utils::read_geo_coll(path, collections);
-    for (auto &coll: collections)
-    {
-        studio_line_c line_c = studio_line_c_init();
-        studio_line_c vac_line_c = studio_line_c_init();
-        studio_line_to_c_line(coll.m_line, &line_c);
-
-        // 简化线段,目标点数为28个
-        int max_points = 28;
-        double epsilon = 1.0;
-        bool res = line_vacuate_gcs_c(&line_c, max_points, epsilon, &vac_line_c);
-        if (!res)
-        {
-            std::cout << "Failed to simplify line." << std::endl;
-            return 1;
-        }
-        studio_line simplified_line;
-        c_line_to_studio_line(&vac_line_c, simplified_line);
-
-        studio_geo_coll temp;
-        temp.m_type = enum_geometry_type::egtLineString;
-        temp.m_line = simplified_line;
-        res_collections.push_back(temp);
-
-        studio_line_c_destroy(&line_c);
-        studio_line_c_destroy(&vac_line_c);
-
-        break;
-    }
-    std::string output_path;
-#ifdef IS_WINDOWS
-    output_path = "D:/5_file/2_readfile/geojson/multi_point/fitting_examples_res_1.geojson";
-#else
-    output_path = "/home/wyj/myself/2_data/2_geojson/multi_point/fitting_examples_res_2_c.geojson";
-#endif
-    silly::geo::utils::write_geo_coll(output_path, res_collections);
-
-    silly::geo::utils::destroy_gdal_env();
-
-    return 0;
-}
-
-// int main()
-// {
-//     printf("\n\n===================== %s =====================\n\n", __FILE__);
-//     silly::geo::utils::init_gdal_env();
-//     std::string path;
-// #ifdef IS_WINDOWS
-//     path = "D:/5_file/2_readfile/geojson/multi_point/fitting_examples.geojson";
-// #else
-//     path = "/home/wyj/myself/2_data/2_geojson/multi_point/fitting_examples.geojson";
-// #endif
-//
-//     std::cout << "path: " << path << std::endl;
-//     std::vector<studio_geo_coll> res_collections;
-//     std::vector<studio_geo_coll> collections;
-//     silly::geo::utils::read_geo_coll(path, collections);
-//     for (auto &coll : collections)
-//     {
-//         // ------------- 转换为高斯投影 -------------
-//         studio_line gauss_line;
-//         double central = static_cast<int>(coll.m_line[0].x / 3) * 3;
-//         for (auto &point : coll.m_line)
-//         {
-//             double gx = 0.0;
-//             double gy = 0.0;
-//             lonlat_to_gauss(central, point.x, point.y, &gx, &gy);
-//             gauss_line.push_back(studio_point(gx, gy));
-//         }
-//         studio_line_c gauss_line_c = studio_line_c_init();
-//         studio_line_c vac_gauss_line_c = studio_line_c_init();
-//         studio_line_to_c_line(gauss_line, &gauss_line_c);
-//
-//         // 简化线段,目标点数为28个
-//         int max_points = 28;
-//         double epsilon = 1.0;
-//
-//         bool res = line_vacuate_c(&gauss_line_c, max_points, epsilon, &vac_gauss_line_c);
-//         if (!res)
-//         {
-//             std::cout << "Failed to simplify line." << std::endl;
-//             return 1;
-//         }
-//
-//         // 高斯投影在转回经纬度
-//         studio_line simplified_line;
-//         for (int i = 0; i < vac_gauss_line_c.size; i++)
-//         {
-//             double lon = 0.0;
-//             double lat = 0.0;
-//             const studio_point_c *p = studio_line_c_get_point(&vac_gauss_line_c, i);
-//             gauss_to_lonlat(central, p->x, p->y, &lon, &lat);
-//             simplified_line.push_back(studio_point(lon, lat));
-//         }
-//
-//         studio_geo_coll temp;
-//         temp.m_type = enum_geometry_type::egtLineString;
-//         temp.m_line = simplified_line;
-//         res_collections.push_back(temp);
-//
-//         studio_line_c_destroy(&gauss_line_c);
-//         studio_line_c_destroy(&vac_gauss_line_c);
-//
-//         break;
-//     }
-//     std::string output_path;
-// #ifdef IS_WINDOWS
-//     output_path = "D:/5_file/2_readfile/geojson/multi_point/fitting_examples_res_1.geojson";
-// #else
-//     output_path = "/home/wyj/myself/2_data/2_geojson/multi_point/fitting_examples_res_1_c.geojson";
-// #endif
-//     silly::geo::utils::write_geo_coll(output_path, res_collections);
-//
-//     silly::geo::utils::destroy_gdal_env();
-//
-//     return 0;
-// }

+ 424 - 0
line_vacuate/test_algo_c.cpp

@@ -0,0 +1,424 @@
+/**
+******************************************************************************
+* @file           : task_alog_c.cpp
+* @author         : wyj
+* @brief          : C语言语法测试
+* @attention      : None
+* @date           : 2025/5/9
+******************************************************************************
+*/
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "control/pid/studio_pid_c.h"
+#include "geography/studio_proj_c.h"
+#include "geometry/studio_geo_algo_c.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <stdio.h>
+#include <stdio.h>
+#include "geometry/studio_geo_utils.h"
+#include "file/studio_dir.h"
+
+void studio_line_to_c_line(const studio_line& line, studio_line_c* c_line)
+{
+    for (size_t i = 0; i < line.size(); ++i)
+    {
+        studio_point_c p = studio_point_init(line[i].x, line[i].y);
+        studio_line_c_add_point(c_line, p);
+    }
+}
+
+void c_line_to_studio_line(const studio_line_c* c_line, studio_line& line)
+{
+    for (size_t i = 0; i < c_line->size; ++i)
+    {
+        const studio_point_c* p = studio_line_c_get_point(c_line, i);
+        line.push_back(studio_point(p->x, p->y));
+    }
+}
+
+///////////////////////////////// 以上是工具转化函数 //////////////////////////////////
+
+
+
+////////////////// 路径压缩算法测试 /////////////////////
+int main()
+{
+    printf("\n\n===================== %s =====================\n\n", __FILE__);
+    studio_geo_utils::init_gdal_env();
+
+    std::string src_root;
+    std::string dst_root;
+#ifdef IS_WINDOWS
+    src_root = "D:/9_data/2_readfile/geojson/path_compress/";
+    dst_root = "D:/9_data/2_readfile/geojson/path_compress/dst/";
+#else
+    src_root = "/home/wyj/myself/2_data/2_geojson/path_compress/";
+    dst_root = "/home/wyj/myself/2_data/2_geojson/path_compress/";
+#endif
+
+    std::vector<std::string> all_paths = studio_dir::getFilesOrDirs(src_root);
+
+    for (const auto& path : all_paths)
+    {
+        std::cout << "src path: " << path << std::endl;
+        std::vector<studio_geo_coll> res_collections;
+        std::vector<studio_geo_coll> collections;
+        studio_geo_utils::read_geo_coll(path, collections);
+        for (auto& coll : collections)
+        {
+            studio_line_c line_c = studio_line_c_init();
+            studio_line_c vac_line_c = studio_line_c_init();
+            studio_line_to_c_line(coll.m_line, &line_c);
+            std::cout << "原始数据量:" << line_c.size << std::endl;
+            // 简化线段,目标点数为30个
+            int max_points = 30;
+            double epsilon = 1.0;
+            bool res = line_vacuate_gcs_c(&line_c, max_points, epsilon, &vac_line_c);
+            if (!res)
+            {
+                std::cout << "Failed to simplify line." << std::endl;
+                return 1;
+            }
+            studio_line simplified_line;
+            c_line_to_studio_line(&vac_line_c, simplified_line);
+            std::cout << "简化后数据量:" << vac_line_c.size << std::endl;
+
+            studio_geo_coll temp;
+            if (simplified_line.size() > 0)
+            {
+                temp.m_type = enum_geometry_type::egtLineString;
+                temp.m_line = simplified_line;
+                res_collections.push_back(temp);
+            }
+
+            studio_line_c_destroy(&line_c);
+            studio_line_c_destroy(&vac_line_c);
+
+            break;
+        }
+
+        std::string name = std::filesystem::path(path).filename().stem().string();
+        std::string output_path = std::filesystem::path(dst_root).append(name + "_dst.geojson").string();
+        studio_geo_utils::write_geo_coll(output_path, res_collections);
+    }
+
+    studio_geo_utils::destroy_gdal_env();
+    return 0;
+}
+
+
+
+
+
+
+//// ///////////////////// 路径压缩错误测试 /////////////////////
+//
+// int main()
+//{
+//    printf("\n\n===================== %s =====================\n\n", __FILE__);
+//    studio_geo_utils::init_gdal_env();
+//    std::string path;
+// #ifdef IS_WINDOWS
+//    path = "D:/5_file/2_readfile/geojson/multi_point/fitting_examples.geojson";
+// #else
+//    path = "/home/wyj/myself/2_data/2_geojson/multi_point/fitting_examples.geojson";
+// #endif
+//
+//    std::cout << "path: " << path << std::endl;
+//    std::vector<studio_geo_coll> res_collections;
+//    std::vector<studio_geo_coll> collections;
+//
+//    // ------------- 转换为高斯投影 -------------
+//    studio_line gauss_line;
+//    double central = static_cast<int>(120.039001 / 3) * 3;
+//
+//    studio_line_c gcs_line_c = studio_line_c_init();
+//    studio_line_c vac_gcs_line_c = studio_line_c_init();
+//
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.039001, 30.982000));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.380996, 30.843999));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.469001, 30.371999));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.453002, 30.420000));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.277000, 30.131999));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.691001, 30.601999));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.210998, 30.250000));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.544998, 30.871999));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.752998, 30.440000));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.864997, 30.0000000));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.102996, 30.125999));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.766998, 30.365999));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.583000, 30.158000));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.663002, 30.326000));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.406997, 30.445999));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.516998, 30.131999));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.972999, 30.555999));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.654998, 30.253999));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.838996, 30.270000));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.855003, 30.110000));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.060997, 30.131999));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.925003, 30.139999));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.845001, 30.299999));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.189002, 30.299999));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.861000, 30.211999));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.154998, 30.722000));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.306999, 30.610000));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.302001, 30.745000));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.152999, 30.079999));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.425003, 30.656000));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.782997, 30.054000));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.151000, 30.517999));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.303001, 30.558000));
+//    studio_line_c_add_point(&gcs_line_c, studio_point_init(120.238998, 30.733999));
+//  //     studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07207105000001,36.15950345000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07206542000000,36.15950579000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07205912000001,36.15950948000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07205174000001,36.15951394000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07204096000000,36.15952022000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07203156000000,36.15952566000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07202411999999,36.15952983000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07200829000000,36.15953788000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07199709000000,36.15954257000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07198529000000,36.15954627000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07172613000000,36.15949536000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07171595000000,36.15949109000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07170811000000,36.15948855000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07169195000000,36.15948536000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07168092000001,36.15948474000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07166998000000,36.15948535000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07166058000000,36.15948630000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07165083000000,36.15948751000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07164358999999,36.15948821000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07163668000000,36.15948832000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07155865000000,36.15941322000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07155723000000,36.15940347000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07155523000000,36.15939333000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07155331000000,36.15938564000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07154843000001,36.15937024000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07154410000000,36.15936034000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07153860000000,36.15935092000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07153018000000,36.15934010000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07152281000000,36.15933237000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07151780000000,36.15932673000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07151684000000,36.15932353000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07152006000000,36.15932156000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07152524000000,36.15932060000000));
+//  // studio_line_c_add_point(&gcs_line_c, studio_point_init(120.07153203999999,36.15931997000000));
+//
+//    std::cout << "befor line size:" << gcs_line_c.size << std::endl;
+//    {
+//        studio_line stu_line;
+//        c_line_to_studio_line(&gcs_line_c, stu_line);
+//        studio_geo_coll temp;
+//        temp.m_type = enum_geometry_type::egtLineString;
+//        temp.m_line = stu_line;
+//        collections.push_back(temp);
+//        std::string temp_out = "/home/wyj/myself/2_data/2_geojson/multi_point/20250603_aaa.geojson";
+//        studio_geo_utils::write_geo_coll(temp_out, collections);
+//    }
+//
+//
+//    // 简化线段,目标点数为28个
+//    int max_points = 30;
+//    double epsilon = 1.0;
+//
+//    bool res = line_vacuate_gcs_c(&gcs_line_c, max_points, epsilon, &vac_gcs_line_c);
+//    if (!res)
+//    {
+//        std::cout << "Failed to simplify line." << std::endl;
+//        return 1;
+//    }
+//    std::cout << "Simplified line size:" << vac_gcs_line_c.size << std::endl;
+//    studio_line simplified_line;
+//    c_line_to_studio_line(&vac_gcs_line_c, simplified_line);
+//
+//    studio_geo_coll temp;
+//    temp.m_type = enum_geometry_type::egtLineString;
+//    temp.m_line = simplified_line;
+//    res_collections.push_back(temp);
+//
+//    studio_line_c_destroy(&gcs_line_c);
+//    studio_line_c_destroy(&vac_gcs_line_c);
+//
+//    std::string output_path;
+// #ifdef IS_WINDOWS
+//    output_path = "D:/5_file/2_readfile/geojson/multi_point/fitting_examples_res_1.geojson";
+// #else
+//    output_path = "/home/wyj/myself/2_data/2_geojson/multi_point/20250603_res.geojson";
+// #endif
+//    studio_geo_utils::write_geo_coll(output_path, res_collections);
+//
+//    studio_geo_utils::destroy_gdal_env();
+//
+//    return 0;
+//}
+
+// ///////////////////// PID算法 /////////////////////
+// int main()
+// {
+//     double ini = 2, goal = 50, p = 0.5, i = 0.001, d = 0.6;
+//     studio_pid pid;
+//
+//     // 初始化PID
+//     init_pid(&pid, ini, goal, p, i, d, 100, 200);
+//
+//     // 第一阶段控制到 50
+//     double current_value = ini;
+//     for (int i = 0; i < 20; ++i)
+//     {
+//         double output = compute_pid(&pid, current_value);
+//         current_value += output; // 积分项和微分项会影响下一次计算
+//         printf("Step %d: 目标=%.2f, 当前值=%.2f, 输出=%.2f\n", i + 1, get_goal(&pid), current_value, output);
+//     }
+//
+//     printf("到达目标值,开始第二阶段控制\n");
+//     // 改变目标值并重置PID状态
+//     set_goal(&pid, 100);
+//
+//     // 第二阶段控制到 100
+//     for (int i = 0; i < 20; ++i)
+//     {
+//         double output = compute_pid(&pid, current_value);
+//         current_value += output; // 积分项和微分项会影响下一次计算
+//         printf("Step %d: 目标=%.2f, 当前值=%.2f, 输出=%.2f\n", i + 1, get_goal(&pid), current_value, output);
+//     }
+//
+//     return 0;
+// }
+
+// ///////////////////// 移除离群点 /////////////////////
+// int main()
+// {
+//     printf("\n\n===================== %s =====================\n\n", __FILE__);
+//     studio_geo_utils::init_gdal_env();
+//     std::string path;
+// #ifdef IS_WINDOWS
+//     path = "D:/5_file/2_readfile/geojson/multi_point/fitting_examples.geojson";
+// #else
+//     path = "/home/wyj/myself/2_data/2_geojson/multi_point/fitting_examples.geojson";
+// #endif
+//
+//     std::cout << "path: " << path << std::endl;
+//     std::vector<studio_geo_coll> res_collections;
+//     std::vector<studio_geo_coll> collections;
+//     studio_geo_utils::read_geo_coll(path, collections);
+//
+//     for (auto &coll: collections)
+//     {
+//         studio_line_c line_c = studio_line_c_init();
+//         studio_line_to_c_line(coll.m_line, &line_c);
+//         std::cout << "原始数据量:" << coll.m_line.size() << std::endl;
+//
+//         studio_line_c outliers_line = studio_line_c_init();
+//         remove_outliers_c(&line_c, 1, &outliers_line);
+//
+//         studio_line simplified_line;
+//         c_line_to_studio_line(&outliers_line, simplified_line);
+//         std::cout << "简化后数据量:" << simplified_line.size() << std::endl;
+//
+//         studio_geo_coll temp;
+//         temp.m_type = enum_geometry_type::egtLineString;
+//         temp.m_line = simplified_line;
+//         res_collections.push_back(temp);
+//
+//         studio_line_c_destroy(&line_c);
+//         studio_line_c_destroy(&outliers_line);
+//
+//         break;
+//     }
+//
+//
+//     std::string output_path;
+// #ifdef IS_WINDOWS
+//     output_path = "D:/5_file/2_readfile/geojson/multi_point/remove_outliers.geojson";
+// #else
+//     output_path = "/home/wyj/myself/2_data/2_geojson/multi_point/remove_outliers.geojson";
+// #endif
+//     studio_geo_utils::write_geo_coll(output_path, res_collections);
+//      studio_geo_utils::destroy_gdal_env();
+//     return 0;
+// }
+
+
+// int main()
+//{
+//     printf("\n\n===================== %s =====================\n\n", __FILE__);
+//     studio_geo_utils::init_gdal_env();
+//     std::string path;
+// #ifdef IS_WINDOWS
+//     path = "D:/5_file/2_readfile/geojson/multi_point/fitting_examples.geojson";
+// #else
+//     path = "/home/wyj/myself/2_data/2_geojson/multi_point/fitting_examples.geojson";
+// #endif
+
+//    std::cout << "path: " << path << std::endl;
+//    std::vector<studio_geo_coll> res_collections;
+//    std::vector<studio_geo_coll> collections;
+//    studio_geo_utils::read_geo_coll(path, collections);
+//    for (auto &coll : collections)
+//    {
+//        // ------------- 转换为高斯投影 -------------
+//        studio_line gauss_line;
+//        double central = static_cast<int>(coll.m_line[0].x / 3) * 3;
+//        for (auto &point : coll.m_line)
+//        {
+//            double gx = 0.0;
+//            double gy = 0.0;
+//            lonlat_to_gauss(central, point.x, point.y, &gx, &gy);
+//            gauss_line.push_back(studio_point(gx, gy));
+//        }
+//        studio_line_c gauss_line_c = studio_line_c_init();
+//        studio_line_c vac_gauss_line_c = studio_line_c_init();
+//        studio_line_to_c_line(gauss_line, &gauss_line_c);
+
+//        // 简化线段,目标点数为28个
+//        int max_points = 28;
+//        double epsilon = 1.0;
+
+//        bool res = line_vacuate_c(&gauss_line_c, max_points, epsilon, &vac_gauss_line_c);
+//        if (!res)
+//        {
+//            std::cout << "Failed to simplify line." << std::endl;
+//            return 1;
+//        }
+
+//        // 高斯投影在转回经纬度
+//        studio_line simplified_line;
+//        for (int i = 0; i < vac_gauss_line_c.size; i++)
+//        {
+//            double lon = 0.0;
+//            double lat = 0.0;
+//            const studio_point_c *p = studio_line_c_get_point(&vac_gauss_line_c, i);
+//            gauss_to_lonlat(central, p->x, p->y, &lon, &lat);
+//            simplified_line.push_back(studio_point(lon, lat));
+//        }
+
+//        studio_geo_coll temp;
+//        temp.m_type = enum_geometry_type::egtLineString;
+//        temp.m_line = simplified_line;
+//        res_collections.push_back(temp);
+
+//        studio_line_c_destroy(&gauss_line_c);
+//        studio_line_c_destroy(&vac_gauss_line_c);
+
+//        break;
+//    }
+//    std::string output_path;
+// #ifdef IS_WINDOWS
+//    output_path = "D:/5_file/2_readfile/geojson/multi_point/fitting_examples_res_1.geojson";
+// #else
+//    output_path = "/home/wyj/myself/2_data/2_geojson/multi_point/fitting_examples_res_1_c.geojson";
+// #endif
+//    studio_geo_utils::write_geo_coll(output_path, res_collections);
+
+//    studio_geo_utils::destroy_gdal_env();
+
+//    return 0;
+//}