多边形组成的网格的边界提取方法

最近一个项目,需要提取多边形组成的网格的边界点(关键点),如图所示:

希望得到如下图所示的多边形:

已知条件:

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的话,则需要先找到一个边界点,以及相连的边界点即可,具体就不展开了。

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值