我尝试使用限幅器库来合并我要导入Google地球的多边形(c#)。它使用了两个圆形的平面样本,但使用long和latitude作为X和Y有两个问题:
我想知道是否已经有针对该场景的实现/库。
更新:由于已经请求使用了源代码,我创建了一个包含相关代码的小型示例应用程序:
internal class Polygon : List<DoublePoint> { }
internal class Polygons : List<List<DoublePoint>> { }
internal class IntPolygon : List<IntPoint> { }
internal class IntPolygons : List<List<IntPoint>> { }
class Program
{
static void Main(string[] args)
{
MercatorProjection mercatorProjection = new MercatorProjection();
List<GeoCoordinate> circle1Coordinates = GenerateCirclePolygonByMeters(new GeoCoordinate { Longitude = 50, Latitude = 50 }, 5000);
List<GeoCoordinate> circle2Coordinates = GenerateCirclePolygonByMeters(new GeoCoordinate { Longitude = 50.05, Latitude = 50 }, 5000);
Polygons polygons = new Polygons();
Polygon circle1 = new Polygon();
foreach(var coordinate in circle1Coordinates)
{
DoublePoint point = mercatorProjection.FromLatLngToPoint(coordinate.Latitude, coordinate.Longitude, 15);
circle1.Add(point);
}
Polygon circle2 = new Polygon();
foreach (var coordinate in circle2Coordinates)
{
DoublePoint point = mercatorProjection.FromLatLngToPoint(coordinate.Latitude, coordinate.Longitude, 15);
circle2.Add(point);
}
polygons.Add(circle1);
polygons.Add(circle2);
// Workaround: get int-values
IntPolygons intPolygons = ConvertToIntPolygons(polygons);
// Merge
IntPolygons mergedIntPolygons = MergePolygons(intPolygons);
// Workaroud: Get doubles again
Polygons mergedPolygons = ConvertToDoublePolygons(mergedIntPolygons);
// Convert back to spherical surface
// GeoCoordinate class is from System.Device
List<GeoCoordinate> mergedCoordinates = new List<GeoCoordinate>();
foreach (var polygon in mergedPolygons)
{
foreach (var point in polygon)
{
GeoCoordinate coordinate = mercatorProjection.FromPointToLatLng(point, 15);
mergedCoordinates.Add(coordinate);
}
}
// Generate output csv-list
WriteOutputFile(mergedCoordinates);
}
private static void WriteOutputFile(List<GeoCoordinate> coordinates, string filename = "")
{
string uniquename = DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".txt";
string filenameToUse = String.IsNullOrEmpty(filename) ? uniquename : filename;
var fileStream = File.OpenWrite(filenameToUse);
using (var writer = new StreamWriter(fileStream))
{
// header
writer.WriteLine("lat,long");
// content
foreach (var coordinate in coordinates)
{
writer.WriteLine(coordinate.Latitude.ToString(CultureInfo.InvariantCulture) + "," + coordinate.Longitude.ToString(CultureInfo.InvariantCulture));
}
writer.Close();
}
}
private static Polygons ConvertToDoublePolygons(IntPolygons polygons)
{
double multiplier = 100;
Polygons doublePolygons = new Polygons();
foreach (var intPolygon in polygons)
{
Polygon doublePolygon = new Polygon();
foreach (var intPoint in intPolygon)
{
doublePolygon.Add(new DoublePoint { X = intPoint.X / multiplier, Y = intPoint.Y / multiplier });
}
doublePolygons.Add(doublePolygon);
}
return doublePolygons;
}
private static IntPolygons ConvertToIntPolygons(Polygons polygons)
{
double multiplier = 100;
IntPolygons intPolygons = new IntPolygons();
foreach (var doublePolygon in polygons)
{
IntPolygon intPolygon = new IntPolygon();
foreach (var doublePoint in doublePolygon)
{
intPolygon.Add(new IntPoint { X = (int)(doublePoint.X * multiplier), Y = (int)(doublePoint.Y * multiplier) });
}
intPolygons.Add(intPolygon);
}
return intPolygons;
}
private static List<GeoCoordinate> GenerateCirclePolygonByMeters(GeoCoordinate position, double distanceMeters)
{
// calc distance in coordinates-system
double latitude2 = CalculateDerivedPosition(position, distanceMeters, 0).Latitude;
double yRadius = latitude2 - position.Latitude;
double longitude2 = CalculateDerivedPosition(position, distanceMeters, 90).Longitude;
double xRadius = longitude2 - position.Longitude;
var circle = GenerateCirclePolygon(position.Longitude, position.Latitude, xRadius, yRadius, 16);
List<GeoCoordinate> circleCoordinates = new List<GeoCoordinate>();
foreach(var point in circle)
{
circleCoordinates.Add(new GeoCoordinate { Latitude = point.Y, Longitude = point.X });
}
return circleCoordinates;
}
private static Polygon GenerateCirclePolygon(double xStart, double yStart, double xRadius, double yRadius, int points)
{
Polygon polygon = new Polygon();
double slice = 2 * Math.PI / points;
for (int i = 0; i < points; i++)
{
double angle = slice * i;
Console.WriteLine(angle);
double x = xRadius * Math.Cos(angle);
double y = yRadius * Math.Sin(angle);
polygon.Add(new DoublePoint(xStart + x, yStart + y));
}
return polygon;
}
// Source: https://stackoverflow.com/questions/1125144/how-do-i-find-the-lat-long-that-is-x-km-north-of-a-given-lat-long
public static GeoCoordinate CalculateDerivedPosition(GeoCoordinate source, double rangeMeters, double bearing)
{
double radiansToDegrees = 57.2957795;
double degreesToRadians = 0.0174532925;
double twoPi = Math.PI * 2;
double earthRadius = 6378137.0;
double latA = source.Latitude * degreesToRadians;
double lonA = source.Longitude * degreesToRadians;
double angularDistance = rangeMeters / earthRadius;
double trueCourse = bearing * degreesToRadians;
double lat = Math.Asin(
Math.Sin(latA) * Math.Cos(angularDistance) +
Math.Cos(latA) * Math.Sin(angularDistance) * Math.Cos(trueCourse));
double dlon = Math.Atan2(
Math.Sin(trueCourse) * Math.Sin(angularDistance) * Math.Cos(latA),
Math.Cos(angularDistance) - Math.Sin(latA) * Math.Sin(lat));
double lon = ((lonA + dlon + Math.PI) % twoPi) - Math.PI;
return new GeoCoordinate(
lat * radiansToDegrees,
lon * radiansToDegrees);
}
private static IntPolygons MergePolygons(IntPolygons polygons)
{
Clipper clipper = new Clipper();
clipper.AddPaths(polygons, PolyType.ptSubject, true);
IntPolygons mergedPolygons = new IntPolygons();
clipper.Execute(ClipType.ctUnion, mergedPolygons,
PolyFillType.pftNonZero, PolyFillType.pftNonZero);
return mergedPolygons;
}
更新2:创建圆圈的方式确实存在错误。通过更正,圆圈可以正确创建和合并:
我发现我可以使用Mercator Projection将上图中圆圈的点投影到平面上。然后可以使用Clipper进行合并。最后,所有点都再次投射到球体上(墨卡托投影的反面)。
这将是一种解决方法(将更深入地测试它)。所以这个问题有点回答,但如果有其他解决方案的解决方法较少,那将会很有趣。