如何在一组简单多边形中拆分多边形

时间:2015-09-14 13:55:16

标签: c# sql-server geospatial

我从SQL空间数据库中检索一个复杂的多边形,在C#中检索一个DbGeometry。复合意味着该多边形被定义为外环和一个或多个内环(如甜甜圈)。

是否有一种简单的方法可以在一组简单多边形中转换此复杂多边形。简单的多边形是没有内圈的plygon。

谢谢, 的Mickaël

2 个答案:

答案 0 :(得分:0)

我已经和DbGeometries玩了一段时间,this article提供了一个很好的工具来操纵它。

我怀疑这不能像return myGeo.ExteriorRing;那么简单,所以,根据评论的要求,我来了一些代码(基于文章):

//handling of LineStrings (I use this to build WPF Path for example)
        private static object LineStringToSomething(DbGeometry sqlGeometry)
        {
            object result = null;
            DbGeometry curPoint;
            System.Windows.Point startPoint = new System.Windows.Point()
            {
                X = sqlGeometry.PointAt(1).XCoordinate.Value,
                Y = sqlGeometry.PointAt(1).YCoordinate.Value
            };

            curPoint = sqlGeometry.PointAt(1);

            for (int i = 2; i <= sqlGeometry.PointCount; i++)
            {
                //Do something with the line between curPoint and PointAt(i)

                curPoint = sqlGeometry.PointAt(i);
            }

            return result;
        }

        //Defines an extension method on DbGeometry objects
        //usage :
        // myOwnGeometry = myGeo.AsSimpleGeometry();
        public static object AsSimpleGeometry(this DbGeometry sqlGeometry)
        {
            object result = null;

            switch (sqlGeometry.SpatialTypeName.ToLower())
            {
                case "point":
                    //Here we found a point

                    return result;

                case "polygon":
                    // A Spacial Polygon is a collection of Rings
                    // A Ring is a Closed LineString, i.e. a collection of lines.
                    List<object> lotOfGeos = new List<object>();
                    // Outer Ring
                    return LineStringToSomething(sqlGeometry.ExteriorRing);

                    // Inner Rings (holes in the donut)
                    for (int i = 1; i <= sqlGeometry.InteriorRingCount; i++)
                    {
                        //just comment to ignore the inner loops
                        lotOfGeos.Add(LineStringToSomething(sqlGeometry.InteriorRingAt(i)));
                    }

                    return lotOfGeos;

                case "linestring":
                    // Return a PathFigure
                    return LineStringToSomething(sqlGeometry);

                case "multipoint":
                case "multilinestring":
                case "multipolygon":
                case "geometrycollection":
                    //Here we handle a collection of points, polygons and/or lines
                    List<object> moreGeos = new List<object>();
                    for (int i = 1; i <= sqlGeometry.ElementCount.Value; i++)
                    {
                        //Simply calling the same method on each item
                        moreGeos.Add(sqlGeometry.ElementAt(i).AsSimpleGeometry());
                    }

                    return moreGeos;

                default:
                    // Unrecognized Type
                    // Shall not happen
                    return null;
            }
        }

由于我不确切地知道您尝试使用几何形状实现的目标,因此我对地理数据的处理更加具体(特别是在LineStringToSomething中)

答案 1 :(得分:0)

根据我的理解,您希望简化POLYGON或MULTIPOLYGON几何实例以基本上删除所有孔。当实例是MULTIPOLYGON时,你不希望得到什么,但我会假设你想要在同一个实例中的所有外部边界。

因此我建议如下:

首先,定义一个简化几何的方法:

    using Microsoft.SqlServer.Types;
    using System.Data.Entity.Spatial;

    private static DbGeometry GetSimpleDbGeography(DbGeometry input)
    {
        // We can create geometry using Microsoft.SqlServer.Types.SqlGeometryBuilder
        // We have to use this to reconstruct the geometry we want from the input as DbGeometry.ExteriorRing() returns a LINESTRING which is no good to us
        SqlGeometryBuilder builder = new SqlGeometryBuilder();

        // We MUST set an SRID
        builder.SetSrid(0);

        OpenGisGeometryType ourType;

        // We must set the type
        if (input.SpatialTypeName.ToUpper() == "POLYGON")
            ourType = OpenGisGeometryType.Polygon;
        else if (input.SpatialTypeName.ToUpper() == "MULTIPOLYGON")
            ourType = OpenGisGeometryType.MultiPolygon;
        else
            throw new ArgumentException("Non Polygon received.");

        // Tell the Builder what we're creating
        builder.BeginGeometry(ourType);

        // This assumes we have a valid DbGeometry instance, otherwise .Value will cause an error
        int numberOfElements = input.ElementCount.Value;

        // Loop through each element, this will either be one (POLYGON) or more (MULTIPOLYGON)
        for (int i = 1; i < (numberOfElements + 1); i++)
        {
            // BeginGeometry only required for MULTIPOLYGON
            if (ourType == OpenGisGeometryType.MultiPolygon)
            {
                // Begin a POLYGON geometry
                builder.BeginGeometry(OpenGisGeometryType.Polygon);
            }

            // ElementAt() is not zero-based index
            DbGeometry element = input.ElementAt(i).ExteriorRing;

            // Start the figure with the first point
            builder.BeginFigure(element.StartPoint.XCoordinate.Value, element.StartPoint.YCoordinate.Value);

            // Lopp through remaining points
            for (int j = 2; j < (element.PointCount.Value + 1); j++)
            {
                // PointAt() is not zero-based index
                builder.AddLine(element.PointAt(j).XCoordinate.Value, element.PointAt(j).YCoordinate.Value);
            }

            // End the current polygon
            builder.EndFigure();

            // EndGeometry only required for MULTIPOLYGON
            if (ourType == OpenGisGeometryType.MultiPolygon)
            {
                // End the current Geometry
                builder.EndGeometry();
            }
        }

        // Finalise the geometry
        builder.EndGeometry();

        // Convert the construsted geometry back to a DbGeometry instance
        DbGeometry finalGeometry = DbGeometry.FromBinary(builder.ConstructedGeometry.STAsBinary().Buffer);

        return finalGeometry;
    }

现在只需这样打电话:

// Two sample donuts
DbGeometry donut1 = DbGeometry.FromText("POLYGON((0 0, 3 0, 3 3, 0 3, 0 0),(1 1, 2 1, 2 2, 1 2, 1 1))", 0);
DbGeometry donut2 = DbGeometry.FromText("POLYGON((10 10, 13 10, 13 13, 10 13, 10 10),(11 11, 12 11, 12 12, 11 12, 11 11))", 0);

// A merged, double-donut
DbGeometry doubleDonut = donut1.Union(donut2);

// Produces POLYGON((0 0, 3 0, 3 3, 0 3, 0 0))
DbGeometry donut1_simple = GetSimpleDbGeography(donut1);

// Produces MULTIPOLYGON(((10 10, 13 10, 13 13, 10 13, 10 10)),((0 0, 3 0, 3 3, 0 3, 0 0)))
DbGeometry doubleDonut_simple = GetSimpleDbGeography(doubleDonut);

基本上,它会按原样重新创建几何体,但没有任何孔。我真的很喜欢把它放在一起的挑战! : - )