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;
}
}
}