using System; using GeoAPI.Geometries; namespace SharpMap.Utilities { /// /// A /// public static class GeoSpatialMath { /// /// Conversion factor degrees to radians /// public const double DegToRad = Math.PI/180d; //0.01745329252; // Convert Degrees to Radians /// /// Meters per inch /// public const double MetersPerInch = 0.0254; /// /// Meters per mile /// public const double MetersPerMile = 1609.347219; /// /// Miles per degree at equator /// public const double MilesPerDegreeAtEquator = 69.171; /// /// Meters per degree at equator /// public const double MetersPerDegreeAtEquator = MetersPerMile * MilesPerDegreeAtEquator; /// /// Web Mercator SRID constant /// public const int WebMercatorSrid = 3857; /// /// Web Mercator SRID constant /// public const double WebMercatorRadius = 6378137.0; /// /// Web Mercator Domain as Envelope /// public static readonly Envelope WebMercatorEnv = new Envelope(-20037508.34,20037508.34,-20000000,20000000); /// /// Calculate the distance between 2 points on the great circle /// /// The first longitue value /// The latitude value for /// The second longitue value /// The latitude value for /// The distance in meters public static double GreatCircleDistance(double lon1, double lat1, double lon2, double lat2) { var lonDistance = DiffLongitude(lon1, lon2); var arg1 = Math.Sin(lat1*DegToRad)*Math.Sin(lat2*DegToRad); var arg2 = Math.Cos(lat1*DegToRad)*Math.Cos(lat2*DegToRad)*Math.Cos(lonDistance*DegToRad); return MetersPerDegreeAtEquator*Math.Acos(arg1 + arg2)/DegToRad; } /// /// Calculate the great circle distance between 2 points (ie the shortest distance on the sphere) /// /// The first longitue value /// The second longitue value /// The common latitued value for and /// The distance in meters public static double GreatCircleDistance(double lon1, double lon2, double lat) { var lonDistance = DiffLongitude(lon1, lon2); lat = Math.Abs(lat); if (lat >= 90.0) lat = 89.999; var distance = Math.Cos(lat*DegToRad)*MetersPerDegreeAtEquator*lonDistance; return distance; } /// /// Calculate the great circle distance between 2 points without constraining longitudinal REFLEX angle 0-180deg (ie supports angles > 180 deg). /// Typically used to support scale calculations on a global projection from longitude -180 to +180 (or even greater when zoomed out), /// this will NOT be the shortest distance on the sphere when longitudinal angle > 180 degrees. /// /// The first longitude value /// The second longitude value /// The common latitude value for and /// The distance in meters from LHS to RHS of a global projection. /// This will NOT the shortest distance on sphere for longitudinal REFLEX (> 180deg) angles public static double GreatCircleDistanceReflex(double lon1, double lon2, double lat) { var lonDistance = Math.Abs(lon2 - lon1); lat = Math.Abs(lat); if (lat >= 90.0) lat = 89.999; var distance = Math.Cos(lat * DegToRad) * MetersPerDegreeAtEquator * lonDistance; return distance; } /// /// Calculate the difference between two longitudal values constrained 0 - 180 deg /// /// The first longitue value in degrees /// The second longitue value in degrees /// The distance in degrees public 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; } } }