1. 程式人生 > >ArcGIS Engine實現根據傳入的多段線獲取其路由分析網路途經資源

ArcGIS Engine實現根據傳入的多段線獲取其路由分析網路途經資源

程式碼:

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;
        }
    }
}