ArcGIS Engine實現根據傳入的多段線獲取其路由分析網路途經資源
阿新 • • 發佈:2018-12-04
程式碼:
using ESRI.ArcGIS.ADF; using ESRI.ArcGIS.Geodatabase; using ESRI.ArcGIS.Geometry; using Newtonsoft.Json; using SyGisWebService.DataEnum; using SyGisWebService.GlobalConfig; using System; using System.Collections.Generic; namespace SyGisWebService.Common { public class RouteResourceAnalysis { public class LineSegment { [JsonProperty("Type")] public string LineType { get; set; } [JsonConverter(typeof(BoolConvert))] [JsonProperty("Existence")] public bool Existence { get; set; } [JsonProperty("Shape")] public GeoJSON.Net.Geometry.MultiLineString Shape { get; set; } private double _shapeLength = 0; [JsonProperty("Length")] public double Length { get { if (null != Shape) { _shapeLength = GeometryFactory.CalculateLength(Shape); } return _shapeLength; } set => _shapeLength = value; } public LineSegment() { } public LineSegment(LineSegment aLineSegment) { LineType = aLineSegment.LineType; Existence = aLineSegment.Existence; Shape = aLineSegment.Shape; } } /// <summary> /// 多段線資訊(中心點,是否已過濾) /// </summary> public class MultiLineStringInfo { public GeoJSON.Net.Geometry.MultiLineString Shape { get; set; } private GeoJSON.Net.Geometry.Position _centerPosition = null; public GeoJSON.Net.Geometry.Position CenterPosition { get { if (null == _centerPosition && null != Shape) _centerPosition = CalculateCenterPoint(Shape); return _centerPosition; } set => _centerPosition = value; } } /// <summary> /// 幾何物件資訊(包括中心點偽座標) /// </summary> public class GeometryInfo { public IGeometry Geometry { get; set; } private GeoJSON.Net.Geometry.Position _centerPosition = null; public GeoJSON.Net.Geometry.Position CenterPosition { get { if (null == _centerPosition && null != Geometry) _centerPosition = CalculateCenterPoint(Geometry); return _centerPosition; } set => _centerPosition = value; } public bool Used { get; set; } } public const string UnknownVirtualLineFlag = "UNKLK"; public const string PipeLineFlag = "PIPLK"; public const string SuspensionWireFlag = "SPWLK"; public const string RoadLineFlag = "ROALK"; public const string LedupLineFlag = "LDPLK"; public static List<string> RealLineType = new List<string> { PipeLineFlag, SuspensionWireFlag, LedupLineFlag }; public static List<string> VirtualLineType = new List<string> { RoadLineFlag, "VIRLK", "PPDLK" }; private const double MiniBufferRadius = 0.01; private static Dictionary<string, List<GeometryInfo>> GetLine(IFeatureWorkspace pFeatureWorkspace, IQueryFilter pQueryFilter, List<string> lstFeatureClass) { var dictLine = new Dictionary<string, List<GeometryInfo>>(); try { if (null != lstFeatureClass) { using (var aComReleaser = new ComReleaser()) { foreach (var sTableName in lstFeatureClass) { dictLine.Add(sTableName, new List<GeometryInfo>()); var pFeatureCursor = pFeatureWorkspace.OpenFeatureClass(sTableName).Search(pQueryFilter, false); aComReleaser.ManageLifetime(pFeatureCursor); for (var pFeature = pFeatureCursor.NextFeature(); pFeature != null; pFeature = pFeatureCursor.NextFeature()) { dictLine[sTableName].Add(new GeometryInfo() { Geometry = pFeature.ShapeCopy, Used = false }); } } } } } catch (Exception e) { ExceptionHelper.LogActionException(e); } return dictLine; } private static List<MultiLineStringInfo> SplitMultiLineString(GeoJSON.Net.Geometry.MultiLineString aMultiLineString) { var lstMultiLineStringInfo = new List<MultiLineStringInfo>(); try { Log.Loging.Info($"---> Input MultiLineString : {JsonConvert.SerializeObject(aMultiLineString)}"); if (null == aMultiLineString) return null; var nCoordinatesCount = aMultiLineString.Coordinates[0].Coordinates.Count; if (1 >= nCoordinatesCount) return null; for (int i = 0; i < nCoordinatesCount - 1; i++) { var lstPosition = new List<GeoJSON.Net.Geometry.Position> { aMultiLineString.Coordinates[0].Coordinates[i], aMultiLineString.Coordinates[0].Coordinates[i + 1] }; var aNewMultiLineString = new GeoJSON.Net.Geometry.MultiLineString( new List<GeoJSON.Net.Geometry.LineString>() { new GeoJSON.Net.Geometry.LineString(lstPosition) }); lstMultiLineStringInfo.Add(new MultiLineStringInfo() { Shape = aNewMultiLineString }); } } catch (Exception e) { ExceptionHelper.LogActionException(e); } return lstMultiLineStringInfo; } /// <summary> /// 偽中心點(Z為Position個數) /// </summary> /// <param name="multiLineString"></param> /// <returns></returns> private static GeoJSON.Net.Geometry.Position CalculateCenterPoint(GeoJSON.Net.Geometry.MultiLineString multiLineString) { int nCount = 0; double dTotalX = 0; double dTotalY = 0; foreach (var aLineString in multiLineString.Coordinates) { foreach (var aPosition in aLineString.Coordinates) { dTotalX += aPosition.X; dTotalY += aPosition.Y; nCount++; } } return new GeoJSON.Net.Geometry.Position(dTotalX, dTotalY, nCount); } /// <summary> /// 偽中心點(Z為Position個數) /// </summary> /// <param name="pGeometry"></param> /// <returns></returns> private static GeoJSON.Net.Geometry.Position CalculateCenterPoint(IGeometry pGeometry) { int nCount = 0; double dTotalX = 0; double dTotalY = 0; if (pGeometry is IPointCollection pPointCollection) { for (int i = 0; i < pPointCollection.PointCount; i++) { dTotalX += pPointCollection.Point[i].X; dTotalY += pPointCollection.Point[i].Y; nCount++; } } return new GeoJSON.Net.Geometry.Position(dTotalX, dTotalY, nCount); } /// <summary> /// 是否位置相同 /// </summary> /// <param name="positionA"></param> /// <param name="positionB"></param> /// <returns></returns> private static bool SamePosition(GeoJSON.Net.Geometry.Position positionA, GeoJSON.Net.Geometry.Position positionB) { if (null == positionA && null == positionB) return true; if (null == positionA || null == positionB) return false; var dDistanceTolerance = positionA.Z * MiniBufferRadius; return positionA.Z == positionB.Z && Math.Abs(positionA.X - positionB.X) < dDistanceTolerance && Math.Abs(positionA.Y - positionB.Y) < dDistanceTolerance; } /// <summary> /// 新增線段 /// </summary> /// <param name="multiLineStringInfo"></param> /// <param name="bExitenceFlag"></param> /// <param name="bFound"></param> /// <param name="dictGeometryInfoList"></param> /// <param name="lstTargetLineSegment"></param> private static void AddLineSegment(MultiLineStringInfo multiLineStringInfo, bool bExitenceFlag, ref bool bFound, ref Dictionary<string, List<GeometryInfo>> dictGeometryInfoList, ref List<LineSegment> lstTargetLineSegment) { try { if (bFound) return; foreach(var aGeometryInfoList in dictGeometryInfoList) { for (int i = 0; i < dictGeometryInfoList[aGeometryInfoList.Key].Count; i++) { if (dictGeometryInfoList[aGeometryInfoList.Key][i].Used) continue; if (SamePosition(multiLineStringInfo.CenterPosition, dictGeometryInfoList[aGeometryInfoList.Key][i].CenterPosition)) { lstTargetLineSegment.Add(new LineSegment() { LineType = aGeometryInfoList.Key, Existence = bExitenceFlag, Shape = multiLineStringInfo.Shape }); dictGeometryInfoList[aGeometryInfoList.Key][i].Used = true; bFound = true; break; } } if (bFound) break; } } catch (Exception e) { ExceptionHelper.LogActionException(e); } } /// <summary> /// 自動敷設虛擬資源線段 /// </summary> /// <param name="eLayerMethod"></param> /// <param name="lstLineSegment"></param> private static void AutoLayingVirtualLineSegment(LayingMethodEnum eLayerMethod, ref List<LineSegment> lstLineSegment) { if (null == lstLineSegment || 0 >= lstLineSegment.Count) return; switch (eLayerMethod) { case LayingMethodEnum.Unknown: break; case LayingMethodEnum.Overhead: ChangeVirtualLineType(SuspensionWireFlag, ref lstLineSegment); break; case LayingMethodEnum.Mixed: ChangeVirtualLineType(null, ref lstLineSegment); break; case LayingMethodEnum.Buried: ChangeVirtualLineType(PipeLineFlag, ref lstLineSegment); break; default: break; } } /// <summary> /// 修改虛擬資源線段型別 /// </summary> /// <param name="sResourceLineFlag"></param> /// <param name="lstLineSegment"></param> private static void ChangeVirtualLineType(string sResourceLineFlag, ref List<LineSegment> lstLineSegment) { if (null == lstLineSegment || 0 >= lstLineSegment.Count) return; for (int i = 0; i < lstLineSegment.Count; i++) { if (lstLineSegment[i].Existence) continue; lstLineSegment[i].LineType = string.IsNullOrEmpty(sResourceLineFlag) ? (0 == string.Compare(lstLineSegment[i].LineType, RoadLineFlag, StringComparison.OrdinalIgnoreCase) ? PipeLineFlag : SuspensionWireFlag) : sResourceLineFlag; } } /// <summary> /// 資源線段平滑處理 /// </summary> /// <param name="lstLineSegment"></param> private static void SmoothLineSegment(ref List<LineSegment> lstLineSegment) { if (null == lstLineSegment || 3 >= lstLineSegment.Count) return; for (int i = 1; i < lstLineSegment.Count - 1; i++) { if (lstLineSegment[i].Existence) continue; var sPreviousLineType = lstLineSegment[i - 1].LineType; var sCurrentLineType = lstLineSegment[i].LineType; var sNextLineType = lstLineSegment[i + 1].LineType; //首尾型別相同,如果中間型別不同,則中間自動變為與首尾相同型別 if (0 == string.Compare(sPreviousLineType, sNextLineType, StringComparison.OrdinalIgnoreCase)) { if (0 != string.Compare(sCurrentLineType, sPreviousLineType, StringComparison.OrdinalIgnoreCase)) { lstLineSegment[i].LineType = sPreviousLineType; } } //首尾型別不同,一端為引上,另一端為管道或吊線,中間自動變為管道或吊線,即與非引上的那一端相同 else if (0 == string.Compare(sPreviousLineType, LedupLineFlag, StringComparison.OrdinalIgnoreCase) || (0 == string.Compare(sNextLineType, LedupLineFlag, StringComparison.OrdinalIgnoreCase))) { if (0 == string.Compare(sPreviousLineType, LedupLineFlag, StringComparison.OrdinalIgnoreCase)) { lstLineSegment[i].LineType = sNextLineType; } else { lstLineSegment[i].LineType = sPreviousLineType; } } //首尾型別不同,一端為引上,另一端為吊線,中間自動變成引上 else { lstLineSegment[i].LineType = LedupLineFlag; } } } /// <summary> /// 合併同類的多段短的資源線 /// </summary> /// <param name="bUnion"></param> /// <param name="lstLineSegment"></param> private static void UnionLineSegment(bool bUnion, ref List<LineSegment> lstLineSegment) { if (!bUnion || null == lstLineSegment) return; var nSegmentCount = lstLineSegment.Count; if (0 >= nSegmentCount) return; var lstNewLineSegment = new List<LineSegment>(); try { LineSegment aUnionLineSegment = null; var lstPosition = new List<GeoJSON.Net.Geometry.Position>(); for (var i = 0; i < nSegmentCount; i++) { var aLineSegment = lstLineSegment[i]; if (0 == i) { aUnionLineSegment = new LineSegment(aLineSegment); AddPointToCollection(aLineSegment.Shape, ref lstPosition); } else { var bSameExistence = aLineSegment.Existence == aUnionLineSegment.Existence; var bSameType = (0 == string.Compare(aLineSegment.LineType, aUnionLineSegment.LineType, StringComparison.OrdinalIgnoreCase)); if (!(bSameExistence && bSameType)) { aUnionLineSegment.Shape = CreateMultiLineString(lstPosition); lstNewLineSegment.Add(aUnionLineSegment); lstPosition.Clear(); aUnionLineSegment = new LineSegment(aLineSegment); } AddPointToCollection(aLineSegment.Shape, ref lstPosition); } if (i + 1 == nSegmentCount) { aUnionLineSegment.Shape = CreateMultiLineString(lstPosition); lstNewLineSegment.Add(aUnionLineSegment); lstPosition.Clear(); } } lstLineSegment = new List<LineSegment>(lstNewLineSegment); } catch (Exception e) { ExceptionHelper.LogActionException(e); } } /// <summary> /// 新增點到集合中 /// </summary> /// <param name="aMultiLineString"></param> /// <param name="lstPosition"></param> private static void AddPointToCollection(GeoJSON.Net.Geometry.MultiLineString aMultiLineString, ref List<GeoJSON.Net.Geometry.Position> lstPosition) { if (null == aMultiLineString || null == lstPosition) return; lstPosition.AddRange(aMultiLineString.Coordinates[0].Coordinates); } /// <summary> /// 根據Position集合建立多段線 /// </summary> /// <param name="lstPosition"></param> /// <returns></returns> private static GeoJSON.Net.Geometry.MultiLineString CreateMultiLineString(List<GeoJSON.Net.Geometry.Position> lstPosition) { var aLineString = new GeoJSON.Net.Geometry.LineString(lstPosition); return new GeoJSON.Net.Geometry.MultiLineString(new List<GeoJSON.Net.Geometry.LineString>() { aLineString }); } /// <summary> /// 查詢途經資源線(給外部呼叫的方法) /// </summary> /// <param name="sNetworkPath">本地分析網路資料gdb檔案路徑</param> /// <param name="aRouteLine">路由線物件</param> /// <param name="bUnion">是否合併同類連續的多段線</param> /// <param name="eLayingMethod">敷設方式</param> /// <returns></returns> public static List<LineSegment> GetResourceLine(string sNetworkPath, GeoJSON.Net.Geometry.MultiLineString aRouteLine, bool bUnion = false, LayingMethodEnum eLayingMethod = LayingMethodEnum.Mixed) { var lstLineSegment = new List<LineSegment>(); try { using (SpatialConnectionLibrary.SpatialConnection aConnection = DatabaseConnection.GetSpatialConnection(sNetworkPath)) { var pFeatureWorkspace = aConnection.Workspace as IFeatureWorkspace; var pRouteLine = GeometryFactory.CreateEsriGeometryFromGeoJson(aRouteLine); var pRouteBuffer = GeometryFactory.BufferEx(pRouteLine, MiniBufferRadius, esriBufferConstructionSideEnum.esriBufferFull, esriBufferConstructionEndEnum.esriBufferRound); var pSpatialFilter = FilterFactory.CreateSpatialFilter(pRouteBuffer, esriSpatialRelEnum.esriSpatialRelContains); var dictRealLineInfo = GetLine(pFeatureWorkspace, pSpatialFilter, RealLineType); var dictVirtualLineInfo = GetLine(pFeatureWorkspace, pSpatialFilter, VirtualLineType); var lstRouteMultiLineStringInfo = SplitMultiLineString(aRouteLine); foreach (var aRouteMultiLineStringInfo in lstRouteMultiLineStringInfo) { var bFound = false; AddLineSegment(aRouteMultiLineStringInfo, true, ref bFound, ref dictRealLineInfo, ref lstLineSegment); AddLineSegment(aRouteMultiLineStringInfo, false, ref bFound, ref dictVirtualLineInfo, ref lstLineSegment); if (!bFound) { lstLineSegment.Add(new LineSegment() { LineType = UnknownVirtualLineFlag, Existence = false, Shape = aRouteMultiLineStringInfo.Shape }); } Log.Loging.Info($"---> Add Line Segment : {JsonConvert.SerializeObject(lstLineSegment[lstLineSegment.Count - 1])}"); } } Log.Loging.Info($"---> Line Segment (Original) : {JsonConvert.SerializeObject(lstLineSegment)}"); AutoLayingVirtualLineSegment(eLayingMethod, ref lstLineSegment); Log.Loging.Info($"---> Line Segment (Auto Laying by Method {eLayingMethod}) : {JsonConvert.SerializeObject(lstLineSegment)}"); SmoothLineSegment(ref lstLineSegment); Log.Loging.Info($"---> Line Segment (Smooth) : {JsonConvert.SerializeObject(lstLineSegment)}"); UnionLineSegment(bUnion, ref lstLineSegment); Log.Loging.Info($"---> Line Segment (Union) : {JsonConvert.SerializeObject(lstLineSegment)}"); } catch (Exception e) { ExceptionHelper.LogActionException(e); } return lstLineSegment; } } }