最近在搞一个模块,要用到shp的读写,发现读取shp这部分,大兄弟们~都已经给出自己的经验,但是在写shp这部分却没有相关参考,
导致自己花了一段时间在研究shp上,根据很多朋友给出的shp文件结构,哪几个字节代表什么意思,慢慢摸索,然后简易实现了shp的写
操作,但是这任然不能满足项目需要,简直就是弱鸡呀~~,后来不得不寻求三方库。
下面把shp的写操作贴上来,满足内部带有 环*n的多边形,三方库用的shplib。然后把自己之前摸索写的代码贴出来,其实都是按照什
么字节代表什么意思来写入,但是总归考虑不够全面,既是一个教训,也算是一个总结吧。
主要是几个shplib库函数的使用,但是重点是要准备这些函数的参数,也就是顶点序列,因为要满足带环的多边形,所以要求外环顶点
顺时针,内环为逆时针,顺时针还是逆时针的判断我这里用到了CGAL,当然也有其他方法。然后要注意顶点首尾相连,即同一个点。
bool writeToShp(string outPutShpFile, vector<Polygon_with_holes_2> vecQueryPolygon, std::string strResultPath, double offx, double offy)
{
//输出的shp
string shpFileName = outPutShpFile;
string tmp = outPutShpFile.substr(0,outPutShpFile.find_last_of("\."));
string dbfFileName = tmp + ".dbf";
SHPHandle hShp = SHPCreate(shpFileName.c_str(), SHPT_POLYGON);
DBFHandle hDbf = DBFCreate(dbfFileName.c_str());
if (NULL == hShp || NULL == hDbf)
{
return false;
}
//
Pwh_list_2 resPolygonList;
for (size_t i = 0; i < vecQueryPolygon.size(); i++)
{
resPolygonList.push_back(Polygon_with_holes_2(vecQueryPolygon[i]));
}
//含有几个部分
//int nNumPart = 0; //= resPolygonList.size();
//每个部分起始点索引
//int* nPanStart = new int[nNumPart];
//memset(nPanStart, 0, nNumPart);
//nPanStart[0] = 0;
//int cnt = 0;
//int nNumTotalPoint = 0;
//Pwh_list_2::const_iterator ita;
//for (ita = resPolygonList.begin(); ita != resPolygonList.end(); ita++)
//{
// //外环
// Polygon_with_holes_2 poly_hole_2 = *ita;
// Polygon_2 resPolygon = poly_hole_2.outer_boundary();
// int nNumPoint_outer = resPolygon.size() + 1;//外环点数量,首尾点相同,加上尾点
// int nNumPoint_inner = 0;//内环
// for (Polygon_with_holes_2::Hole_iterator itr = poly_hole_2.holes_begin(); itr != poly_hole_2.holes_end(); itr++)
// {
// Polygon_2 innerPoly = *itr;
// nNumPoint_inner += innerPoly.size() + 1;
// }
// //当前部分所有点的量
// nNumTotalPoint += nNumPoint_outer + nNumPoint_inner;
// if (cnt == (nNumPart - 1))
// break;
//
// cnt++;
// nPanStart[cnt] = nNumTotalPoint;
//}
vector <int> vecPolygonHasProblem;
int nField = 0;
//it,每个部分
int cntex = 0;
Pwh_list_2::const_iterator it;
for (it = resPolygonList.begin(); it != resPolygonList.end(); it++)
{
cntex++;
//当前部分外环
Polygon_with_holes_2 poly_hole_2 = *it;
Polygon_2 resPolygon = poly_hole_2.outer_boundary();
//外环顺时针
//if (cntex == 99)
//{
// PrintPolygon(resPolygon, "c:/before.txt");
//}
//makePolygonClockWise(resPolygon);
if (resPolygon.is_counterclockwise_oriented())
{
std::vector<Point_2> pts;
pts.reserve(resPolygon.size() + 1);
pts.push_back(resPolygon[0]);
for (int j = resPolygon.size() - 1; j >= 0; j--)
{
pts.push_back(resPolygon[j]);
}
pts.pop_back();
resPolygon = Polygon_2(pts.begin(), pts.end());
}
//PrintPolygon(resPolygon, "c:/after.txt");
//外环点数量,首尾点相同,加上尾点
int nNumPoint_outer = resPolygon.size() + 1;
//
int nNumPart = 1;
int nNumPoint_inner = 0;//内环点数量
for (Polygon_with_holes_2::Hole_iterator itr = poly_hole_2.holes_begin(); itr != poly_hole_2.holes_end(); itr++)
{
Polygon_2 innerPoly = *itr;
nNumPoint_inner += innerPoly.size() + 1;
nNumPart++;
}
//当前多边形由几个小部分组成,比如内部存在环
int* nPanStart = new int[nNumPart];
memset(nPanStart, 0, nNumPart);
nPanStart[0] = 0;
//当前部分所有点
int nNumTotalPoint = nNumPoint_outer + nNumPoint_inner;
vector <double> vecX;
vector <double> vecY;
vecX.reserve(nNumTotalPoint);
vecY.reserve(nNumTotalPoint);
double* pdx = new double[nNumTotalPoint];
double* pdy = new double[nNumTotalPoint];
memset(pdx, 0, nNumTotalPoint);
memset(pdy, 0, nNumTotalPoint);
int nNumPoint_current = 0;
//取得外环所有点
for (size_t i = 0; i < nNumPoint_outer - 1; i++)
{
pdx[i + nNumPoint_current] = resPolygon[i].hx().doubleValue() + offx;
pdy[i + nNumPoint_current] = resPolygon[i].hy().doubleValue() + offy;
vecX.push_back(resPolygon[i].hx().doubleValue() + offx);
vecY.push_back(resPolygon[i].hy().doubleValue() + offy);
//vecDD.push_back(pdx[i + nNumPoint_current]);
if (resPolygon.size() - 1 == i)
{
pdx[i + nNumPoint_current + 1] = resPolygon[0].hx().doubleValue() + offx;
pdy[i + nNumPoint_current + 1] = resPolygon[0].hy().doubleValue() + offy;
//vecDD.push_back(pdx[i + nNumPoint_current]);
vecX.push_back(resPolygon[0].hx().doubleValue() + offx);
vecY.push_back(resPolygon[0].hy().doubleValue() + offy);
}
}
//ofstream fout("c:/a.txt");
//for (size_t i = 0; i < nNumPoint_outer; i++)
//{
// fout << "\t" << pdx[i + nNumPoint_current] << "\t" << pdy[i + nNumPoint_current] << "\n";
//}
//fout.close();
//
nNumPoint_current += nNumPoint_outer;
//取得内环所有点
int cnt = 1;
int nNumPoint_in = 0;
for (Polygon_with_holes_2::Hole_iterator itr = poly_hole_2.holes_begin(); itr != poly_hole_2.holes_end(); itr++)
{
nPanStart[cnt] = nNumPoint_current;
Polygon_2 innerPoly = *itr;
//内环逆时针
//PrintPolygon(innerPoly, "c:/before.txt");
//kePolygonCounterClockWise(innerPoly);
if (innerPoly.is_clockwise_oriented())//如果是顺时针就将其逆时针
{
std::vector<Point_2> pts;
pts.reserve(innerPoly.size());
pts.push_back(innerPoly[0]);
for (int j = innerPoly.size() - 1; j >= 0; j--)
{
pts.push_back(innerPoly[j]);
}
pts.pop_back();
innerPoly = Polygon_2(pts.begin(), pts.end());
}
//PrintPolygon(innerPoly, "c:/after.txt");
nNumPoint_in += innerPoly.size() + 1;
for (size_t i = 0; i < /*nNumPoint_in - 1*/innerPoly.size(); i++)
{
pdx[i + nNumPoint_current] = innerPoly[i].hx().doubleValue() + offx;
pdy[i + nNumPoint_current] = innerPoly[i].hy().doubleValue() + offy;
//vecDD.push_back(pdx[i + nNumPoint_current]);
vecX.push_back(resPolygon[i].hx().doubleValue() + offx);
vecY.push_back(resPolygon[i].hy().doubleValue() + offy);
if (innerPoly.size() - 1 == i)
{
pdx[i + nNumPoint_current + 1] = innerPoly[0].hx().doubleValue() + offx;
pdy[i + nNumPoint_current + 1] = innerPoly[0].hy().doubleValue() + offy;
//vecDD.push_back(pdx[i + nNumPoint_current + 1]);
vecX.push_back(resPolygon[0].hx().doubleValue() + offx);
vecY.push_back(resPolygon[0].hy().doubleValue() + offy);
}
}
nNumPoint_current += /*nNumPoint_in*/innerPoly.size() + 1;
cnt++;
}
//判断当前多边形,不满足条件的将不被写入shp
vector<Point_2> pts;
pts.reserve(nNumTotalPoint);
for (int i = 0; i < nNumTotalPoint; i++)
{
pts.push_back(Point_2(vecX[i] - offx, vecY[i] - offy));//svn
}
如果is_simple,Polygon_hole2_将为空
//Polygon_with_holes_2 Polygon_hole2_ = createPolywithHole(pts);
//if (Polygon_hole2_.is_unbounded())
//{
// for (int j = 0; j < nNumTotalPoint; j++)
// {
// pdx[j] = 0;
// pdy[j] = 0;
// }
// vecX.clear();
// vecY.clear();
// vector <double>().swap(vecX);
// vector <double>().swap(vecY);
// vector<Point_2>().swap(pts);
// vecPolygonHasProblem.push_back(cntex - 1);
// //ofs << "ID:"<<cntex - 1 << endl;
// continue;
//}
//shp,创建记录
SHPObject* ishpobj = SHPCreateObject(SHPT_POLYGON, -1/*-1 means unknown*/, nNumPart, nPanStart, NULL, nNumTotalPoint, pdx, pdy, NULL, NULL);
//shp,重新计算包围盒
SHPComputeExtents(ishpobj);
//shp,写入记录
SHPWriteObject(hShp, -1, ishpobj);
nField++;
}
//dbf,添加字段
int ifield = DBFAddNativeFieldType(hDbf, "ID", 'N', 6, 2);
//dbf,写入字段值
int nrecordCnt = 0;
for (int i = 0; i < nField; i++)
{
DBFWriteIntegerAttribute(hDbf, i, ifield, i);
nrecordCnt = DBFGetRecordCount(hDbf);
}
//关闭
SHPClose(hShp);
DBFClose(hDbf);
将不能处理的polygon的ID输入文本
//string strTip = strResultPath + "tip.txt";
//FILE* fp = fopen(strTip.c_str(), "a+");
//if (fp == NULL)
//{
// cout << "文件打开失败:" << strTip << endl;
// return false;
//}
//ofstream ofs;
//ofs.open(strTip, ios::app);
//char buf[256];
//time_t tt = time(NULL);//返回的只是一个时间戳
//tm* t = localtime(&tt);
//sprintf_s(buf, sizeof(buf), "%d-%02d-%02d %02d:%02d:%02d\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
//ofs << buf;
//memset(buf, 0, 256);
//
//ofs << "数目:" << vecPolygonHasProblem.size() << endl;
//for (int q = 0; q < vecPolygonHasProblem.size(); q++)
//{
// ofs << "ID:" << vecPolygonHasProblem[q] << endl;
//}
//ofs << endl;
//ofs.close();
//
return true;
}
另外说一下shp的读操作,很多三方库也有,比如osg的shp插件,能满足一般需要,但是还是有某些地方不太完善,不过仍然值得学习,比如
代码风格,模板的使用等。