The formulas for the azimuthal equidistant projection can be found at mathworld.wolfram.com. This formula can then be directly translated into F# like this:
open System module AzimuthalEquidistantProjection = let inline degToRad d = 0.0174532925199433 * d; // (1.0/180.0 * Math.PI) * d let project centerlon centerlat lon lat = // http://mathworld.wolfram.com/AzimuthalEquidistantProjection.html // http://www.radicalcartography.net/?projectionref let t:float = degToRad lat let l:float = degToRad lon let t1 = degToRad centerlat // latitude center of projection let l0 = degToRad centerlon // longitude center of projection let c = acos ((sin t1) * (sin t) + (cos t1) * (cos t) * (cos (l-l0))) let k = c / (sin c) let x = k * (cos t) * (sin (l-l0)) let y = k * ((cos t1) * (sin t) - (sin t1) * (cos t) * (cos (l-l0))) (x, y)
I also implemented the azimuthal equidistant projection by using units of measure. I create one set of measures to distinguish between degrees and radians and another one to distinguish between x and y.
open System module AzimuthalEquidistantProjectionWithMeasures = [<Measure>] type deg // degrees [<Measure>] type rad // radians [<Measure>] type x [<Measure>] type y type lon = float<x deg> type lat = float<y deg> let inline degToRad (d:float<'u deg>) = d*0.0174532925199433<rad/deg> let cos (d:float<'u rad>) = Math.Cos(float d) let sin (d:float<'u rad>) = Math.Sin(float d) let project (centerlon:lon) (centerlat:lat) (lon:lon) (lat:lat) = // http://mathworld.wolfram.com/AzimuthalEquidistantProjection.html // http://www.radicalcartography.net/?projectionref let t = degToRad lat let l = degToRad lon let t1 = degToRad centerlat // latitude center of projection let l0 = degToRad centerlon // longitude center of projection let c = acos ((sin t1) * (sin t) + (cos t1) * (cos t) * (cos (l-l0))) let k = c / (sin c) let x:float<x> = k * (cos t) * (sin (l-l0)) |> LanguagePrimitives.FloatWithMeasure let y:float<y> = k * ((cos t1) * (sin t) - (sin t1) * (cos t) * (cos (l-l0))) |> LanguagePrimitives.FloatWithMeasure (x, y)
In the past I've used the python OGR bindings and the ESRI Projection Engine for my map projection needs but this time I needed a pure python implementation so I translated the above code and optimized it a bit by precalculating some values that we'll need when we project the points during the initialization of my projection class. A similarly optimized version in F# is on fssnip.net.
from math import cos, sin, acos, radians class Point(object): def __init__(self,x,y): self.x = x self.y = y class AzimuthalEquidistantProjection(object): """ http://mathworld.wolfram.com/AzimuthalEquidistantProjection.html http://www.radicalcartography.net/?projectionref """ def __init__(self, center): self.center = center self.t1 = radians(center.y) ## latitude center of projection self.l0 = radians(center.x) ## longitude center of projection self.cost1 = cos(self.t1) self.sint1 = sin(self.t1) def project(self, point): t = radians(point.y) l = radians(point.x) costcosll0 = cos(t) * cos(l-self.l0) sint = sin(t) c = acos ((self.sint1) * (sint) + (self.cost1) * costcosll0) k = c / sin(c) x = k * cos(t) * sin(l-self.l0) y = k * (self.cost1 * sint - self.sint1 * costcosll0) return Point(x, y) import unittest class Test_AzimuthalEquidistantProjection(unittest.TestCase): def test_project(self): p = AzimuthalEquidistantProjection(Point(0.0,0.0)) r = p.project(Point(1.0,1.0)) self.assertAlmostEqual(0.01745152022, r.x) self.assertAlmostEqual(0.01745417858, r.y) p = AzimuthalEquidistantProjection(Point(1.0,2.0)) r = p.project(Point(3.0,4.0)) self.assertAlmostEqual(0.03482860733, r.x) self.assertAlmostEqual(0.03494898734, r.y) p = AzimuthalEquidistantProjection(Point(-10.0001, 80.0001)) r = p.project(Point(7.935, 63.302)) self.assertAlmostEqual(0.1405127567, r.x) self.assertAlmostEqual(-0.263406547, r.y) if __name__ == '__main__': unittest.main()
I've also implemented the straightforward projection code in Julia. Julia is a promising language built for technical computing. It's still only on version 0.2 but a lot of packages have already been created for the language.
function degToRad(d) d*0.0174532925199433 end function project(centerlon, centerlat, lon, lat) t = degToRad(lat) l = degToRad(lon) t1 = degToRad(centerlat) l0 = degToRad(centerlon) c = acos(sin(t1) * sin(t) + cos(t1) * cos(t) * cos(l-l0)) k = c / sin(c) x = k * cos(t) * sin(l-l0) y = k * (cos(t1) * sin(t) - sin(t1) * cos(t) * cos(l-l0)) x, y end
More Python, F#, Julia and geospatial code will be posted here so make sure to subscribe to my e-mail updates on the left. I also occasionally tweet as @gissolved.
2 comments:
Comparing with equation 2 in the Wolfram link, aren't you forgetting the parenthesis in calculating y?
Should be:
y = k * (self.cost1 * sint - self.sint1 * costcosll0)
You're right, I updated the code.
Post a Comment