最近一个项目,需要提取多边形组成的网格的边界点(关键点),如图所示:
希望得到如下图所示的多边形:
已知条件:
1.每个多边形上的点坐标
2.多边形都是顺时针或者逆时针(相同)
3.多边形都是接壤的,没有独立的多边形(降低难度)
4.大多边形的外边界上一定存在一个边界点只属于一个多边形,(降低难度)
多方探索,最终找到了解决办法,算是自己想出来的一个小办法吧,其实条件3,4 都可以去掉,代码难度会高一点,但是沿用编程的思想还是可以解决,那么现在问题既然简单化,就开始吧。
条件4相当于告诉我们一个边界点,所以就从这个点出发,然后开始寻找其他边界点。
为了方便处理,我们对每一个多边形编号(顺序无所谓),然后分别记录每个点在哪几个多边形中,具体细节就赘述了,这里给出核心代码。
int GiveBoundPtFlag(std::vector<CRrlxContent>& vRrlxs, const std::vector<OneKey>& vKeyPts)
{
size_t nKeyPtNum = vKeyPts.size();
std::vector<geodetic> vPtsAll, vPt1D;
//第一遍保证基础的边界点被检测出来
for (int i = 0; i < nKeyPtNum; ++i)
{
geodetic& gKeyPt = vRrlxs[vKeyPts[i].nRrlxIndex].m_vKeyPoints[vKeyPts[i].nKeyIndex];
if (gKeyPt.vIndexs.size() < 3 && !(gKeyPt.nFlag & RND_G))
{
//第一步,标记所有的边界点,不仅仅是带KEY_G标志的关键点
geodetic& gKeyPoint = gKeyPt;
for (int m = 0; m < gKeyPoint.vIndexs.size(); ++m)
{
for (int n = 0; n < vRrlxs[gKeyPoint.vIndexs[m]].m_vKeyPoints.size(); ++n)
{
if (vRrlxs[gKeyPoint.vIndexs[m]].m_vKeyPoints[n] == gKeyPoint)
{
vRrlxs[gKeyPoint.vIndexs[m]].m_vKeyPoints[n].nFlag |= NMV_G;
}
}
}
//提取所有的关键点,带KEY_G标志
vPtsAll.push_back(gKeyPt);
//提取1维的关键点,带KEY_G标志
if (gKeyPt.vIndexs.size() == 1)
{
vPt1D.push_back(gKeyPt);
}
}
}
//第二类特殊的边界点可能会漏掉,需要特殊处理
//考虑到影像可能出现独立和分块的现象,所以需要分多块进行考虑
std::vector<geodetic> vPts1DBlock = vPt1D;
const size_t n1DNum = vPt1D.size();
for (int i = 0; i < n1DNum; ++i)
{
//去掉拷贝一维点中的所有标志位
//因为可能被分成几大块(后续每个一维点都必须被标记)
vPts1DBlock[i].nFlag &= ~NMV_G;
}
for (int i = 0; i < n1DNum; ++i)
{
if (vPts1DBlock[i].nFlag & NMV_G)
{
continue;
}
//第一个点一定是一维的点
geodetic& gBoundPt1st = vPts1DBlock[i];
int nPreRrlxIndex = -1;
bool bFindNextBound = false;
geodetic gCurBound(gBoundPt1st);
do
{
//内部会改变gCurBound和nPreRrlxIndex
bFindNextBound = FindNextBoudPt(vRrlxs, vKeyPts, gBoundPt1st, gCurBound, nPreRrlxIndex);
//找到当前的bound点在rrlx中的位置,并给予标记NMW_G
int nBoundPos = GetIndexInOneKeys(vRrlxs, vKeyPts, gCurBound);
if (nBoundPos != -1 && !(gCurBound.nFlag & NMV_G))
{
//标记所有的边界点,不仅仅是带KEY_G标志的关键点
geodetic& gKeyPoint = gCurBound;
for (int m = 0; m < gKeyPoint.vIndexs.size(); ++m)
{
for (int n = 0; n < vRrlxs[gKeyPoint.vIndexs[m]].m_vKeyPoints.size(); ++n)
{
if (vRrlxs[gKeyPoint.vIndexs[m]].m_vKeyPoints[n] == gKeyPoint)
{
vRrlxs[gKeyPoint.vIndexs[m]].m_vKeyPoints[n].nFlag |= NMV_G;
}
}
}
}
//减少循环次数
int nBlockPos = CStdTpl::VectorFind(vPts1DBlock, gCurBound);
if (nBlockPos != -1 && !(vPts1DBlock[nBlockPos].nFlag & NMV_G))
{
vPts1DBlock[nBlockPos].nFlag |= NMV_G;
}
} while (bFindNextBound);
}
return 0;
}
其中CRrlxContent是多边形的类,包含每个顶点坐标,项目原因我就不贴出来了。
下面继续核心代码:
bool FindNextBoudPt(std::vector<CRrlxContent>& vRrlxs, const std::vector<OneKey>& vKeyPts,
const geodetic& gPtBound1st, geodetic& gPtBoundCur, int& nRrlxIndexPre)
{
//根据当前的边界点,以及其所属的ploygon以及其相邻的ploygon寻找下一个边界点
std::vector<int> vIndexs = gPtBoundCur.vIndexs;
const size_t nDim = vIndexs.size();
if (1 == nDim)
{
//如果是一维点,则可以直接找到下一个点
geodetic gForward, gBackward;
FindForwardBackKeyPts(vRrlxs[vIndexs[0]], gPtBoundCur, gForward, gBackward);
gPtBoundCur = gBackward;
nRrlxIndexPre = vIndexs[0];
}
else if (2 == nDim)
{
//如果是二维点,则需要判断下一个边界点
int nPos = CStdTpl::VectorFind(vIndexs, nRrlxIndexPre);
if (-1 == nPos)
{
return false;
}
vIndexs.erase(vIndexs.begin() + nPos);
geodetic gForward, gBackward;
FindForwardBackKeyPts(vRrlxs[vIndexs[0]], gPtBoundCur, gForward, gBackward);
gPtBoundCur = gBackward;
nRrlxIndexPre = vIndexs[0];
}
else
{
//如果是三维以及以上的点
int nPos = CStdTpl::VectorFind(vIndexs, nRrlxIndexPre);
if (-1 == nPos)
{
return false;
}
vIndexs.erase(vIndexs.begin() + nPos);
geodetic gForward, gBackward;
do
{
FindForwardBackKeyPts(vRrlxs[nRrlxIndexPre], gPtBoundCur, gForward, gBackward);
std::vector<int> vCurIndexs = vIndexs;
std::vector<int> vBackIndexs = gBackward.vIndexs;
std::vector<int> vBCInter(MIN(vCurIndexs.size(), vBackIndexs.size()), -1);
//取交集的时候必须要先排序
std::sort(vCurIndexs.begin(), vCurIndexs.end());
std::sort(vBackIndexs.begin(), vBackIndexs.end());
std::set_intersection(vCurIndexs.begin(), vCurIndexs.end(),
vBackIndexs.begin(), vBackIndexs.end(), vBCInter.begin());
//排除错误情形
for (int k = 0; k < vBCInter.size(); ++k)
{
if (vBCInter[k] == -1)
{
vBCInter.erase(vBCInter.begin() + k);
--k;
}
}
if (vBCInter.size() != 1)
{
return false;
}
//vIndex中需要去掉这个值
nPos = CStdTpl::VectorFind(vIndexs, vBCInter[0]);
if (-1 == nPos && vIndexs.size() > 1)
{
return false;
}
else
{
vIndexs.erase(vIndexs.begin() + nPos);
}
if (1 == vIndexs.size())
{
break;
}
nRrlxIndexPre = vBCInter[0];
} while (vIndexs.size() > 1);
FindForwardBackKeyPts(vRrlxs[vIndexs[0]], gPtBoundCur, gForward, gBackward);
gPtBoundCur = gBackward;
nRrlxIndexPre = vIndexs[0];
}
//如果循环一圈了,则返回false
return gPtBoundCur != gPtBound1st;
}
以上就可以把所有的边界点提取出来了,即便没有条件3也没问题,如果没有条件4的话,则需要先找到一个边界点,以及相连的边界点即可,具体就不展开了。