Skip to content

Instantly share code, notes, and snippets.

@FObermaier
Last active May 2, 2022 11:00
Show Gist options
  • Save FObermaier/0f88895bac6160b3ac0e380d3e12b421 to your computer and use it in GitHub Desktop.
Save FObermaier/0f88895bac6160b3ac0e380d3e12b421 to your computer and use it in GitHub Desktop.
Lat-/Longitude Utility
using System;
using System.Globalization;
using NetTopologySuite.Geometries;
namespace NetTopologySuite.Geography
{
public struct LatLon
{
/// <summary>
/// Initializes this LatLon struct with the provided <paramref name="lat"/> and <paramref name="lon"/> values
/// </summary>
/// <param name="lat">A latitude</param>
/// <param name="lon">A longitude</param>
public LatLon(double lat, double lon)
{
if (lat < -90 || lat > 90)
throw new ArgumentException("latitude outside of valid range", nameof(lat));
if (lon < -180 || lon > 180)
throw new ArgumentException("longitude outside of valid range", nameof(lon));
Lat = lat;
Lon = lon;
}
/// <summary>
/// Initializes this LatLon struct with <see cref="Lat"/>=<see cref="Coordinate.Z"/> and
/// <see cref="Lon"/>=<see cref="Coordinate.X"/> values of <paramref name="coordinate"/> values
/// </summary>
/// <param name="lat">A latitude</param>
/// <param name="lon">A longitude</param>
public LatLon(Coordinate coordinate)
:this(coordinate.Y, coordinate.X)
{
}
/// <summary>
/// Gets a value indicating the latitude
/// </summary>
public double Lat { get; }
/// <summary>
/// Gets a value indicating the longitude
/// </summary>
public double Lon { get; }
/// <inheritdoc/>
public override string ToString()
{
return string.Format(NumberFormatInfo.InvariantInfo,"LL({0:R},{1:R})", Lat, Lon);
}
/// <summary>
/// Implict conversion operator to transform a <see cref="Coordinate"/> into a <see cref="LatLon"/>
/// </summary>
/// <param name="coordinate">A coordinate</param>
public static implicit operator LatLon(Coordinate coordinate)
{
return new LatLon(coordinate);
}
/// <summary>
/// Implict conversion operator to transform a <see cref="LatLon"/> into a <see cref="Coordinate"/>
/// </summary>
/// <param name="ll">A LatLon</param>
public static implicit operator Coordinate(LatLon ll)
{
return new Coordinate(ll.Lon, ll.Lat);
}
}
}
using System;
using NetTopologySuite.Geography;
namespace NetTopologySuite.Algorithm
{
public static class LatLonDistance
{
/// <summary>
/// Conversion factor degrees to radians
/// </summary>
const double DegToRad = Math.PI / 180d; //0.01745329252; // Convert Degrees to Radians
/// <summary>
/// Meters per inch
/// </summary>
const double MetersPerInch = 0.0254;
/// <summary>
/// Meters per mile
/// </summary>
const double MetersPerMile = 1609.347219;
/// <summary>
/// Miles per degree at equator
/// </summary>
const double MilesPerDegreeAtEquator = 69.171;
/// <summary>
/// Meters per degree at equator
/// </summary>
const double MetersPerDegreeAtEquator = MetersPerMile * MilesPerDegreeAtEquator;
/// <summary>
/// Calculate the distance between 2 points on the great circle
/// </summary>
/// <param name="ll1">Lat/lon of the 1st point</param>
/// <param name="ll2">Lat/lon of the 2nd point</param>
/// <returns>The distance in meters</returns>
public static double Distance(LatLon ll1, LatLon ll2)
{
double lonDistance = DiffLongitude(ll1.Lon, ll2.Lon);
double arg1 = Math.Sin(ll1.Lat * DegToRad) * Math.Sin(ll2.Lat * DegToRad);
double arg2 = Math.Cos(ll1.Lat * DegToRad) * Math.Cos(ll2.Lat * DegToRad) * Math.Cos(lonDistance * DegToRad);
return MetersPerDegreeAtEquator * Math.Acos(arg1 + arg2) / DegToRad;
}
/// <summary>
/// Calculate the difference between two longitudinal values
/// </summary>
/// <param name="lon1">The first longitude value in degrees</param>
/// <param name="lon2">The second longitude value in degrees</param>
/// <returns>The distance in degrees</returns>
private static double DiffLongitude(double lon1, double lon2)
{
double diff;
if (lon1 > 180.0)
lon1 = 360.0 - lon1;
if (lon2 > 180.0)
lon2 = 360.0 - lon2;
if ((lon1 >= 0.0) && (lon2 >= 0.0))
diff = lon2 - lon1;
else if ((lon1 < 0.0) && (lon2 < 0.0))
diff = lon2 - lon1;
else
{
// different hemispheres
if (lon1 < 0)
lon1 = -1 * lon1;
if (lon2 < 0)
lon2 = -1 * lon2;
diff = lon1 + lon2;
if (diff > 180.0)
diff = 360.0 - diff;
}
return diff;
}
}
}
using System;
using System.Collections.Generic;
using NetTopologySuite.Algorithm;
using NetTopologySuite.Geometries;
namespace NetTopologySuite.Geography
{
public static class LatLonExtensions
{
public static LatLon GetLatLon(this CoordinateSequence self, int index)
{
return (LatLon)self.GetCoordinate(index);
}
public static void SetLatLon(this CoordinateSequence self, int index, LatLon ll)
{
self.SetOrdinate(index, Ordinate.Y, ll.Lat);
self.SetOrdinate(index, Ordinate.X, ll.Lon);
}
public static IEnumerable<LatLon> GetLatLons(this ICoordinateSequence self)
{
if (self == null)
yield break;
for (int i = 0; i < self.Count; i++)
yield return self.GetLatLon(i);
}
public static ICoordinateSequence Create(this ICoordinateSequenceFactory self, IList<LatLon> lls)
{
var llList = new List<Coordinate>();
foreach (var ll in lls)
llList.Add(ll);
return self.Create(llList.ToArray());
}
public static LatLon GetLatLon(this Coordinate self)
{
return self;
}
public static LatLon GetLatLon(this IGeometry self)
{
return self.Coordinate;
}
public static Point CreateGeoPoint(this GeometryFactory self, LatLon ll)
{
if (self.SRID != 4326)
throw new ArgumentException("Factory has invalid SRID", nameof(self));
return self.CreatePoint(self.CoordinateSequenceFactory.Create(new[] { ll }));
}
public static LineString CreateGeoLineString(this GeometryFactory self, IList<LatLon> lls)
{
if (self.SRID != 4326)
throw new ArgumentException("Factory has invalid SRID", nameof(self));
return self.CreateLineString(self.CoordinateSequenceFactory.Create(lls));
}
public static Polygon CreateGeoPolygon(this GeometryFactory self, IList<LatLon> lls)
{
if (self.SRID != 4326)
throw new ArgumentException("Factory has invalid SRID", nameof(self));
var ring = self.CreateLinearRing(self.CoordinateSequenceFactory.Create(lls));
return self.CreatePolygon(ring, null);
}
public static double GetLength(this IEnumerable<LatLon> self)
{
double res = 0;
var last = new LatLon();
int i = 0;
foreach(var ll in self)
{
if (i > 0)
res += LatLonDistance.Distance(last, ll);
last = ll;
i++;
}
return res;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment