Bing地图中的线多边形交集

时间:2012-11-27 19:22:28

标签: gis geospatial bing-maps

免责声明:我不是GIS人员。

我们正在尝试使用DotSpatial库来计算线多边形交集,然后在WPF Bing Maps控件中显示该交集。出于某种原因,任何在EW方向上不完全笔直的交叉点都会从Bing中的原始线向下移动。我假设这是一个投影问题,因为当我们在投影到WGS1984的DotSpatial控件中显示所有内容时,不会发生移位。

要重新创建,请将以下内容放在地图窗口后面的xaml代码中:

using Microsoft.Maps.MapControl.WPF;
using System.Windows;
using System.Windows.Media;
using DotSpatial.Data;
using DotSpatial.Topology;
public partial class MainWindow : Window
{
    private LocationCollection _polygonLocs = new LocationCollection();

    public MainWindow ()
    {
        InitializeComponent();

        AddSquarePolygon();

        // angled line 1
        LocationCollection slantedLocs = new LocationCollection();
        slantedLocs.Add(new Microsoft.Maps.MapControl.WPF.Location(40, -97));
        slantedLocs.Add(new Microsoft.Maps.MapControl.WPF.Location(35, -86));
        AddAndIntersectLine( slantedLocs );

        // straight EW line 
        LocationCollection ewLocs = new LocationCollection();
        ewLocs.Add(new Microsoft.Maps.MapControl.WPF.Location(37, -97));
        ewLocs.Add(new Microsoft.Maps.MapControl.WPF.Location(37, -86));
        AddAndIntersectLine(ewLocs);
    }

    private void AddAndIntersectLine(LocationCollection lineLocs)
    {
        MapPolyline line = new MapPolyline() { Locations = lineLocs, Stroke = new SolidColorBrush(Colors.Black) };

        this._bingMap.Children.Add(line);

        LocationCollection inters = Intersect(lineLocs, _polygonLocs);

        MapPolyline interLine = new MapPolyline() { Locations = inters, Stroke = new SolidColorBrush(Colors.Red) };
        this._bingMap.Children.Add(interLine);

    }

    private void AddSquarePolygon()
    {
        _polygonLocs.Add(new Microsoft.Maps.MapControl.WPF.Location(39.0, -92));
        _polygonLocs.Add(new Microsoft.Maps.MapControl.WPF.Location(36.0, -92));
        _polygonLocs.Add(new Microsoft.Maps.MapControl.WPF.Location(36.0, -93));
        _polygonLocs.Add(new Microsoft.Maps.MapControl.WPF.Location(39.0, -93));

        MapPolygon square = new MapPolygon()
        {
            Locations = _polygonLocs,
            Stroke = new SolidColorBrush(Colors.Black)
        };

        this._bingMap.Children.Add(square);
    }

    public static LocationCollection Intersect(LocationCollection line, LocationCollection bounds)
    {
        Feature lineFeature = CreateFeatureFromLocations(line);
        Feature boundsFeature = CreateFeatureFromLocations(bounds);

        IFeature featureIntersection = boundsFeature.Intersection(lineFeature);

        if (featureIntersection != null)
        {
            return (CreateLocationsFromFeature(featureIntersection));
        }

        return new LocationCollection();
    }


    private static LocationCollection CreateLocationsFromFeature(IFeature feature)
    {
        LocationCollection lc = new LocationCollection();
        foreach (var coords in feature.Coordinates)
        {
            lc.Add(new Microsoft.Maps.MapControl.WPF.Location(coords.Y, coords.X));
        }

        return lc;
    }

    private static Feature CreateFeatureFromLocations(LocationCollection locs)
    {

        Coordinate[] coords = new Coordinate[locs.Count];
        long inx = 0;

        foreach (var l in locs)
        {
            Coordinate coord = new Coordinate();
            coord.X = l.Longitude;
            coord.Y = l.Latitude;
            coords[inx] = coord;
            inx++;
        }

        LineString ls = new LineString(coords);
        MultiLineString mls = new MultiLineString(ls);
        return new Feature(mls);
    }
}

2 个答案:

答案 0 :(得分:1)

这是因为你的线是一个测地线(即:大地水准面上的一条线)。当在平面地图上绘制时,它应该变成弧形,并且不再是直的。

1)你应该添加一个将MapPolyline切割成若干段的函数来绘制接近现实的弧

private static LocationCollection BuildGeodesicPolyline(Microsoft.Maps.MapControl.WPF.Location start, Microsoft.Maps.MapControl.WPF.Location end)
    {
        int segments = 32; // The number of line segments used to approximate the true curved route 
        LocationCollection latLongs = new LocationCollection();

        // Convert all coordinates to Radians
        double lat1 = start.Latitude * (Math.PI / 180);
        double lon1 = start.Longitude * (Math.PI / 180);
        double lat2 = end.Latitude * (Math.PI / 180);
        double lon2 = end.Longitude * (Math.PI / 180);
        // Calculate the total extent of the route
        double d = 2 * Math.Asin(Math.Sqrt(Math.Pow((Math.Sin((lat1 - lat2) / 2)), 2) + Math.Cos(lat1) * Math.Cos(lat2) * Math.Pow((Math.Sin((lon1 - lon2) / 2)), 2)));
        // Calculate the position at fixed intervals along the route
        for (double n = 0; n < segments + 1; n++)
        {
            double f = (1d / segments) * n;
            double A = Math.Sin((1 - f) * d) / Math.Sin(d);
            double B = Math.Sin(f * d) / Math.Sin(d);
            // Calculate 3D Cartesian coordinates of the point
            double x = A * Math.Cos(lat1) * Math.Cos(lon1) + B * Math.Cos(lat2) * Math.Cos(lon2);
            double y = A * Math.Cos(lat1) * Math.Sin(lon1) + B * Math.Cos(lat2) * Math.Sin(lon2);
            double z = A * Math.Sin(lat1) + B * Math.Sin(lat2);
            // Convert these to latitude/longitude
            double lat = Math.Atan2(z, Math.Sqrt(Math.Pow(x, 2) + Math.Pow(y, 2)));
            double lon = Math.Atan2(y, x);
            // Create a VELatLong representing this location (remember to convert back to degrees)
            double newLat = lat / (Math.PI / 180d);
            double newLon = lon / (Math.PI / 180d);
            Microsoft.Maps.MapControl.WPF.Location p = new Microsoft.Maps.MapControl.WPF.Location(newLat, newLon);
            // Add this to the array
            latLongs.Add(p);
        }

        return latLongs;
    }

取自http://www.beginningspatial.com/plotting_geography_linestrings_google_maps_and_virtual_earth

如果在“成角度的第1行”块之后添加这些行,您将看到实际上是弧形的黑色虚线:

slantedLocs = BuildGeodesicPolyline(new Microsoft.Maps.MapControl.WPF.Location(35d, -86d),new Microsoft.Maps.MapControl.WPF.Location(40d, -97d)) ;
MapPolyline m = new MapPolyline() { Locations = slantedLocs, Stroke = new SolidColorBrush(Colors.Black), StrokeThickness = 2d, StrokeDashArray = new DoubleCollection(new List<double>() { 5, 5 }) };
_bingMap.Children.Add(m);

2)你应该读一下DotSpatial,因为红线(交叉的结果)是使用平面坐标系,因此你的目的是错误的。以下是SQL Server对此的说法:

declare @p geography = geography::STPolyFromText('POLYGON((-92 39 , -93 39 , -93 36 ,-92 36  , -92 39 ))',4326)
declare @l1 geography = geography::STLineFromText('LINESTRING(-97 40, -86 35)',4326)

declare @pG geometry = geometry::STPolyFromText('POLYGON((-92 39 , -93 39 , -93 36 ,-92 36  , -92 39 ))',4326)
declare @l1G geometry = geometry::STLineFromText('LINESTRING(-97 40, -86 35)',4326)
select 
@p.STIntersection(@l1).ToString() as [GEODESIC] -- LINESTRING (-92.0000000179902 37.936656236067556, -93.000000053162651 38.376235391098518)
    , @pG.STIntersection(@l1G).ToString() as [PLANAR] -- LINESTRING (-93 38.18181818181818, -92 37.727272727272727)

平面和测地几何之间的几何操作在这种尺度上是不同的。

答案 1 :(得分:0)

您的位置集合/交叉点测试不适合海拔

您遇到这种情况是因为有时多边形旁边的点(在球形地图上)不一定在多边形之外。我们正在尝试在2D平原上测试3D点。

在同样的问题上,我的头撞了墙几天!我提出了一个视觉上吸引人的修复,这非常简单。

使用Point函数将全部多边形纬度/长度转换为屏幕上的LocationToViewPortpoint对象,以及您要测试交点的点,并使用X和Y值代替纬度/经度。