使用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;
下面是正式的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; }
实现调用
// 获取单例实例并使用 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字符串
-
坐标系修改:支持在打开文件后修改坐标系
- 单例实现:方便调用,一体化管理
-
完整的编码转换函数:
-
添加了
UTF8ToGBK和GBKToUTF8函数,使用Windows API进行准确的编码转换 -
改进了
ConvertEncoding函数,根据当前编码设置进行适当的转换
-
-
属性值编码处理:
-
在
SetField和AddFeature方法中,确保所有字符串属性值都经过编码转换 -
在
CopyAttributes方法中,对复制的字符串属性也进行编码转换
-
-
字段名编码处理:
-
在创建字段时,对字段名也进行编码转换,确保中文字段名正确显示
-

浙公网安备 33010602011771号