免责声明:我不是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);
}
}
答案 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值代替纬度/经度。