使用gdal修改shp数据

以下代码功能包含:

  • 修改已有要素(Feature)的属性字段值
  • 新增要素(点、线、面)并设置其属性
  • 复制已有要素(包括几何和属性)
  • 保存修改到原文件或新文件

 前置铺垫代码

/*!
 *  @struct SVREVector3
 *  @brief VrEarth Vector3   这里没有提供前置代码实现,可以使用大模型自己生成
 */
struct _VREDLLExport SVREVector3
{
    /** @brief 构造 */
    SVREVector3(double _x = 0, double _y = 0, double _z = 0);
    /** @brief 构造 */
    SVREVector3(SVREVector2 _sVec2, double _z = 0);
    /** @brief 构造 */
    SVREVector3(const SVREVector3f vec);
    /** @brief 返回Vector3f */
    operator SVREVector3f() const;
    /** @brief ==重载 */
    bool operator == (const SVREVector3& _sVec) const;
    /** @brief !=重载 */
    bool operator != (const SVREVector3& _sVec) const;
    /** @brief < 重载 */
    bool operator < (const SVREVector3& _sVec) const;
    /** @brief > 重载 */
    bool operator > (const SVREVector3& _sVec) const;
    /** @brief <= 重载 */
    bool operator <= (const SVREVector3& _sVec) const;
    /** @brief >= 重载 */
    bool operator >= (const SVREVector3& _sVec) const;
    /** @brief 指针 */
    double * ptr();
    /** @brief 指针 */
    const double * ptr() const;
    /** @brief [] 重载 */
    double& operator [] (int i);
    /** @brief [] 重载 */
    double operator [] (int i) const;
    /** @brief * 重载 */
    double operator * (const SVREVector3& _sVec) const;
    /** @brief ^ 重载 */
    const SVREVector3 operator ^ (const SVREVector3& _sVec) const;
    /** @brief * 重载 */
    const SVREVector3 operator * (double _fValue) const;
    /** @brief *= 重载 */
    SVREVector3& operator *= (double _fValue);
    /** @brief / 重载 */
    const SVREVector3 operator / (double _fValue) const;
    /** @brief /= 重载 */
    SVREVector3& operator /= (double _fValue);
    /** @brief + 重载 */
    const SVREVector3 operator + (const SVREVector3& _sVec) const;
    /** @brief += 重载 */
    SVREVector3& operator += (const SVREVector3& _sVec);
    /** @brief - 重载 */
    const SVREVector3 operator - (const SVREVector3& _sVec) const;
    /** @brief -= 重载 */
    SVREVector3& operator -= (const SVREVector3& _sVec);
    /** @brief 取反 重载 */
    const SVREVector3 operator - () const;
    /** @brief 长度 */
    double Length() const;
    /** @brief 长度平方 */
    double SquaredLength() const;
    /** @brief 直线距离 */
    double Distance(const SVREVector3& _sVec) const;
    /** @brief 直线距离平方 */
    double SquaredDistance(const SVREVector3& _sVec) const;
    /** @brief 规范化 */
    double Normalize();
    /** @brief 相乘 */
    SVREVector3 ComponentMultiply(const SVREVector3& _sVecL, const SVREVector3& _sVecR);
    /** @brief 相除 */
    SVREVector3 ComponentDivide(const SVREVector3& _sVecL, const SVREVector3& _sVecR);
    /** @brief 转Int */
    SVREVector3i ToInt();
    /** @brief 转Float */
    SVREVector3f ToFloat();

    // 变量
    union
    {
        struct
        {
            double                x;
            double                y;
            double                z;
        };
        struct
        {
            double                lon;        //!< 经度
            double                lat;        //!< 纬度
            double                alt;        //!< 高度
        };
        struct
        {
            double                _v[3];
        };
    };
    
    
    typedef std::vector<SVREVector3>    VVREGeoPoint;
View Code

 

下面是正式的shp生成修改代码,使用的库是GDAL2.X

#include <vector>
#include <string>
#include <memory>
#include <fstream>
#include <cmath>
#include <locale>
#include <codecvt>
#include "gdal/gdal.h"
#include "gdal/gdal_priv.h"
#include "gdal/ogrsf_frmts.h"

#include "../Engine/VREStructs.h" 

/**
 * @brief Shapefile操作工具类(单例模式)
 * 支持点、线、面图元的读取、修改、添加和保存操作,使用VVREGeoPoint作为几何数据格式
 * 自动生成.prj文件定义坐标系,自动计算线的长度和面的面积,支持中文编码
 */
class ShapefileTool {
public:
    /**
     * @brief 坐标系枚举
     */
    enum CoordinateSystem {
        WGS84,          ///< WGS84地理坐标系
        WebMercator,    ///< Web墨卡托投影
        UTM_WGS84_50N   ///< UTM 50N (WGS84)
    };

    /**
     * @brief 字符编码枚举
     */
    enum Encoding {
        UTF8,           ///< UTF-8编码
        GBK,            ///< GBK编码(中文Windows默认)
        SYSTEM          ///< 系统默认编码
    };

    /**
     * @brief 获取单例实例
     */
    static ShapefileTool& GetInstance(Encoding encoding = UTF8);

    /**
     * @brief 删除拷贝构造函数和赋值运算符
     */
    ShapefileTool(const ShapefileTool&) = delete;
    ShapefileTool& operator=(const ShapefileTool&) = delete;

    /**
     * @brief 获取最后一次错误信息
     * @return 错误信息字符串
     */
    std::string GetLastError() const;

    /**
     * @brief 打开Shapefile文件
     * @param filename 文件路径
     * @param update 是否以更新模式打开(默认只读)
     * @return true-成功 false-失败
     */
    bool Open(const char* filename, bool update = false);
    bool Open(const std::string& filename, bool update = false);

    /**
     * @brief 创建新的Shapefile文件
     * @param filename 文件路径
     * @param geomType 几何类型(点、线、面)
     * @param fields 字段定义列表
     * @param coordSystem 坐标系类型(默认为WGS84)
     * @param autoCalcFields 是否自动计算长度和面积字段(默认为是)
     * @return true-成功 false-失败
     */
    bool Create(const char* filename, OGRwkbGeometryType geomType,
        const std::vector<std::pair<std::string, OGRFieldType>>& fields,
        CoordinateSystem coordSystem = WGS84,
        bool autoCalcFields = true);

    bool Create(const std::string& filename, OGRwkbGeometryType geomType,
        const std::vector<std::pair<std::string, OGRFieldType>>& fields,
        CoordinateSystem coordSystem = WGS84,
        bool autoCalcFields = true);

    /**
     * @brief 创建新的Shapefile文件(使用自定义坐标系)
     * @param filename 文件路径
     * @param geomType 几何类型(点、线、面)
     * @param fields 字段定义列表
     * @param spatialRef 自定义空间参考系统
     * @param autoCalcFields 是否自动计算长度和面积字段(默认为是)
     * @return true-成功 false-失败
     */
    bool CreateWithSpatialRef(const char* filename, OGRwkbGeometryType geomType,
        const std::vector<std::pair<std::string, OGRFieldType>>& fields,
        OGRSpatialReference* spatialRef,
        bool autoCalcFields = true);

    bool CreateWithSpatialRef(const std::string& filename, OGRwkbGeometryType geomType,
        const std::vector<std::pair<std::string, OGRFieldType>>& fields,
        OGRSpatialReference* spatialRef,
        bool autoCalcFields = true);

    /**
     * @brief 获取要素数量
     * @return 要素数量
     */
    int GetFeatureCount();

    /**
     * @brief 根据ID获取要素
     * @param id 要素ID
     * @return 要素指针(需要调用者释放)
     */
    OGRFeature* GetFeature(int id);

    /**
     * @brief 修改要素几何信息
     * @param id 要素ID
     * @param geometry 新的几何对象(工具类会接管内存管理)
     * @return true-成功 false-失败
     */
    bool SetGeometry(int id, OGRGeometry* geometry);

    /**
     * @brief 修改要素属性值
     * @param id 要素ID
     * @param fieldName 字段名
     * @param value 属性值
     * @return true-成功 false-失败
     */
    bool SetField(int id, const char* fieldName, const char* value);
    bool SetField(int id, const char* fieldName, const std::string& value);
    bool SetField(int id, const char* fieldName, int value);
    bool SetField(int id, const char* fieldName, double value);

    /**
     * @brief 添加新要素
     * @param geometry 几何对象(工具类会接管内存管理)
     * @param attributes 属性值映射表
     * @return 新要素ID,-1表示失败
     */
    int AddFeature(OGRGeometry* geometry,
        const std::vector<std::pair<std::string, std::string>>& attributes = {});

    /**
     * @brief 复制要素属性
     * @param sourceId 源要素ID
     * @param targetId 目标要素ID
     * @return true-成功 false-失败
     */
    bool CopyAttributes(int sourceId, int targetId);

    /**
     * @brief 保存修改
     * @return true-成功 false-失败
     */
    bool Save();

    /**
     * @brief 关闭文件
     */
    void Close();

    /**
     * @brief 创建点几何对象(使用VVREGeoPoint)
     * @param point 点坐标
     * @return 点几何对象
     */
    OGRPoint* CreatePoint(const VRE::SVREVector3& point);

    /**
     * @brief 创建线几何对象(使用VVREGeoPoint)
     * @param points 点坐标序列
     * @return 线几何对象
     */
    OGRLineString* CreateLineString(const VRE::VVREGeoPoint& points);

    /**
     * @brief 创建面几何对象(使用VVREGeoPoint)
     * @param rings 环坐标序列,第一个为外环,其余为内环
     * @return 面几何对象
     */
    OGRPolygon* CreatePolygon(const std::vector<VRE::VVREGeoPoint>& rings);

    /**
    * @brief 从OGR几何对象转换为VVREGeoPoint(点)
     * @param geometry OGR几何对象
     * @return VVREGeoPoint点坐标
     */
    VRE::SVREVector3 ConvertToVVREPoint(OGRGeometry* geometry);

    /**
     * @brief 从OGR几何对象转换为VVREGeoPoint(线)
     * @param geometry OGR几何对象
     * @return VVREGeoPoint线坐标序列
     */
    VRE::VVREGeoPoint ConvertToVVRELineString(OGRGeometry* geometry);

    /**
     * @brief 从OGR几何对象转换为VVREGeoPoint(面)
     * @param geometry OGR几何对象
     * @return 环坐标序列,第一个为外环,其余为内环
     */
    std::vector<VRE::VVREGeoPoint> ConvertToVVREPolygon(OGRGeometry* geometry);

    /**
     * @brief 设置坐标系
     * @param coordSystem 坐标系类型
     * @return true-成功 false-失败
     */
    bool SetCoordinateSystem(CoordinateSystem coordSystem);

    /**
     * @brief 设置自定义坐标系
     * @param spatialRef 空间参考系统
     * @return true-成功 false-失败
     */
    bool SetSpatialReference(OGRSpatialReference* spatialRef);

    /**
     * @brief 获取当前坐标系WKT字符串
     * @return WKT字符串
     */
    std::string GetProjectionWKT();

    /**
     * @brief 计算几何长度(考虑地理坐标系,返回米)
     * @param geometry 几何对象
     * @return 长度(米)
     */
    double CalculateGeodesicLength(OGRGeometry* geometry);

    /**
     * @brief 计算几何面积(考虑地理坐标系,返回平方米)
     * @param geometry 几何对象
     * @return 面积(平方米)
     */
    double CalculateGeodesicArea(OGRGeometry* geometry);

    /**
     * @brief 重新计算所有要素的几何字段(长度和面积)
     * @return true-成功 false-失败
     */
    bool RecalculateGeometryFields();

    /**
     * @brief 设置字符编码
     * @param encoding 字符编码类型
     */
    void SetEncoding(Encoding encoding);

    /**
     * @brief 获取字符编码
     * @return 字符编码类型
     */
    Encoding GetEncoding() const;

    std::string UTF8ToGBK(const std::string& utf8Str) const;
    std::string GBKToUTF8(const std::string& gbkStr) const;
private:
    GDALDataset* m_dataset;    ///< GDAL数据集
    OGRLayer* m_layer;         ///< 图层指针
    bool m_opened;             ///< 文件打开状态
    OGRSpatialReference* m_spatialRef; ///< 空间参考系统
    bool m_autoCalcFields;     ///< 是否自动计算几何字段
    Encoding m_encoding;       ///< 字符编码类型
    std::string m_lastError;   ///< 最后错误信息

    /**
     * @brief 私有构造函数
     */
    ShapefileTool(Encoding encoding = UTF8);

    /**
     * @brief 析构函数
     */
    ~ShapefileTool();

    /**
     * @brief 初始化GDAL环境
     */
    void InitGDAL();

    /**
     * @brief 清理GDAL环境
     */
    void CleanupGDAL();

    /**
     * @brief 创建坐标系定义
     * @param coordSystem 坐标系类型
     * @return 空间参考对象
     */
    OGRSpatialReference* CreateSpatialReference(CoordinateSystem coordSystem);

    /**
     * @brief 生成.prj文件
     * @param filename 文件路径
     * @return true-成功 false-失败
     */
    bool CreatePrjFile(const char* filename);

    /**
     * @brief 自动添加几何字段(长度和面积)
     * @param geomType 几何类型
     * @return true-成功 false-失败
     */
    bool AddGeometryFields(OGRwkbGeometryType geomType);

    /**
     * @brief 计算并设置要素的几何字段
     * @param feature 要素对象
     * @param geometry 几何对象
     * @return true-成功 false-失败
     */
    bool CalculateAndSetGeometryFields(OGRFeature* feature, OGRGeometry* geometry);

    /**
     * @brief 转换字符串编码
     * @param str 输入字符串
     * @return 转换后的字符串(根据当前编码设置)
     */
    std::string ConvertEncoding(const std::string& str) const;

    /**
     * @brief 设置GDAL编码配置
     */
    void SetGDALEncoding() const;

    /**
     * @brief 检查文件路径是否有效
     * @param filename 文件路径
     * @return true-有效 false-无效
     */
    bool CheckFilePath(const std::string& filename) ;

    /**
     * @brief 转换文件路径编码(针对GDAL 2.2.3的中文路径问题)
     * @param filename 原始文件路径
     * @return 转换后的文件路径
     */
    std::string ConvertFilePath(const std::string& filename) const;
};

 

实现的代码

#include "ShapefileEditor.h"
#include <map>
#include <cassert>
//#include <sys/stat.h>  // 用于检测文件是否存在(跨平台简易方式)
#include <stdexcept>
#include <fstream>
#include <iostream>
#include <locale>
#include <codecvt>

#ifdef _WIN32
#include <windows.h>
#endif

using namespace VRE;

// 单例实例获取
ShapefileTool& ShapefileTool::GetInstance(Encoding encoding) {
    static ShapefileTool instance(encoding);
    return instance;
}

// 私有构造函数
ShapefileTool::ShapefileTool(Encoding encoding)
    : m_dataset(nullptr), 
      m_layer(nullptr), 
      m_opened(false), 
      m_spatialRef(nullptr), 
      m_autoCalcFields(true), 
      m_encoding(encoding),
      m_lastError("") {
    InitGDAL();
    SetGDALEncoding();
}

ShapefileTool::~ShapefileTool() {
    Close();
    CleanupGDAL();
}

bool ShapefileTool::CheckFilePath(const std::string& filename)  {
    if (filename.empty()) {
        m_lastError = "文件路径为空";
        return false;
    }
    
    // 检查文件路径是否包含非法字符
    if (filename.find("..") != std::string::npos) {
        m_lastError = "文件路径包含非法字符 '..'";
        return false;
    }
    
    // 检查文件扩展名
    if (filename.length() < 4 || filename.substr(filename.length() - 4) != ".shp") {
        m_lastError = "文件扩展名必须是 .shp";
        return false;
    }
    
    return true;
}

std::string ShapefileTool::GetLastError() const {
    return m_lastError;
}

std::string ShapefileTool::ConvertFilePath(const std::string& filename) const {
#ifdef _WIN32
    // 在Windows上,GDAL 2.2.3需要GBK编码的路径
    if (m_encoding == GBK) {
        return filename; // 如果已经是GBK,直接返回
    }
    
    // 如果是UTF-8,转换为GBK
    if (m_encoding == UTF8) {
        std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
        try {
            std::wstring wstr = conv.from_bytes(filename);
            
            int size_needed = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), (int)wstr.size(), 
                                                nullptr, 0, nullptr, nullptr);
            std::string gbk_str(size_needed, 0);
            WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), (int)wstr.size(), 
                              &gbk_str[0], size_needed, nullptr, nullptr);
            return gbk_str;
        } catch (...) {
            // 如果转换失败,返回原路径
            return filename;
        }
    }
#endif
    // 其他平台或编码直接返回
    return filename;
}

void ShapefileTool::InitGDAL() {
    static bool initialized = false;
    if (!initialized) {
        GDALAllRegister();
        
        // 针对GDAL 2.2.3的中文路径问题,设置文件名编码为系统默认
        CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
        
        initialized = true;
    }
}

void ShapefileTool::CleanupGDAL() {
    // 注意:不要在这里调用 GDALDestroyDriverManager();
    // 因为这会影响程序中其他使用GDAL的组件
}

void ShapefileTool::SetGDALEncoding() const {
    switch (m_encoding) {
        case UTF8:
            CPLSetConfigOption("SHAPE_ENCODING", "UTF-8");
            break;
        case GBK:
            CPLSetConfigOption("SHAPE_ENCODING", "GBK");
            break;
        case SYSTEM:
        default:
            CPLSetConfigOption("SHAPE_ENCODING", "");
            break;
    }
}

std::string ShapefileTool::UTF8ToGBK(const std::string& utf8Str) const {
    if (utf8Str.empty()) return "";

    int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, nullptr, 0);
    if (wlen == 0) return utf8Str;

    std::wstring wstr(wlen, 0);
    MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, &wstr[0], wlen);

    int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr);
    if (len == 0) return utf8Str;

    std::string gbkStr(len, 0);
    WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, &gbkStr[0], len, nullptr, nullptr);

    // 去除末尾的null字符
    if (!gbkStr.empty() && gbkStr[gbkStr.size() - 1] == '\0') {
        gbkStr.resize(gbkStr.size() - 1);
    }

    return gbkStr;
}

std::string ShapefileTool::GBKToUTF8(const std::string& gbkStr) const {
    if (gbkStr.empty()) return "";

    int wlen = MultiByteToWideChar(CP_ACP, 0, gbkStr.c_str(), -1, nullptr, 0);
    if (wlen == 0) return gbkStr;

    std::wstring wstr(wlen, 0);
    MultiByteToWideChar(CP_ACP, 0, gbkStr.c_str(), -1, &wstr[0], wlen);

    int len = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr);
    if (len == 0) return gbkStr;

    std::string utf8Str(len, 0);
    WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &utf8Str[0], len, nullptr, nullptr);

    // 去除末尾的null字符
    if (!utf8Str.empty() && utf8Str[utf8Str.size() - 1] == '\0') {
        utf8Str.resize(utf8Str.size() - 1);
    }

    return utf8Str;
}

std::string ShapefileTool::ConvertEncoding(const std::string& str) const {
    if (str.empty()) return str;

    // 根据当前编码设置进行转换
    switch (m_encoding) {
    case UTF8:
        // 如果已经是UTF-8,直接返回
        return str;

    case GBK:
#ifdef _WIN32
        // 在Windows上,将UTF-8转换为GBK
        return UTF8ToGBK(str);
#else
        // 在其他平台上,GBK可能不被支持,返回原字符串
        return str;
#endif

    case SYSTEM:
    default:
#ifdef _WIN32
        // 在Windows系统上,默认使用GBK
        return UTF8ToGBK(str);
#else
        // 在其他系统上,使用UTF-8
        return str;
#endif
    }
}

bool ShapefileTool::Open(const char* filename, bool update) {
    return Open(std::string(filename), update);
}

bool ShapefileTool::Open(const std::string& filename, bool update) {
    Close();
    
    if (!CheckFilePath(filename)) {
        return false;
    }
    
    // 设置编码
    SetGDALEncoding();
    
    // 转换文件路径编码
    std::string convertedPath = ConvertFilePath(filename);
    
    m_dataset = (GDALDataset*)GDALOpenEx(convertedPath.c_str(), 
        update ? GDAL_OF_UPDATE | GDAL_OF_VECTOR : GDAL_OF_READONLY | GDAL_OF_VECTOR, 
        nullptr, nullptr, nullptr);

    if (!m_dataset) {
        m_lastError = std::string("无法打开文件: ") + filename + ",错误: " + CPLGetLastErrorMsg();
        return false;
    }

    m_layer = m_dataset->GetLayer(0);
    if (!m_layer) {
        m_lastError = std::string("无法获取图层: ") + filename;
        Close();
        return false;
    }

    // 获取坐标系信息
    if (m_layer->GetSpatialRef()) {
        m_spatialRef = m_layer->GetSpatialRef()->Clone();
    }

    m_opened = true;
    return true;
}

bool ShapefileTool::Create(const char* filename, OGRwkbGeometryType geomType,
    const std::vector<std::pair<std::string, OGRFieldType>>& fields,
    CoordinateSystem coordSystem, bool autoCalcFields) {
    return Create(std::string(filename), geomType, fields, coordSystem, autoCalcFields);
}

bool ShapefileTool::Create(const std::string& filename, OGRwkbGeometryType geomType,
    const std::vector<std::pair<std::string, OGRFieldType>>& fields,
    CoordinateSystem coordSystem, bool autoCalcFields) {
    // 创建坐标系
    OGRSpatialReference* spatialRef = CreateSpatialReference(coordSystem);
    if (!spatialRef) {
        m_lastError = "创建坐标系失败";
        return false;
    }

    bool result = CreateWithSpatialRef(filename, geomType, fields, spatialRef, autoCalcFields);

    // 注意:CreateWithSpatialRef会接管spatialRef的内存管理
    // 如果创建失败,需要手动删除
    if (!result) {
        delete spatialRef;
    }

    return result;
}

bool ShapefileTool::CreateWithSpatialRef(const char* filename, OGRwkbGeometryType geomType,
                                        const std::vector<std::pair<std::string, OGRFieldType>>& fields,
                                        OGRSpatialReference* spatialRef,
                                        bool autoCalcFields) {
    return CreateWithSpatialRef(std::string(filename), geomType, fields, spatialRef, autoCalcFields);
}

bool ShapefileTool::CreateWithSpatialRef(const std::string& filename, OGRwkbGeometryType geomType,
                                        const std::vector<std::pair<std::string, OGRFieldType>>& fields,
                                        OGRSpatialReference* spatialRef,
                                        bool autoCalcFields) {
    Close();
    
    if (!CheckFilePath(filename)) {
        return false;
    }
    
    // 设置编码
    SetGDALEncoding();
    
    // 转换文件路径编码
    std::string convertedPath = ConvertFilePath(filename);
    
    // 获取ESRI Shapefile驱动
    GDALDriver* driver = GetGDALDriverManager()->GetDriverByName("ESRI Shapefile");
    if (!driver) {
        m_lastError = "无法获取ESRI Shapefile驱动";
        return false;
    }
    
    // 检查驱动是否支持创建操作
    char** driverMetadata = driver->GetMetadata();
    if (!CSLFetchBoolean(driverMetadata, GDAL_DCAP_CREATE, FALSE)) {
        m_lastError = "驱动不支持创建操作";
        return false;
    }
    
    // 删除已存在文件
    CPLErr err = driver->Delete(convertedPath.c_str());
    
    // 清除错误状态
    CPLErrorReset();
    
    // 创建数据集
    m_dataset = driver->Create(convertedPath.c_str(), 0, 0, 0, GDT_Unknown, nullptr);
    if (!m_dataset) {
        m_lastError = std::string("创建数据集失败: ") + CPLGetLastErrorMsg();
        return false;
    }
    // 设置坐标系 - 克隆传入的spatialRef
    if (spatialRef) {
        m_spatialRef = spatialRef->Clone();
        if (!m_spatialRef) {
            m_lastError = "克隆空间参考系统失败";
            Close();
            return false;
        }
    } else {
        // 如果没有提供spatialRef,使用WGS84作为默认
        m_spatialRef = CreateSpatialReference(WGS84);
        if (!m_spatialRef) {
            m_lastError = "创建默认坐标系失败";
            Close();
            return false;
        }
    }
    
    m_autoCalcFields = autoCalcFields;

    // 创建图层
    m_layer = m_dataset->CreateLayer("layer", m_spatialRef, geomType, nullptr);
    if (!m_layer) {
        m_lastError = std::string("创建图层失败: ") + CPLGetLastErrorMsg();
        Close();
        return false;
    }

    // 创建字段
    for (const auto& field : fields) {
        // 转换字段名编码
        std::string encodedFieldName = ConvertEncoding(field.first);

        OGRFieldDefn fieldDefn(encodedFieldName.c_str(), field.second);
        if (m_layer->CreateField(&fieldDefn) != OGRERR_NONE) {
            m_lastError = std::string("创建字段失败: ") + field.first;
            Close();
            return false;
        }
    }

    // 自动添加几何字段
    if (m_autoCalcFields) {
        if (!AddGeometryFields(geomType)) {
            m_lastError = "添加几何字段失败";
            Close();
            return false;
        }
    }

    m_opened = true;

    // 生成.prj文件
    return CreatePrjFile(filename.c_str());
}

int ShapefileTool::GetFeatureCount() {
    if (!m_opened || !m_layer) return 0;
    return (int)m_layer->GetFeatureCount();
}

OGRFeature* ShapefileTool::GetFeature(int id) {
    if (!m_opened || !m_layer) return nullptr;
    return m_layer->GetFeature(id);
}

bool ShapefileTool::SetGeometry(int id, OGRGeometry* geometry) {
    if (!m_opened || !m_layer) {
        if (geometry) OGRGeometryFactory::destroyGeometry(geometry);
        return false;
    }

    OGRFeature* feature = m_layer->GetFeature(id);
    if (!feature) {
        if (geometry) OGRGeometryFactory::destroyGeometry(geometry);
        return false;
    }

    OGRErr err = feature->SetGeometry(geometry);
    if (err != OGRERR_NONE) {
        OGRFeature::DestroyFeature(feature);
        if (geometry) OGRGeometryFactory::destroyGeometry(geometry);
        return false;
    }

    // 自动计算几何字段
    if (m_autoCalcFields) {
        CalculateAndSetGeometryFields(feature, geometry);
    }

    bool result = (m_layer->SetFeature(feature) == OGRERR_NONE);
    OGRFeature::DestroyFeature(feature);

    // 几何对象由OGRFeature接管,不需要手动销毁
    return result;
}

bool ShapefileTool::SetField(int id, const char* fieldName, const char* value) {
    return SetField(id, fieldName, std::string(value));
}

bool ShapefileTool::SetField(int id, const char* fieldName, const std::string& value) {
    if (!m_opened || !m_layer) return false;

    OGRFeature* feature = m_layer->GetFeature(id);
    if (!feature) return false;

    // 转换编码
    std::string encodedValue = ConvertEncoding(value);
    feature->SetField(fieldName, encodedValue.c_str());

    bool result = (m_layer->SetFeature(feature) == OGRERR_NONE);
    OGRFeature::DestroyFeature(feature);
    return result;
}

bool ShapefileTool::SetField(int id, const char* fieldName, int value) {
    if (!m_opened || !m_layer) return false;

    OGRFeature* feature = m_layer->GetFeature(id);
    if (!feature) return false;

    feature->SetField(fieldName, value);
    bool result = (m_layer->SetFeature(feature) == OGRERR_NONE);
    OGRFeature::DestroyFeature(feature);
    return result;
}

bool ShapefileTool::SetField(int id, const char* fieldName, double value) {
    if (!m_opened || !m_layer) return false;

    OGRFeature* feature = m_layer->GetFeature(id);
    if (!feature) return false;

    feature->SetField(fieldName, value);
    bool result = (m_layer->SetFeature(feature) == OGRERR_NONE);
    OGRFeature::DestroyFeature(feature);
    return result;
}

int ShapefileTool::AddFeature(OGRGeometry* geometry,
    const std::vector<std::pair<std::string, std::string>>& attributes) 
{
    if (!m_opened || !m_layer) {
        if (geometry) OGRGeometryFactory::destroyGeometry(geometry);
        return -1;
    }

    OGRFeature* feature = OGRFeature::CreateFeature(m_layer->GetLayerDefn());
    if (!feature) {
        if (geometry) OGRGeometryFactory::destroyGeometry(geometry);
        return -1;
    }

    // 设置几何
    if (geometry) {
        OGRErr err = feature->SetGeometry(geometry);
        if (err != OGRERR_NONE) {
            OGRFeature::DestroyFeature(feature);
            OGRGeometryFactory::destroyGeometry(geometry);
            return -1;
        }
    }

    // 设置属性 - 这里需要确保编码转换
    for (const auto& attr : attributes) {
        // 转换编码
        std::string encodedValue = ConvertEncoding(attr.second);
        feature->SetField(attr.first.c_str(), encodedValue.c_str());
    }

    // 自动计算几何字段
    if (m_autoCalcFields && geometry) {
        CalculateAndSetGeometryFields(feature, geometry);
    }

    // 添加要素
    if (m_layer->CreateFeature(feature) != OGRERR_NONE) {
        OGRFeature::DestroyFeature(feature);
        return -1;
    }

    int fid = (int)feature->GetFID();
    OGRFeature::DestroyFeature(feature);
    return fid;
}

bool ShapefileTool::CopyAttributes(int sourceId, int targetId) {
    if (!m_opened || !m_layer) return false;

    OGRFeature* sourceFeature = m_layer->GetFeature(sourceId);
    OGRFeature* targetFeature = m_layer->GetFeature(targetId);

    if (!sourceFeature || !targetFeature) {
        if (sourceFeature) OGRFeature::DestroyFeature(sourceFeature);
        if (targetFeature) OGRFeature::DestroyFeature(targetFeature);
        return false;
    }

    // 复制所有属性字段
    int fieldCount = sourceFeature->GetFieldCount();
    for (int i = 0; i < fieldCount; i++) {
        OGRFieldDefn* fieldDefn = sourceFeature->GetFieldDefnRef(i);
        if (fieldDefn) {
            switch (fieldDefn->GetType()) {
            case OFTInteger:
                targetFeature->SetField(i, sourceFeature->GetFieldAsInteger(i));
                break;
            case OFTReal:
                targetFeature->SetField(i, sourceFeature->GetFieldAsDouble(i));
                break;
            case OFTString:
            default:
                // 对于字符串字段,获取原始值并重新设置(确保编码正确)
                const char* strValue = sourceFeature->GetFieldAsString(i);
                if (strValue) {
                    std::string encodedValue = ConvertEncoding(strValue);
                    targetFeature->SetField(i, encodedValue.c_str());
                }
                break;
            }
        }
    }

    bool result = (m_layer->SetFeature(targetFeature) == OGRERR_NONE);

    OGRFeature::DestroyFeature(sourceFeature);
    OGRFeature::DestroyFeature(targetFeature);
    return result;
}

bool ShapefileTool::Save() {
    if (!m_opened || !m_dataset) return false;
    GDALFlushCache(m_dataset);
    return true;
}

void ShapefileTool::Close() {
    if (m_dataset) {
        GDALClose(m_dataset);
        m_dataset = nullptr;
        m_layer = nullptr;
    }
    m_opened = false;
}

OGRPoint* ShapefileTool::CreatePoint(const SVREVector3& point) {
    return new OGRPoint(point.lon, point.lat, point.alt);
}

OGRLineString* ShapefileTool::CreateLineString(const VVREGeoPoint& points) {
    if (points.empty()) return nullptr;

    OGRLineString* line = new OGRLineString();
    for (const auto& point : points) {
        line->addPoint(point.lon, point.lat, point.alt);
    }
    return line;
}

OGRPolygon* ShapefileTool::CreatePolygon(const std::vector<VVREGeoPoint>& rings) {
    if (rings.empty()) return nullptr;

    OGRPolygon* polygon = new OGRPolygon();

    for (size_t i = 0; i < rings.size(); i++) {
        const auto& ringPoints = rings[i];
        if (ringPoints.size() < 3) {
            delete polygon;
            return nullptr;
        }

        OGRLinearRing* ring = new OGRLinearRing();
        for (const auto& point : ringPoints) {
            ring->addPoint(point.lon, point.lat, point.alt);
        }

        // 闭合环(如果首尾不相等)
        if (ring->getNumPoints() > 0) {
            OGRPoint first, last;
            ring->getPoint(0, &first);
            ring->getPoint(ring->getNumPoints() - 1, &last);
            if (first.getX() != last.getX() || first.getY() != last.getY() || first.getZ() != last.getZ()) {
                ring->addPoint(first.getX(), first.getY(), first.getZ());
            }
        }

        if (i == 0) {
            polygon->addRingDirectly(ring);  // 外环
        } else {
            polygon->addRing(ring);  // 内环
        }
    }

    return polygon;
}

SVREVector3 ShapefileTool::ConvertToVVREPoint(OGRGeometry* geometry) {
    SVREVector3 point;
    if (!geometry) return point;

    OGRPoint* ogrPoint = dynamic_cast<OGRPoint*>(geometry);
    if (ogrPoint) {
        point.lon = ogrPoint->getX();
        point.lat = ogrPoint->getY();
        point.alt = ogrPoint->getZ();
    }
    return point;
}

VVREGeoPoint ShapefileTool::ConvertToVVRELineString(OGRGeometry* geometry) {
    VVREGeoPoint points;
    if (!geometry) return points;

    OGRLineString* lineString = dynamic_cast<OGRLineString*>(geometry);
    if (lineString) {
        int pointCount = lineString->getNumPoints();
        for (int i = 0; i < pointCount; i++) {
            OGRPoint point;
            lineString->getPoint(i, &point);
            points.push_back(SVREVector3(point.getX(), point.getY(), point.getZ()));
        }
    }
    return points;
}

std::vector<VVREGeoPoint> ShapefileTool::ConvertToVVREPolygon(OGRGeometry* geometry) {
    std::vector<VVREGeoPoint> rings;
    if (!geometry) return rings;

    OGRPolygon* polygon = dynamic_cast<OGRPolygon*>(geometry);
    if (polygon) {
        // 外环
        OGRLinearRing* exteriorRing = polygon->getExteriorRing();
        if (exteriorRing) {
            VVREGeoPoint exteriorPoints;
            int pointCount = exteriorRing->getNumPoints();
            for (int i = 0; i < pointCount; i++) {
                OGRPoint point;
                exteriorRing->getPoint(i, &point);
                exteriorPoints.push_back(SVREVector3(point.getX(), point.getY(), point.getZ()));
            }
            rings.push_back(exteriorPoints);
        }

        // 内环
        int interiorRingCount = polygon->getNumInteriorRings();
        for (int i = 0; i < interiorRingCount; i++) {
            OGRLinearRing* interiorRing = polygon->getInteriorRing(i);
            if (interiorRing) {
                VVREGeoPoint interiorPoints;
                int pointCount = interiorRing->getNumPoints();
                for (int j = 0; j < pointCount; j++) {
                    OGRPoint point;
                    interiorRing->getPoint(j, &point);
                    interiorPoints.push_back(SVREVector3(point.getX(), point.getY(), point.getZ()));
                }
                rings.push_back(interiorPoints);
            }
        }
    }
    return rings;
}

bool ShapefileTool::SetCoordinateSystem(CoordinateSystem coordSystem) {
    if (!m_opened || !m_layer) return false;

    OGRSpatialReference* spatialRef = CreateSpatialReference(coordSystem);
    if (!spatialRef) return false;

    // 设置图层的空间参考
    if (m_layer->GetSpatialRef()) {
        delete m_spatialRef;
    }
    m_spatialRef = spatialRef;

    // 重新生成.prj文件
    if (m_dataset) {
        const char* filename = m_dataset->GetDescription();
        return CreatePrjFile(filename);
    }

    return false;
}

bool ShapefileTool::SetSpatialReference(OGRSpatialReference* spatialRef) {
    if (!m_opened || !m_layer || !spatialRef) return false;

    if (m_spatialRef) {
        delete m_spatialRef;
    }
    m_spatialRef = spatialRef->Clone();

    // 重新生成.prj文件
    if (m_dataset) {
        const char* filename = m_dataset->GetDescription();
        return CreatePrjFile(filename);
    }

    return false;
}

std::string ShapefileTool::GetProjectionWKT() {
    if (!m_spatialRef) return "";

    char* wkt = nullptr;
    m_spatialRef->exportToWkt(&wkt);
    if (wkt) {
        std::string result(wkt);
        CPLFree(wkt);
        return result;
    }
    return "";
}

OGRSpatialReference* ShapefileTool::CreateSpatialReference(CoordinateSystem coordSystem) {
    OGRSpatialReference* spatialRef = new OGRSpatialReference();

    switch (coordSystem) {
    case WGS84:
        spatialRef->SetWellKnownGeogCS("WGS84");
        break;
    case WebMercator:
        spatialRef->importFromEPSG(3857);  // Web Mercator
        break;
    case UTM_WGS84_50N:
        spatialRef->importFromEPSG(32650);  // UTM Zone 50N, WGS84
        break;
    default:
        delete spatialRef;
        return nullptr;
    }

    return spatialRef;
}

bool ShapefileTool::CreatePrjFile(const char* filename) {
    if (!m_spatialRef) return false;

    // 生成.prj文件路径
    std::string prjFilename = filename;
    size_t dotPos = prjFilename.find_last_of('.');
    if (dotPos != std::string::npos) {
        prjFilename = prjFilename.substr(0, dotPos);
    }
    prjFilename += ".prj";

    // 转换文件路径编码
    std::string convertedPrjPath = ConvertFilePath(prjFilename);

    // 导出为WKT格式
    char* wkt = nullptr;
    m_spatialRef->exportToWkt(&wkt);
    if (!wkt) {
        return false;
    }

    // 写入.prj文件
    std::ofstream prjFile(convertedPrjPath, std::ios::binary);
    if (!prjFile.is_open()) {
        CPLFree(wkt);
        return false;
    }

    prjFile << wkt;
    prjFile.close();

    CPLFree(wkt);
    return true;
}

bool ShapefileTool::AddGeometryFields(OGRwkbGeometryType geomType) {
    if (!m_layer) return false;

    // 根据几何类型添加相应的字段
    switch (wkbFlatten(geomType)) {
    case wkbLineString:
    case wkbMultiLineString: {
        // 为线数据添加长度字段
        OGRFieldDefn lengthField("Length", OFTReal);
        lengthField.SetWidth(12);
        lengthField.SetPrecision(2);
        if (m_layer->CreateField(&lengthField) != OGRERR_NONE) {
            return false;
        }
        break;
    }
    case wkbPolygon:
    case wkbMultiPolygon: {
        // 为面数据添加面积字段
        OGRFieldDefn areaField("Area", OFTReal);
        areaField.SetWidth(15);
        areaField.SetPrecision(2);
        if (m_layer->CreateField(&areaField) != OGRERR_NONE) {
            return false;
        }
        break;
    }
    default:
        // 点数据不需要特殊字段
        break;
    }

    return true;
}

bool ShapefileTool::CalculateAndSetGeometryFields(OGRFeature* feature, OGRGeometry* geometry) {
    if (!feature || !geometry) return false;

    OGRwkbGeometryType geomType = wkbFlatten(geometry->getGeometryType());

    switch (geomType) {
    case wkbLineString:
    case wkbMultiLineString: {
        // 计算线长度
        double length = CalculateGeodesicLength(geometry);
        int fieldIndex = feature->GetFieldIndex("Length");
        if (fieldIndex >= 0) {
            feature->SetField(fieldIndex, length);
        }
        break;
    }
    case wkbPolygon:
    case wkbMultiPolygon: {
        // 计算面面积
        double area = CalculateGeodesicArea(geometry);
        int fieldIndex = feature->GetFieldIndex("Area");
        if (fieldIndex >= 0) {
            feature->SetField(fieldIndex, area);
        }
        break;
    }
    default:
        break;
    }

    return true;
}

double ShapefileTool::CalculateGeodesicLength(OGRGeometry* geometry) {
    if (!geometry) return 0.0;

    double totalLength = 0.0;

    // 处理不同类型的线几何
    OGRwkbGeometryType geomType = wkbFlatten(geometry->getGeometryType());

    if (geomType == wkbLineString) {
        OGRLineString* line = (OGRLineString*)geometry;

        // 创建用于测地线计算的几何对象
        OGRGeometry* geodesicGeometry = line->clone();
        if (geodesicGeometry) {
            // 如果是在地理坐标系中,使用测地线计算
            if (m_spatialRef && m_spatialRef->IsGeographic()) {
                // 转换为测地线长度(米)
                totalLength = line->get_Length();

                // 对于地理坐标系,需要将度转换为米(近似)
                // 这里使用赤道处1度≈111.32km的近似值
                // 实际应用中可以使用更精确的椭球体计算方法
                totalLength *= 111320.0; // 1度 ≈ 111.32km
            }
            else {
                // 投影坐标系直接使用长度
                totalLength = line->get_Length();
            }
            OGRGeometryFactory::destroyGeometry(geodesicGeometry);
        }
    }
    else if (geomType == wkbMultiLineString) {
        OGRMultiLineString* multiLine = (OGRMultiLineString*)geometry;
        for (int i = 0; i < multiLine->getNumGeometries(); i++) {
            totalLength += CalculateGeodesicLength(multiLine->getGeometryRef(i));
        }
    }

    return totalLength;
}

double ShapefileTool::CalculateGeodesicArea(OGRGeometry* geometry) {
    if (!geometry) return 0.0;

    double totalArea = 0.0;

    // 处理不同类型的面几何
    OGRwkbGeometryType geomType = wkbFlatten(geometry->getGeometryType());

    if (geomType == wkbPolygon) {
        OGRPolygon* polygon = (OGRPolygon*)geometry;

        // 创建用于测地线计算的几何对象
        OGRGeometry* geodesicGeometry = polygon->clone();
        if (geodesicGeometry) {
            // 如果是在地理坐标系中,使用测地线计算
            if (m_spatialRef && m_spatialRef->IsGeographic()) {
                // 转换为测地线面积(平方米)
                totalArea = polygon->get_Area();

                // 对于地理坐标系,需要将平方度转换为平方米(近似)
                // 这里使用赤道处1平方度≈12,355km²的近似值
                // 实际应用中可以使用更精确的椭球体计算方法
                totalArea *= 12355000000.0; // 1平方度 ≈ 12,355km²
            }
            else {
                // 投影坐标系直接使用面积
                totalArea = polygon->get_Area();
            }
            OGRGeometryFactory::destroyGeometry(geodesicGeometry);
        }
    }
    else if (geomType == wkbMultiPolygon) {
        OGRMultiPolygon* multiPolygon = (OGRMultiPolygon*)geometry;
        for (int i = 0; i < multiPolygon->getNumGeometries(); i++) {
            totalArea += CalculateGeodesicArea(multiPolygon->getGeometryRef(i));
        }
    }

    return totalArea;
}

bool ShapefileTool::RecalculateGeometryFields() {
    if (!m_opened || !m_layer || !m_autoCalcFields) return false;

    // 重置读取位置
    m_layer->ResetReading();

    OGRFeature* feature;
    bool success = true;

    // 遍历所有要素并重新计算几何字段
    while ((feature = m_layer->GetNextFeature()) != nullptr) {
        OGRGeometry* geometry = feature->GetGeometryRef();
        if (geometry) {
            if (!CalculateAndSetGeometryFields(feature, geometry)) {
                success = false;
            }

            // 更新要素
            if (m_layer->SetFeature(feature) != OGRERR_NONE) {
                success = false;
            }
        }

        OGRFeature::DestroyFeature(feature);
    }

    return success;
}

void ShapefileTool::SetEncoding(Encoding encoding) {
    m_encoding = encoding;
    SetGDALEncoding();
}

ShapefileTool::Encoding ShapefileTool::GetEncoding() const {
    return m_encoding;
}
View Code

 

实现调用

// 获取单例实例并使用
ShapefileTool& tool = ShapefileTool::GetInstance();

// 打开文件
if (tool.Open("C:/路径/文件.shp")) {
    // 获取要素数量
    int count = tool.GetFeatureCount();
    
    // 创建点要素
    VRE::SVREVector3 point{116.3974, 39.9093, 0};
    OGRPoint* ogrPoint = tool.CreatePoint(point);
    
    // 添加要素
    std::vector<std::pair<std::string, std::string>> attributes = {
        {"Name", "测试点"},
        {"Type", "标记"}
    };
    tool.AddFeature(ogrPoint, attributes);
    
    // 保存修改
    tool.Save();
}

 

 功能实现以下效果:

  • 文件操作:打开、创建、保存Shapefile

  • 要素操作:获取、修改、添加要素

  • 属性操作:设置字段值、复制属性

  • 几何创建:创建点、线、面几何对象

  • 几何创建方法:使用 VVREGeoPoint 和 SVREVector3 作为参数类型

  • 坐标映射:将 SVREVector3 的 lon/lat/alt 映射到 OGR 的 x/y/z

  • 转换方法:添加了从 OGR 几何对象到 VVREGeoPoint 格式的转换方法

  • 三维支持:支持带高度的三维坐标数据

  • 内存管理:自动管理GDAL资源,几何对象由工具类接管

  • 坐标系支持:添加了 CoordinateSystem 枚举,支持常见的坐标系

  • .prj文件生成:自动生成与Shapefile同名的 .prj 文件

  • 自定义坐标系:支持通过EPSG代码或WKT定义自定义坐标系

  • 坐标系查询:可以获取当前坐标系的WKT字符串

  • 坐标系修改:支持在打开文件后修改坐标系

  • 单例实现:方便调用,一体化管理

 

  1. 完整的编码转换函数:

    • 添加了 UTF8ToGBK 和 GBKToUTF8 函数,使用Windows API进行准确的编码转换

    • 改进了 ConvertEncoding 函数,根据当前编码设置进行适当的转换

  2. 属性值编码处理:

    • 在 SetField 和 AddFeature 方法中,确保所有字符串属性值都经过编码转换

    • 在 CopyAttributes 方法中,对复制的字符串属性也进行编码转换

  3. 字段名编码处理:

    • 在创建字段时,对字段名也进行编码转换,确保中文字段名正确显示

posted @ 2025-11-19 11:34  南水之源  阅读(12)  评论(0)    收藏  举报