版权声明:本文为博主原创文章,不需博主允许即可随意转载。 https://blog.csdn.net/a_dev/article/details/84581744
已知:
1.某一片区域内有多条管线段
2.每条管线段包含两个端点
3.两条管线段可通过一个端点进行连接
4.管线段之间可能存在交叉
数据如下:
我们要做的事情是,画一个范围,将范围内所有连续的管线段识别出来。
先制作一份测试数据:
对上图,我们预期得到的结果集合应该是这样的(不区分方向,即123与321视为同一条连续管线段):
{
(123),
(1245),
(1246),
(345),
(346),
(56),
(78),
(9)
}
算法中需要使用以下几个类来存储相应的变量:
/// <summary>
/// 要素图形信息
/// </summary>
public class FeatureShape
{
/// <summary>
/// 要素GLOBALID(唯一标识)
/// </summary>
public int ObjectID { get; set; }
/// <summary>
/// 要素几何图形
/// </summary>
public IGeometry Geometry { get; set; }
}
/// <summary>
/// 连接的线段要素对象
/// </summary>
public class ContinuousLine
{
private List<FeatureShape> lineFeatures = null;
public List<FeatureShape> Features
{
get
{
if (null == lineFeatures)
lineFeatures = new List<FeatureShape>();
return lineFeatures;
}
set => lineFeatures = value;
}
/// <summary>
/// 重写ToString,将ObjectID输出(方便调试查看)
/// </summary>
/// <returns></returns>
public override string ToString()
{
string s = "";
if (null != lineFeatures && 0 < lineFeatures.Count)
{
lineFeatures.ForEach(e => s += $" {e.ObjectID}");
}
return s;
}
}
/// <summary>
/// 连续线段信息
/// </summary>
public class LineSegmentInfo
{
/// <summary>
/// 是否为端点段(即至少有一端末与其他线段相连)
/// </summary>
public bool IsEnd { get; set; }
/// <summary>
/// 线要素对象信息
/// </summary>
public FeatureShape LineFeature { get; set; }
}
根据以上示例图形生成测试数据:
private FeatureShape GenerateTestFeature(int objectId, double x0, double y0, double x1, double y1)
{
var aLineFeature = new FeatureShape()
{
ObjectID = objectId,
Geometry = CreatePolyline(new PointClass() { X = x0, Y = y0 }, new PointClass() { X = x1, Y = y1 })
};
return aLineFeature;
}
private List<LineSegmentInfo> GenerateTestData()
{
var lstResult = new List<LineSegmentInfo>
{
new LineSegmentInfo() { IsEnd = false, LineFeature = GenerateTestFeature(1, 1, 6, 2, 4) },
new LineSegmentInfo() { IsEnd = false, LineFeature = GenerateTestFeature(2, 2, 4, 4, 4) },
new LineSegmentInfo() { IsEnd = false, LineFeature = GenerateTestFeature(3, 4, 4, 6, 6) },
new LineSegmentInfo() { IsEnd = false, LineFeature = GenerateTestFeature(4, 4, 4, 5, 2) },
new LineSegmentInfo() { IsEnd = false, LineFeature = GenerateTestFeature(5, 5, 2, 5, 0) },
new LineSegmentInfo() { IsEnd = false, LineFeature = GenerateTestFeature(6, 5, 2, 7, 3) },
new LineSegmentInfo() { IsEnd = false, LineFeature = GenerateTestFeature(7, 1, 2, 3, 2) },
new LineSegmentInfo() { IsEnd = false, LineFeature = GenerateTestFeature(8, 3, 2, 4, 0) },
new LineSegmentInfo() { IsEnd = false, LineFeature = GenerateTestFeature(9, 2, 6, 4, 6) }
};
return lstResult;
}
我们设计的算法步骤如下:
1.根据区域,使用空间包含关系,查询所有管线段(示例中使用代码生成所有管线段)
2.识别出所有截止管线段(即至少有一个端点未连接其他管线段)
3.从每个截止管线段起,搜索连续的管线段(不区分方向)
相应的代码,第一步,我们已经得到了管线段集合,第二步,就是识别出其中的截止管线段,代码如下:
private void IdentifyEndLine(ref List<LineSegmentInfo> lstLineSegmentInfo)
{
for (int i = 0; i < lstLineSegmentInfo.Count; i++)
{
var nCount = -1;
var pPointCollection = lstLineSegmentInfo[i].LineFeature.Geometry as IPointCollection;
if (null != pPointCollection && 0 < pPointCollection.PointCount)
{
for (int j = 0; j < pPointCollection.PointCount; j++)
{
var nTempCount = GetTouchFeatureCount(pPointCollection.Point[j] as IRelationalOperator, lstLineSegmentInfo[i].LineFeature.ObjectID, lstLineSegmentInfo);
if (nCount < 0 || nTempCount < nCount)
nCount = nTempCount;
}
}
if (0 == nCount)
lstLineSegmentInfo[i].IsEnd = true;
}
}
private int GetTouchFeatureCount(IRelationalOperator pRelationalOperator, int nOwnOid, List<LineSegmentInfo> lstLineSegmentInfo)
{
var nCount = 0;
if (null != pRelationalOperator && null != lstLineSegmentInfo && 0 < lstLineSegmentInfo.Count)
{
foreach (var aLineSegment in lstLineSegmentInfo)
{
if (aLineSegment.LineFeature.ObjectID == nOwnOid)
continue;
if (pRelationalOperator.Touches(aLineSegment.LineFeature.Geometry))
nCount++;
}
}
return nCount;
}
第三步,我们要循环各个截止管线段,搜索连续管线段,结果存放在如下变量中:
private List<ContinuousLine> m_lstContinuousLine;
循环吧:
foreach (var item in lstSegment)
{
if (item.IsEnd)
{
GetContinuousLine(lstSegment, item.LineFeature, null, null, ref m_lstContinuousLine);
}
}
算法其实是一个递归:
private void GetContinuousLine(List<LineSegmentInfo> lstSegment, FeatureShape currentLineFeature,
ContinuousLine currentContinuousLine, List<FeatureShape> lstIgnored, ref List<ContinuousLine> lstResult)
{
if (null == lstSegment || 0 == lstSegment.Count)
return;
if (null == lstResult)
lstResult = new List<ContinuousLine>();
foreach (var aSegment in lstSegment)
{
if (null != lstIgnored && 0 < lstIgnored.Count)
{
if (lstIgnored.Contains(aSegment.LineFeature))
{
continue;
}
}
if (null != currentContinuousLine && 0 < currentContinuousLine.Features.Count)
{
if (currentContinuousLine.Features.Contains(aSegment.LineFeature))
{
continue;
}
}
if (null == currentLineFeature)
{
if (!aSegment.IsEnd)
{
continue;
}
else
{
GetContinuousLine(lstSegment, aSegment.LineFeature, null, null, ref lstResult);
break;
}
}
if (aSegment.LineFeature.ObjectID != currentLineFeature.ObjectID)
{
continue;
}
if (aSegment.IsEnd)
{
if (null == currentContinuousLine || 0 == currentContinuousLine.Features.Count)
{
var lstConnected = GetConnectedLineFeature(aSegment.LineFeature, lstSegment, lstIgnored);
if (0 < lstConnected.Count)
{
var aNewCurrentContinuous = new ContinuousLine();
if (null != currentContinuousLine && 0 < currentContinuousLine.Features.Count)
{
currentContinuousLine.Features.ForEach(e => aNewCurrentContinuous.Features.Add(e));
}
aNewCurrentContinuous.Features.Add(aSegment.LineFeature);
foreach (var aConnected in lstConnected)
{
if (aNewCurrentContinuous.Features.Contains(aConnected))
continue;
var lstNewIgnored = new List<FeatureShape>();
foreach (var item in lstConnected)
{
if (item.ObjectID == aConnected.ObjectID)
continue;
if (aNewCurrentContinuous.Features.Contains(item))
continue;
lstNewIgnored.Add(item);
}
GetContinuousLine(lstSegment, aConnected, aNewCurrentContinuous, lstNewIgnored, ref lstResult);
}
}
else
{
var aNewContinuousLine = new ContinuousLine() { Features = new List<FeatureShape>() { aSegment.LineFeature } };
if (!ContainsContinuousLine(aNewContinuousLine, ref lstResult))
lstResult.Add(aNewContinuousLine);
}
}
else
{
var aNewContinuousLine = new ContinuousLine();
currentContinuousLine.Features.ForEach(e => aNewContinuousLine.Features.Add(e));
aNewContinuousLine.Features.Add(aSegment.LineFeature);
if (!ContainsContinuousLine(aNewContinuousLine, ref lstResult))
lstResult.Add(aNewContinuousLine);
break;
}
}
else
{
var lstConnected = GetConnectedLineFeature(aSegment.LineFeature, lstSegment, lstIgnored);
var aNewCurrentContinuous = new ContinuousLine();
if (null != currentContinuousLine && 0 < currentContinuousLine.Features.Count)
{
currentContinuousLine.Features.ForEach(e => aNewCurrentContinuous.Features.Add(e));
}
aNewCurrentContinuous.Features.Add(aSegment.LineFeature);
foreach (var aConnected in lstConnected)
{
if (currentContinuousLine.Features.Contains(aConnected))
continue;
var lstNewIgnored = new List<FeatureShape>();
foreach (var item in lstConnected)
{
if (item.ObjectID == aConnected.ObjectID)
continue;
if (currentContinuousLine.Features.Contains(item))
continue;
lstNewIgnored.Add(item);
}
GetContinuousLine(lstSegment, aConnected, aNewCurrentContinuous, lstNewIgnored, ref lstResult);
}
}
}
}
private bool ContainsContinuousLine(ContinuousLine continuousLine, ref List<ContinuousLine> lstLine)
{
var bContains = true;
if (null == lstLine || 0 >= lstLine.Count)
{
if (null != continuousLine)
{
bContains = false;
}
}
else
{
if (null == continuousLine || 0 >= continuousLine.Features.Count)
{
bContains = false;
}
else
{
bContains = false;
foreach (var aLine in lstLine)
{
if (aLine.Features.Count != continuousLine.Features.Count)
{
continue;
}
if (continuousLine.Features.All(aLine.Features.Contains) && aLine.Features.All(continuousLine.Features.Contains))
{
bContains = true;
break;
}
}
}
}
return bContains;
}
private List<FeatureShape> GetConnectedLineFeature(FeatureShape lineFeature, List<LineSegmentInfo> lstLineSegment, List<FeatureShape> lstIgnored = null)
{
var lstResult = new List<FeatureShape>();
if (null != lineFeature && null != lineFeature.Geometry && null != lstLineSegment && 0 < lstLineSegment.Count)
{
var pRelOperator = lineFeature.Geometry as IRelationalOperator;
foreach (var aLineSegment in lstLineSegment)
{
if (aLineSegment.LineFeature.ObjectID == lineFeature.ObjectID)
continue;
var bIgnore = false;
if (null != lstIgnored && 0 < lstIgnored.Count)
{
foreach (var aLineFeature in lstIgnored)
{
if (aLineSegment.LineFeature.ObjectID == aLineFeature.ObjectID)
{
bIgnore = true;
break;
}
}
}
if (!bIgnore)
{
if (pRelOperator.Touches(aLineSegment.LineFeature.Geometry))
lstResult.Add(aLineSegment.LineFeature);
}
}
}
return lstResult;
}
代码跑起来,监视变量,发现结果正是我们想要的: