C# 拓扑结构 多边形算法

 

 原作者  https://blog.csdn.net/gis_master/article/details/4941457?utm_source=copy

 原作者参考的论文  GIS中自动拓扑算法研究及组件化实现 作者:王进宝

 链接:https://pan.baidu.com/s/1nWWAxudGGFRvDTIgWsmnag 
 提取码:4dx9

using System.Collections.Generic;

namespace Polygon
{
    /// <summary>
    /// 点模型
    /// </summary>
    public class Point
    {
        public int Id;
        public float X;
        public float Y;
    }

    /// <summary>
    /// 弧段模型
    /// </summary>
    public class Arc
    {
        public string Id;
        public List<Point> Points = new List<Point>();
        public int TimesOfSearch; //遍历次数
        public int Direction; //弧段的方向
    }

    /// <summary>
    /// 生成多边形时弧段及当时的方向
    /// </summary>
    public class ArcDirection
    {
        public Arc Arc;
        public int Direction;
    }

    /// <summary>
    /// 多边形模型
    /// </summary>
    public class Polygon
    {
        public char Id;
        public List<Point> Points = new List<Point>();
        public List<ArcDirection> ArcDirections = new List<ArcDirection>();
        public double Area;
    }

    /// <summary>
    /// 点-弧段模型
    /// </summary>
    public class PointArcs
    {
        public int PointId;
        public List<Arc> Arcs = new List<Arc>();
    }

    /// <summary>
    /// 弧段夹角
    /// </summary>
    public class ArcAngle
    {
        public double Angle;
        public Arc Arc = new Arc();
        public int Direction;
    }

    /// <summary>
    /// 多边形-弧段关系表
    /// </summary>
    public class PolygonAcrs
    {
        public char PolygonId;
        public List<string> ArcIds = new List<string>();
        public double Area;
    }

    /// <summary>
    /// 弧段-点关系表
    /// </summary>
    public class ArcPoints
    {
        public string ArcId;
        public List<int> PointIds = new List<int>();
    }

    /// <summary>
    /// 弧段-多边形关系表
    /// </summary>
    public class ArcPolygon
    {
        public string ArcId;
        public int StartPointId;
        public int EndPointId;
        public char LeftPolygonId;
        public char RightPolygonId;
    }

    /// <summary>
    /// 岛结构
    /// </summary>
    public class IsLand
    {
        public char OutPolygonId;
        public char InPolygonId;
        public string AfterArcId;
        public string BeforeArcId;
    }

    public class Topology
    {
        public IList<Point> Points = new List<Point>();
        public IList<Arc> Arcs = new List<Arc>();
        public IList<ArcPoints> ArcPointses = new List<ArcPoints>();
        public IList<PointArcs> PointArcses = new List<PointArcs>();
        public IList<PolygonAcrs> PolygonAcrses = new List<PolygonAcrs>();
        public IList<ArcPolygon> ArcPolygons = new List<ArcPolygon>();

        public Topology(IList<Point> points)
        {
            this.Points = points;
        }

        public void BuildArcPoints(IList<Arc> arcs)
        {
            foreach (var arc in arcs)
            {
                var arcPoints = new ArcPoints { ArcId = arc.Id };
                foreach (var point in arc.Points)
                {
                    arcPoints.PointIds.Add(point.Id);
                }
                ArcPointses.Add(arcPoints);
            }
        }

        public void BuildPolygonsArcs(IList<Polygon> polygons)
        {
            foreach (var polygon in polygons)
            {
                var polygonAcrs = new PolygonAcrs { PolygonId = polygon.Id, Area = polygon.Area};
                foreach (var arcDirection in polygon.ArcDirections)
                {
                    polygonAcrs.ArcIds.Add(arcDirection.Arc.Id);
                }
                单弧段多边形
                //if (polygon.ArcDirections.Count == 1)
                //{
                //    if(PointInIsland(polygons,polygon))
                //}
                PolygonAcrses.Add(polygonAcrs);
            }
        }

        public bool PointInIsland(IList<Polygon> polygons, Polygon polygon)
        {
            var point = polygon.ArcDirections[0].Arc.Points[0];
            return (from p in polygons where polygon.Id != p.Id select p.Points.Contains(point)).FirstOrDefault();
        }

        public void BuildArcsPolygon(IList<Arc> arcs, IList<Polygon> polygons)
        {
        }

        public void PrintTopology()
        {
            Console.WriteLine("点结构表");
            Console.WriteLine("点ID:横坐标,纵坐标");
            foreach (var point in Points)
            {
                Console.WriteLine("{0}:{1},{2}", point.Id, point.X, point.Y);
            }
            Console.WriteLine();
            Console.WriteLine("段-点拓扑结构表");
            Console.WriteLine("段ID:点ID");
            foreach (var arcPointses in ArcPointses)
            {
                var pointIds = arcPointses.PointIds.Aggregate("", (current, pId) => current + (pId + ","));

                Console.WriteLine("{0}:{1}", arcPointses.ArcId, pointIds.TrimEnd(','));
            }

            Console.WriteLine();
            Console.WriteLine("多边形-段拓扑结构表");
            Console.WriteLine("多边形ID:面积;段ID");
            foreach (var polygonAcrses in PolygonAcrses)
            {
                var arcIds = polygonAcrses.ArcIds.Aggregate("", (current, aId) => current + (aId + ","));
                Console.WriteLine("{0}:{1};{2}", polygonAcrses.PolygonId, polygonAcrses.Area, arcIds.TrimEnd(','));
            }
        }
    }
}

 

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.InteropServices;

namespace Polygon
{
    public class CreatePolygons
    {
        public IList<Point> Points = new List<Point>();
        public IList<Arc> Arcs = new List<Arc>();
        public IList<PointArcs> PointArcses = new List<PointArcs>();
        public IList<Polygon> Polygons = new List<Polygon>();
        public int PolygonIndex;
        public ArcAngle MinAngle;
        public CreatePolygons(IList<Point> points, IList<Arc> arcs)
        {
            Points = points;
            Arcs = arcs;
        }

        public void MainCreatePolygons()
        {
            //建立点-弧段关系
            BuildPointArcs();

            //多边形自动构建算法
            foreach (var arc in Arcs.Where(arc => arc.TimesOfSearch==0))
            {
                BuildPolygons(arc);
            }
        }

        /// <summary>
        /// 建立点-弧段关系
        /// </summary>
        public void BuildPointArcs()
        {
            foreach (var point in Points)
            {
                var pointArcs = new PointArcs {PointId = point.Id};
                foreach (var arc in Arcs)
                {

                    var startPoint = arc.Points[0];
                    var endPoint = arc.Points[arc.Points.Count - 1];

                    if (point.Id == startPoint.Id || point.Id == endPoint.Id)
                    {
                        pointArcs.Arcs.Add(arc);
                    }
                }
                if (pointArcs.Arcs.Count != 0)
                    PointArcses.Add(pointArcs);
            }
        }
        
        /// <summary>
        /// 从指定弧段开始构建多边形
        /// </summary>
        /// <param name="arc">弧段</param>
        public void BuildPolygons(Arc arc)
        {
            var polygons = new List<Polygon>();
            //正向(逆时针)构建第一个多边形
            var polygon = BuildPolygon(arc);
            polygon.Points = GetPolygonPoints(polygon);
            polygon.Area = Math.Abs(GetArea(polygon.Points));
            polygons.Add(polygon);

            //反向(顺时针)构建其它多边形
            for (var i = 0;i<Arcs.Count-1;i++)
            {
                //从正向已经构建任意弧度开始构建
                if (Arcs[i].TimesOfSearch == 1)
                {
                    //改变当前弧段的构建方向
                    Arcs[i].Direction *= -1;
                    polygon = BuildPolygon(Arcs[i]);
                    if (polygon == null) return;
                    polygon.Points = GetPolygonPoints(polygon);
                    polygon.Area = Math.Abs(GetArea(polygon.Points));
                    polygons.Add(polygon);
                    //每次都从第一个弧段开始搜索
                    i = 0;
                }
            }

            //删除面积最大的一个多边形
            RemoveMaxPolygon(polygons);

            //给多边形增加编号
            foreach (var polygon1 in polygons)
            {
                polygon1.Id = Convert.ToChar(65 + PolygonIndex);
                Polygons.Add(polygon1);
                PolygonIndex++;
            }
        }

        /// <summary>
        /// 删除最大面积的多边形
        /// </summary>
        /// <param name="polygons">多边形</param>
        public void RemoveMaxPolygon(List<Polygon> polygons)
        {
            double max=0.0f;
            var maxPolygon = new Polygon();
            foreach (var polygon in polygons.Where(polygon => max <= polygon.Area))
            {
                maxPolygon = polygon;
                max = polygon.Area;
            }
            polygons.Remove(maxPolygon);
        }

        /// <summary>
        /// 计算给定点集合的面积
        /// </summary>
        /// <param name="points">点集合</param>
        /// <returns>面积</returns>
        public double GetArea(List<Point> points)
        {
            double s = 0;
            for (int i = 0; i < points.Count - 1; i++)
                s += points[i].X*points[i + 1].Y - points[i + 1].X*points[i].Y;
            s += points[points.Count - 1].X*points[0].Y - points[0].X*points[points.Count - 1].Y;
            s /= 2.0;
            return s;
        }

        /// <summary>
        /// 获取多边形顶点集合
        /// </summary>
        /// <param name="polygon">多边形</param>
        /// <returns>点集合</returns>
        public List<Point> GetPolygonPoints(Polygon polygon)
        {
            var points = new List<Point>();
            foreach (var arcDirection in polygon.ArcDirections)
            {
                if (arcDirection.Direction == 1)
                {
                    points.AddRange(arcDirection.Arc.Points);
                }
                else
                {
                    points.AddRange(arcDirection.Arc.Points);
                    points.Reverse();
                }
            }
            return points;
        } 

        /// <summary>
        /// 通过指定的弧段构建单个多边形
        /// </summary>
        public Polygon BuildPolygon(Arc arc)
        {
            //添加起始弧段
            var polygon = new Polygon();
            var arcDirection = new ArcDirection { Arc = arc, Direction = arc.Direction };
            polygon.ArcDirections.Add(arcDirection);

            //添加扩展弧段
            var nextArc = GetNextArc(arc);
            //扩展弧段不为起始弧段
            while (nextArc.Id != arc.Id)
            {
                nextArc.Direction = MinAngle.Direction;
                nextArc.TimesOfSearch += 1;

                var arcDir = new ArcDirection {Arc = nextArc, Direction = nextArc.Direction};
                polygon.ArcDirections.Add(arcDir);

                nextArc = GetNextArc(nextArc);
            }
            arc.TimesOfSearch += 1;
            return polygon;
        }

        /// <summary>
        /// 根据指定弧段获取下一个弧段
        /// </summary>
        /// <param name="arc">弧段</param>
        /// <returns>下一弧段</returns>
        public Arc GetNextArc(Arc arc)
        {
            var arcAngles = new List<ArcAngle>();
            var radian = 0.0d;
            if (arc.Direction == 1)
            {
                var start = arc.Points[arc.Points.Count - 2];
                var inflection = arc.Points[arc.Points.Count - 1];

                var pointArcs = GetPointArcsByPoint(inflection);
                foreach (var arc1 in pointArcs.Arcs)
                {
                    if (arc1.Id != arc.Id && arc1.TimesOfSearch < 2)
                    {
                        var arcAngle = new ArcAngle();
                        var sta = arc1.Points[0];
                        var end = arc1.Points[arc1.Points.Count - 1];

                        //确认扩展段
                        if (sta.Id == inflection.Id) //准扩展弧段以第一记录开始,方向与当前弧段相反
                        {
                            sta = arc1.Points[1];
                            radian = GetAngleOfTwoArcs(start, inflection, sta);
                            arcAngle.Direction = 1;
                        }
                        else if (end.Id == inflection.Id) //准扩展弧段以最后记录开始,方向与当前弧段一样
                        {
                            end = arc1.Points[arc1.Points.Count - 2];
                            radian = GetAngleOfTwoArcs(start, inflection, end);
                            arcAngle.Direction = -1;
                        }
                        arcAngle.Angle = radian;
                        arcAngle.Arc = arc1;

                        arcAngles.Add(arcAngle);
                    }
                }
            }
            else if (arc.Direction == -1)
            {
                var start = arc.Points[1];
                var inflection = arc.Points[0];

                var pointArcs = GetPointArcsByPoint(inflection);
                foreach (var arc1 in pointArcs.Arcs)
                {
                    if (arc1.Id != arc.Id && arc1.TimesOfSearch < 2)
                    {
                        var arcAngle = new ArcAngle();
                        var sta = arc1.Points[0];
                        var end = arc1.Points[arc1.Points.Count - 1];

                        if (sta.Id == inflection.Id)
                        {
                            sta = arc1.Points[1];
                            radian = GetAngleOfTwoArcs(start, inflection, sta);
                            arcAngle.Direction = 1;
                        }
                        else if (end.Id == inflection.Id)
                        {
                            end = arc1.Points[arc1.Points.Count - 2];
                            radian = GetAngleOfTwoArcs(start, inflection, end);
                            arcAngle.Direction = -1;
                        }
                        arcAngle.Angle = radian;
                        arcAngle.Arc = arc1;

                        arcAngles.Add(arcAngle);
                    }
                }
            }
            else
            {
                Console.WriteLine("弧段的方向错误!");
            }

            //没有任何夹角时,返回弧段本身
            if (arcAngles.Count == 0) return arc;

            //返回最小角度的弧段
            foreach (var arcAngle in arcAngles.Where(arcAngle => Math.Abs(arcAngle.Angle - arcAngles.Min(a => a.Angle)) < 0.0000001))
            {
                MinAngle = arcAngle;
            }
            return MinAngle.Arc;
        }

        /// <summary>
        /// 计算两条直线逆时针方向的夹角
        /// </summary>
        /// <param name="start">起始点</param>
        /// <param name="inflection">拐点</param>
        /// <param name="end">终止点</param>
        /// <returns>弧度</returns>
        public double GetAngleOfTwoArcs(Point start, Point inflection, Point end)
        {
            double aa = (start.X - inflection.X)*(start.X - inflection.X) +
                        (start.Y - inflection.Y)*(start.Y - inflection.Y);
            double bb = (inflection.X - end.X)*(inflection.X - end.X) +
                        (inflection.Y - end.Y)*(inflection.Y - end.Y);
            double cc = (start.X - end.X)*(start.X - end.X) +
                        (start.Y - end.Y)*(start.Y - end.Y);

            double cos = (aa + bb - cc)/(2*Math.Sqrt(aa)*Math.Sqrt(bb));
            double angle = Math.Acos(cos);

            if (Math.Abs(start.Y - inflection.Y) >= 0.000001)
            {
                double coeff1 = (inflection.X - start.X)/(start.Y - inflection.Y);
                double coeff2 = (start.X*inflection.Y - inflection.X*start.Y)/(start.Y - inflection.Y);
                if (start.Y > inflection.Y)
                {
                    if (end.X + coeff1*end.Y + coeff2 <= 0)
                        return angle;
                    return (Math.PI*2 - angle);
                }
                if (start.Y < inflection.Y)
                {
                    if (end.X + coeff1*end.Y + coeff2 <= 0)
                        return (Math.PI*2 - angle);
                    return angle;
                }
            }
            else
            {
                if (inflection.X > start.X)
                {
                    if (end.Y < start.Y) return angle;
                    return (Math.PI*2 - angle);
                }
                if (end.Y < start.Y) return (Math.PI*2 - angle);
                return angle;
            }
            return 10.0f;
        }

        public PointArcs GetPointArcsByPoint(Point point)
        {
            foreach (var pointarcs in PointArcses.Where(pointarcs => pointarcs.PointId == point.Id))
            {
                return pointarcs;
            }
            return new PointArcs();
        }
    }
}

 

测试

 public static void Main(string[] args)
        {
            //点数据初始化
            var points = new List<Point>();
            var p1 = new Point { Id = 1, X = 30, Y = 30};
            var p2 = new Point { Id = 2, X = 60, Y = 60 };
            var p3 = new Point { Id = 3, X = 40, Y = 80 };
            var p4 = new Point { Id = 4, X = 20, Y = 70 };
            var p5 = new Point { Id = 5, X = 10, Y = 50 };
            var p6 = new Point { Id = 6, X = 50, Y = 10 };
            var p7 = new Point { Id = 7, X = 40, Y = 30 };
            var p8 = new Point { Id = 8, X = 50, Y = 30 };
            var p9 = new Point { Id = 9, X = 50, Y = 20 };
            var p10 = new Point { Id = 10, X = 70, Y = 30 };
            points.Add(p1);
            points.Add(p2);
            points.Add(p3);
            points.Add(p4);
            points.Add(p5);
            points.Add(p6);
            points.Add(p7);
            points.Add(p8);
            points.Add(p9);
            points.Add(p10);

            //弧段-点数据初始化
            var arcs = new List<Arc>();
            var p = new List<Point> {p3, p4, p5, p1};
            var a = new Arc() { Id = "a", Points = p, Direction = 1 };
            p = new List<Point> { p3, p2 };
            var b = new Arc() { Id = "b", Points = p, Direction = 1 };
            p = new List<Point> { p2, p6 };
            var c = new Arc() { Id = "c", Points = p, Direction = 1 };
            p = new List<Point> { p2, p1 };
            var d = new Arc() { Id = "d", Points = p, Direction = 1 };
            p = new List<Point> { p3, p1 };
            var e = new Arc() { Id = "e", Points = p, Direction = 1 };
            p = new List<Point> { p7, p8, p9, p7 };
            var f = new Arc() { Id = "f", Points = p, Direction = 1 };
            p = new List<Point> { p6, p10, p2 };
            var g = new Arc() { Id = "g", Points = p, Direction = 1 };
            p = new List<Point> { p1, p6 };
            var h = new Arc() { Id = "h", Points = p, Direction = 1 };

            arcs.Add(a);
            arcs.Add(b);
            arcs.Add(c);
            arcs.Add(d);
            arcs.Add(e);
            arcs.Add(f);
            arcs.Add(g);
            arcs.Add(h);

            var createPolygons = new CreatePolygons(points, arcs);
            //自动生成多边形,并记录有效信息
            createPolygons.MainCreatePolygons();
            //根据多边形中的信息建立拓扑关系
            var topology = new Topology(points);
            //建立弧段-点关系
            topology.BuildArcPoints(arcs);
            //建立多边形-弧段关系
            topology.BuildPolygonsArcs(createPolygons.Polygons);
            //建立弧段-多边形关系
            topology.BuildArcsPolygon(arcs, createPolygons.Polygons);
            //打印拓扑关系
            topology.PrintTopology();
            Console.ReadLine();
        }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值