项目作者: ofmooseandmen

项目描述 :
Geographical Position Calculations
高级语言: Haskell
项目地址: git://github.com/ofmooseandmen/jord.git
创建时间: 2018-06-27T16:24:41Z
项目社区:https://github.com/ofmooseandmen/jord

开源协议:BSD 3-Clause "New" or "Revised" License

下载


Jord - Geographical Position Calculations

GitHub CI
Hackage
license

Jord [Swedish] is Earth [English]

What is this?

Jord is a Haskell library that implements various geographical position calculations using the algorithms described in Gade, K. (2010) - A Non-singular Horizontal Position Representation,
Shudde, Rex H. (1986) - Some tactical algorithms for spherical geometry and Vincenty, T. (1975) - Direct and Inverse Solutions of Geodesics on the Ellipsoid:

  • conversion between ECEF (earth-centred, earth-fixed), latitude/longitude and n-vector positions for spherical and ellipsoidal earth model,
  • conversion between latitude/longitude and n-vector positions,
  • local frames (body; local level, wander azimuth; north, east, down): delta between positions, target position from reference position and delta,
  • great circles: surface distance, initial & final bearing, interpolated position, great circle intersections, cross track distance, …,
  • geodesic: surface distance, initial & final bearing and destination,
  • kinematics: position from p0, bearing and speed, closest point of approach between tracks, intercept (time, speed, minimum speed),
  • transformation between coordinate systems (both fixed and time-dependent).
  • polygon triangulation

How do I build it?

If you have Stack, then:

  1. $ stack build --test

If you have Cabal, then:

  1. $ cabal v2-build
  2. $ cabal v2-test

How do I use it?

See documentation on Hackage

Import the core functional modules as qualified:

  1. import qualified Data.Geo.Jord.Angle as Angle
  2. import qualified Data.Geo.Jord.Duration as Duration
  3. import qualified Data.Geo.Jord.Geocentric as Geocentric
  4. import qualified Data.Geo.Jord.Geodetic as Geodetic
  5. import qualified Data.Geo.Jord.Geodesic as Geodesic
  6. import qualified Data.Geo.Jord.GreatCircle as GreatCircle
  7. import qualified Data.Geo.Jord.Kinematics as Kinematics
  8. import qualified Data.Geo.Jord.Length as Length
  9. import qualified Data.Geo.Jord.Local as Local
  10. import qualified Data.Geo.Jord.Polygon as Polygon
  11. import qualified Data.Geo.Jord.Positions as Positions
  12. import qualified Data.Geo.Jord.Speed as Speed
  13. import qualified Data.Geo.Jord.Tx as Tx

Note: modules can be selectively imported as non-qualified, but i.m.o. the code reads better when modules are imported qualified.

Import models and transformation parameters:

  1. import Data.Geo.Jord.Model (Epoch(..))
  2. import Data.Geo.Jord.Models
  3. import qualified Data.Geo.Jord.Txs as Txs

Solutions to the 10 examples from NavLab

Example 1: A and B to delta

Given two positions, A and B as latitudes, longitudes and depths relative to Earth, E.

Find the exact vector between the two positions, given in meters north, east, and down, and find the direction (azimuth)
to B, relative to north. Assume WGS-84 ellipsoid. The given depths are from the ellipsoid surface. Use position A to
define north, east, and down directions. (Due to the curvature of Earth and different directions to the North Pole,
the north, east, and down directions will change (relative to Earth) for different places. A must be outside the poles
for the north and east directions to be defined.)

  1. example1 :: IO()
  2. example1 = do
  3. let pA = Geodetic.latLongHeightPos 1 2 (Length.metres 3) WGS84
  4. let pB = Geodetic.latLongHeightPos 4 5 (Length.metres 6) WGS84
  5. let ned = Local.nedBetween pA pB
  6. -- Ned {north = 331.730863099km, east = 332.998501491km, down = 17.39830421km}
  7. let slantRange = Local.slantRange ned
  8. -- 470.357383823km
  9. let bearing = Local.bearing ned
  10. -- 45°6'33.346"
  11. let elevation = Local.elevation ned
  12. -- -2°7'11.381"
  13. putStrLn ("NavLab, Example1: A and B to delta\n\
  14. \ delta = " ++ (show ned) ++ "\n\
  15. \ slantRange = " ++ (show slantRange) ++ "\n\
  16. \ bearing = " ++ (show bearing) ++ "\n\
  17. \ elevation = " ++ (show elevation) ++ "\n")

Example 2: B and delta to C

A radar or sonar attached to a vehicle B (Body coordinate frame) measures the distance and direction to an object C.
We assume that the distance and two angles (typically bearing and elevation relative to B) are already combined to the
vector p_BC_B (i.e. the vector from B to C, decomposed in B). The position of B is given as n_EB_E and z_EB, and the
orientation (attitude) of B is given as R_NB (this rotation matrix can be found from roll/pitch/yaw by using zyx2R).

Find the exact position of object C as n-vector and depth ( n_EC_E and z_EC ), assuming Earth ellipsoid with semi-major
axis a and flattening f. For WGS-72, use a = 6 378 135 m and f = 1/298.26.

  1. example2 :: IO()
  2. example2 = do
  3. let frameB =
  4. Local.frameB
  5. (Angle.decimalDegrees 40)
  6. (Angle.decimalDegrees 20)
  7. (Angle.decimalDegrees 30)
  8. let pB = Geodetic.nvectorHeightPos 1 2 3 (Length.metres 400) WGS72
  9. let delta = Local.deltaMetres 3000 2000 100
  10. let pC = Local.destination pB frameB delta
  11. -- 53°18'46.839"N,63°29'6.179"E 406.006017m (WGS72)
  12. putStrLn ("NavLab, Example 2: B and delta to C\n\
  13. \ pC = " ++ (show pC) ++ "\n")

Example 3: ECEF-vector to geodetic latitude

Position B is given as an “ECEF-vector” p_EB_E (i.e. a vector from E, the center of the Earth, to B, decomposed in E).
Find the geodetic latitude, longitude and height (latEB, lonEB and hEB), assuming WGS-84 ellipsoid.

  1. example3 :: IO()
  2. example3 = do
  3. let ecef = Geocentric.metresPos 5733900.0 (-6371000.0) 7008100.000000001 WGS84
  4. let geod = Positions.toGeodetic ecef
  5. -- 39°22'43.495"N,48°0'46.035"W 4702.059834295km (WGS84)
  6. putStrLn ("NavLab, 3: ECEF-vector to geodetic latitude\n\
  7. \ geodetic pos = " ++ (show geod) ++ "\n")

Example 4: Geodetic latitude to ECEF-vector

Geodetic latitude, longitude and height are given for position B as latEB, lonEB and hEB, find the ECEF-vector
for this position, p_EB_E.

  1. example4 :: IO()
  2. example4 = do
  3. let geod = Geodetic.latLongHeightPos 1 2 (Length.metres 3) WGS84
  4. let ecef = Positions.toGeocentric geod
  5. -- Position {gx = 6373.290277218km, gy = 222.560200675km, gz = 110.568827182km, model = WGS84}
  6. putStrLn ("NavLab, 4: Geodetic latitude to ECEF-vector\n\
  7. \ geocentric pos = " ++ (show ecef) ++ "\n")

Example 5: Surface distance

Find the surface distance sAB (i.e. great circle distance) between two positions A and B. The heights of A and B are
ignored, i.e. if they don’t have zero height, we seek the distance between the points that are at the surface of the
Earth, directly above/below A and B. The Euclidean distance (chord length) dAB should also be found.
Use Earth radius 6371e3 m. Compare the results with exact calculations for the WGS-84 ellipsoid.

  1. example5 :: IO()
  2. example5 = do
  3. let pA = Geodetic.s84Pos 88 0
  4. let pB = Geodetic.s84Pos 89 (-170)
  5. let distance = GreatCircle.distance pA pB
  6. -- 332.456901835km
  7. putStrLn ("NavLab, 5: Surface distance\n\
  8. \ distance = " ++ (show distance) ++ "\n")

Example 6: Interpolated position

Given the position of B at time t0 and t1, n_EB_E(t0) and n_EB_E(t1).

Find an interpolated position at time ti, n_EB_E(ti). All positions are given as n-vectors.

  1. example6 :: IO()
  2. example6 = do
  3. let pA = Geodetic.s84Pos 89 0
  4. let pB = Geodetic.s84Pos 89 180
  5. let f = 0.6
  6. let interpolated = GreatCircle.interpolated pA pB f
  7. -- 89°47'59.929"N,180°0'0.000"E (S84)
  8. putStrLn ("NavLab, 6: Interpolated position\n\
  9. \ interpolated = " ++ (show interpolated) ++ "\n")

Example 7: Mean position

Three positions A, B, and C are given as n-vectors n_EA_E, n_EB_E, and n_EC_E. Find the mean position, M, given as
n_EM_E. Note that the calculation is independent of the depths of the positions.

  1. example7 :: IO()
  2. example7 = do
  3. let ps =
  4. [ Geodetic.s84Pos 90 0
  5. , Geodetic.s84Pos 60 10
  6. , Geodetic.s84Pos 50 (-20)
  7. ]
  8. let mean = GreatCircle.mean ps
  9. -- Just 67°14'10.150"N,6°55'3.040"W (S84)
  10. putStrLn ("NavLab, Example 7: Mean position\n\
  11. \ mean = " ++ (show mean) ++ "\n")

Example 8: A and azimuth/distance to B

We have an initial position A, direction of travel given as an azimuth (bearing) relative to north (clockwise), and finally the distance to travel along a great circle given as sAB. Use Earth radius 6371e3 m to find the destination
point B.

In geodesy this is known as “The first geodetic problem” or “The direct geodetic problem” for a sphere, and we see that this is similar to Example 2, but now the delta is given as an azimuth and a great circle distance.
(“The second/inverse geodetic problem” for a sphere is already solved in Examples 1 and 5.)

  1. example8 :: IO()
  2. example8 = do
  3. let p = Geodetic.s84Pos 80 (-90)
  4. let bearing = Angle.decimalDegrees 200
  5. let distance = Length.metres 1000
  6. let dest = GreatCircle.destination p bearing distance
  7. -- 79°59'29.575"N,90°1'3.714"W (S84)
  8. putStrLn ("NavLab, Example 8: A and azimuth/distance to B\n\
  9. \ destination = " ++ (show dest) ++ "\n")

Example 9: Intersection of two paths

Define a path from two given positions (at the surface of a spherical Earth), as the great circle that goes through the two points.

Path A is given by A1 and A2, while path B is given by B1 and B2.

Find the position C where the two great circles intersect.

  1. example9 :: IO()
  2. example9 = do
  3. let a1 = Geodetic.s84Pos 51.885 0.235
  4. let a2 = Geodetic.s84Pos 48.269 13.093
  5. let b1 = Geodetic.s84Pos 49.008 2.549
  6. let b2 = Geodetic.s84Pos 56.283 11.304
  7. let ga = GreatCircle.through a1 a2
  8. let gb = GreatCircle.through b1 b2
  9. let intersections = GreatCircle.intersections <$> ga <*> gb
  10. -- Just (Just (50°54'6.260"N,4°29'39.052"E (S84),50°54'6.260"S,175°30'20.947"W (S84)))
  11. let ma = GreatCircle.minorArc a1 a2
  12. let mb = GreatCircle.minorArc b1 b2
  13. let intersection = GreatCircle.intersection <$> ma <*> mb
  14. -- Just (Just 50°54'6.260"N,4°29'39.052"E (S84))
  15. putStrLn ("NavLab, Example 9: Intersection of two paths\n\
  16. \ great circle intersections = " ++ (show intersections) ++ "\n\
  17. \ minor arc intersection = " ++ (show intersection) ++ "\n")

Example 10: Cross track distance

*Path A is given by the two positions A1 and A2 (similar to the previous example).

Find the cross track distance sxt between the path A (i.e. the great circle through A1 and A2) and the position B
(i.e. the shortest distance at the surface, between the great circle and B).

  1. example10 :: IO()
  2. example10 = do
  3. let p = Geodetic.s84Pos 1 0.1
  4. let gc = GreatCircle.through (Geodetic.s84Pos 0 0) (Geodetic.s84Pos 10 0)
  5. let sxt = fmap (\g -> GreatCircle.crossTrackDistance p g) gc
  6. -- Just 11.117814411km
  7. putStrLn ("NavLab, Example 10: Cross track distance\n\
  8. \ cross track distance = " ++ (show sxt) ++ "\n")

Solutions to the geodesic problems (Vincenty)

Example 11: Inverse problem

Given the coordinates of the two points (Φ1, L1) and (Φ2, L2), the inverse problem finds the azimuths α1, α2 and the ellipsoidal distance s.

  1. example11 :: IO()
  2. example11 = do
  3. let pA = Geodetic.wgs84Pos 88 0
  4. let pB = Geodetic.wgs84Pos 89 (-170)
  5. let inv = Geodesic.inverse pA pB
  6. let initialBearing = fmap Geodesic.initialBearing inv
  7. -- Just (Just 356°40'8.701")
  8. let finalBearing = fmap Geodesic.finalBearing inv
  9. -- Just (Just 186°40'19.615")
  10. let distance = fmap Geodesic.length inv
  11. -- Just 333.947509469km
  12. putStrLn ("Geodesic, Example 11: Inverse problem\n\
  13. \ initial bearing = " ++ (show initialBearing) ++ "\n\
  14. \ final bearing = " ++ (show finalBearing) ++ "\n\
  15. \ distance = " ++ (show distance) ++ "\n")

Example 12: Direct problem

Given an initial point (Φ1, L1) and initial azimuth, α1, and a distance, s, along the geodesic the problem is to find the end point (Φ2, L2) and azimuth, α2.

  1. example12 :: IO()
  2. example12 = do
  3. let p = Geodetic.wgs84Pos 80 (-90)
  4. let bearing = Angle.decimalDegrees 200
  5. let distance = Length.metres 1000
  6. let dct = Geodesic.direct p bearing distance
  7. let destination = fmap Geodesic.endPosition dct
  8. -- Just 79°59'29.701"N,90°1'3.436"W (WGS84)
  9. let finalBearing = fmap Geodesic.finalBearing dct
  10. -- Just (Just 199°58'57.528")
  11. putStrLn ("Geodesic, Example 12: Direct problem\n\
  12. \ destination = " ++ (show destination) ++ "\n\
  13. \ final bearing = " ++ (show finalBearing) ++ "\n")

Solutions to kinematics problems

Example 13: Closest point of approach

The Closest Point of Approach (CPA) refers to the positions at which two dynamically moving objects reach their
closest possible distance.

  1. example13 :: IO()
  2. example13 = do
  3. let ownship = Kinematics.Track
  4. (Geodetic.s84Pos 20 (-60))
  5. (Angle.decimalDegrees 10)
  6. (Speed.knots 15)
  7. let intruder = Kinematics.Track
  8. (Geodetic.s84Pos 34 (-50))
  9. (Angle.decimalDegrees 220)
  10. (Speed.knots 300)
  11. let cpa = Kinematics.cpa ownship intruder
  12. let timeToCpa = fmap Kinematics.timeToCpa cpa
  13. -- Just 3H9M56.155S
  14. let distanceAtCpa = fmap Kinematics.distanceAtCpa cpa
  15. -- Just 124.231730834km
  16. let cpaOwnshipPosition = fmap Kinematics.cpaOwnshipPosition cpa
  17. -- Just 20°46'43.641"N,59°51'11.225"W (S84)
  18. let cpaIntruderPosition = fmap Kinematics.cpaIntruderPosition cpa
  19. -- Just 21°24'8.523"N,60°50'48.159"W (S84)
  20. putStrLn ("Kinematics, Example 13: Closest point of approach\n\
  21. \ time to CPA = " ++ (show timeToCpa) ++ "\n\
  22. \ distance at CPA = " ++ (show distanceAtCpa) ++ "\n\
  23. \ CPA ownship pos = " ++ (show cpaOwnshipPosition) ++ "\n\
  24. \ CPA intruder pos = " ++ (show cpaIntruderPosition) ++ "\n")

Example 14: Speed required to intercept target

Inputs are the initial latitude and longitude of an interceptor and a target, and the target course and speed.
Also input is the time of the desired intercept. Outputs are the speed required of the interceptor, the course
of the interceptor, the distance travelled to intercept, and the latitude and longitude of the intercept.

  1. example14 :: IO()
  2. example14 = do
  3. let track = Kinematics.Track
  4. (Geodetic.s84Pos 34 (-50))
  5. (Angle.decimalDegrees 220)
  6. (Speed.knots 600)
  7. let interceptor = Geodetic.s84Pos 20 (-60)
  8. let interceptTime = Duration.seconds 2700
  9. let intercept = Kinematics.interceptByTime track interceptor interceptTime
  10. let distanceToIntercept = fmap Kinematics.distanceToIntercept intercept
  11. -- Just 1015.302358852km
  12. let interceptPosition = fmap Kinematics.interceptPosition intercept
  13. -- Just 28°8'12.046"N,55°27'21.411"W (S84)
  14. let interceptorBearing = fmap Kinematics.interceptorBearing intercept
  15. -- Just 26°7'11.649"
  16. let interceptorSpeed = fmap Kinematics.interceptorSpeed intercept
  17. -- Just 1353.736478km/h
  18. putStrLn ("Kinematics, Example 14: Speed required to intercept target\n\
  19. \ distance to intercept = " ++ (show distanceToIntercept) ++ "\n\
  20. \ intercept position = " ++ (show interceptPosition) ++ "\n\
  21. \ interceptor bearing = " ++ (show interceptorBearing) ++ "\n\
  22. \ interceptor speed = " ++ (show interceptorSpeed) ++ "\n")

Example 15: Time required to intercept target

Inputs are the initial latitude and longitude of an interceptor and a target, and the target course and speed. For a
given interceptor speed, it may or may not be possible to make an intercept.

The first algorithm is to compute the minimum interceptor speed required to achieve intercept and the time required to
make such and intercept.

The second algorithm queries the user to input an interceptor speed. If the speed is at least that required for intercept
then the time required to intercept is computed.

  1. example15 :: IO()
  2. example15 = do
  3. let track = Kinematics.Track
  4. (Geodetic.s84Pos 34 (-50))
  5. (Angle.decimalDegrees 220)
  6. (Speed.knots 600)
  7. let interceptor = Geodetic.s84Pos 20 (-60)
  8. let minIntercept = Kinematics.intercept track interceptor
  9. let minTimeToIntercept = fmap Kinematics.timeToIntercept minIntercept
  10. -- Just 1H39M53.831S
  11. let minDistanceToIntercept = fmap Kinematics.distanceToIntercept minIntercept
  12. -- Just 162.294627463km
  13. let minInterceptPosition = fmap Kinematics.interceptPosition minIntercept
  14. -- Just 20°43'42.305"N,61°20'56.848"W (S84)
  15. let minInterceptorBearing = fmap Kinematics.interceptorBearing minIntercept
  16. -- Just 300°10'18.053"
  17. let minInterceptorSpeed = fmap Kinematics.interceptorSpeed minIntercept
  18. -- Just 97.476999km/h
  19. putStrLn ("Kinematics, Example 15: Time required to intercept target (min)\n\
  20. \ time to intercept = " ++ (show minTimeToIntercept) ++ "\n\
  21. \ distance to intercept = " ++ (show minDistanceToIntercept) ++ "\n\
  22. \ intercept position = " ++ (show minInterceptPosition) ++ "\n\
  23. \ interceptor bearing = " ++ (show minInterceptorBearing) ++ "\n\
  24. \ interceptor speed = " ++ (show minInterceptorSpeed) ++ "\n")
  25. let interceptSpeed = Speed.knots 700
  26. let intercept = Kinematics.interceptBySpeed track interceptor interceptSpeed
  27. let timeToIntercept = fmap Kinematics.timeToIntercept intercept
  28. -- Just 0H46M4.692S
  29. let distanceToIntercept = fmap Kinematics.distanceToIntercept intercept
  30. -- Just 995.596069189km
  31. let interceptPosition = fmap Kinematics.interceptPosition intercept
  32. -- Just 27°59'36.764"N,55°34'43.852"W(S84)
  33. let interceptorBearing = fmap Kinematics.interceptorBearing intercept
  34. -- Just 25°56'7.484"
  35. putStrLn ("Kinematics, Example 15: Time required to intercept target\n\
  36. \ time to intercept = " ++ (show timeToIntercept) ++ "\n\
  37. \ distance to intercept = " ++ (show distanceToIntercept) ++ "\n\
  38. \ intercept position = " ++ (show interceptPosition) ++ "\n\
  39. \ interceptor bearing = " ++ (show interceptorBearing) ++ "\n")

Solutions to coordinates transformation problems

Example 16: Transformation between fixed models

Convert the coordinates of a geocentric position between the WGS84 model and the NAD83 model.

  1. example16 :: IO()
  2. example16 = do
  3. let pWGS84 = Geocentric.metresPos 4193790.895437 454436.195118 4768166.813801 WGS84
  4. -- using explicit parameters:
  5. let tx = Tx.params Txs.from_WGS84_to_NAD83
  6. let pNAD83 = Positions.transform' pWGS84 NAD83 tx
  7. -- Position {gx = 4193.792080781km, gy = 454.433921298km, gz = 4768.16615479km, model = NAD83}
  8. -- using the transformation graph:
  9. let pNAD83' = Positions.transform pWGS84 NAD83 Txs.fixed
  10. -- Just (Position {gx = 4193.792080781km, gy = 454.433921298km, gz = 4768.16615479km, model = NAD83})
  11. putStrLn ("Coordinates transformation, Example 16: WGS84 -> NAD83\n\
  12. \ WGS84 = " ++ (show pWGS84) ++ "\n\
  13. \ NAD83 = " ++ (show pNAD83) ++ "\n\
  14. \ NAD83 = " ++ (show pNAD83') ++ "\n")

Example 17: Transformation between time dependent models

Convert the coordinates of a geocentric position between the ITRF2014, ITRF2000 and NAD83 (CORS96) models

  1. example17 :: IO()
  2. example17 = do
  3. let pITRF2014 = Geocentric.metresPos 4193790.895437 454436.195118 4768166.813801 ITRF2014
  4. -- using explicit parameters:
  5. let tx = Tx.params Txs.from_ITRF2014_to_ITRF2000
  6. let pITRF2000 = Positions.transformAt' pITRF2014 (Epoch 2014.0) ITRF2000 tx
  7. -- Position {gx = 4193.790907273km, gy = 454.436197881km, gz = 4768.166792308km, model = ITRF2000}
  8. -- using the transformation graph (goes via ITRF2000):
  9. let pNAD83 = Positions.transformAt pITRF2014 (Epoch 2014.0) NAD83_CORS96 Txs.timeDependent
  10. -- Just (Position {gx = 4193.791801305km, gy = 454.433876376km, gz = 4768.166397281km, model = NAD83_CORS96})
  11. putStrLn ("Coordinates transformation, Example 17: ITRF2014 -> ITRF2000 -> NAD83 (CORS96)\n\
  12. \ ITRF2014 = " ++ (show pITRF2014) ++ "\n\
  13. \ ITRF2000 = " ++ (show pITRF2000) ++ "\n\
  14. \ NAD83 (CORS96) = " ++ (show pNAD83) ++ "\n")

Solutions to polygon triangulation

Example 18: Triangulation of a simple polygon

  1. example18 :: IO()
  2. example18 = do
  3. let p = Polygon.simple
  4. [ Geodetic.s84Pos 45 45
  5. , Geodetic.s84Pos (-45) 45
  6. , Geodetic.s84Pos (-45) (-45)
  7. , Geodetic.s84Pos 45 (-45)
  8. ]
  9. let ts = fmap Polygon.triangulate p
  10. -- Right [ Triangle 45°0'0.000"N,45°0'0.000"W (S84) 45°0'0.000"N,45°0'0.000"E (S84) 45°0'0.000"S,45°0'0.000"E (S84)
  11. -- , Triangle 45°0'0.000"S,45°0'0.000"E (S84) 45°0'0.000"S,45°0'0.000"W (S84) 45°0'0.000"N,45°0'0.000"W (S84)
  12. -- ]
  13. putStrLn ("Polygon triangulation, Example 18: Simple polygon\n\
  14. \ triangles = " ++ (show ts) ++ "\n")

Example 19: Triangulation of a circle

  1. example19 :: IO()
  2. example19 = do
  3. let p = Polygon.circle (Geodetic.s84Pos 0 0) (Length.kilometres 10) 10
  4. let ts = fmap Polygon.triangulate p
  5. -- Right [ Triangle 0°4'21.923"N,0°3'10.298"W (S84) 0°5'23.755"N,0°0'0.000"E (S84) 0°4'21.923"N,0°3'10.298"E (S84)
  6. -- , Triangle 0°4'21.923"N,0°3'10.298"W (S84) 0°4'21.923"N,0°3'10.298"E (S84) 0°1'40.045"N,0°5'7.909"E (S84)
  7. -- , Triangle 0°4'21.923"N,0°3'10.298"W (S84) 0°1'40.045"N,0°5'7.909"E (S84) 0°1'40.045"S,0°5'7.909"E (S84)
  8. -- , Triangle 0°4'21.923"N,0°3'10.298"W (S84) 0°1'40.045"S,0°5'7.909"E (S84) 0°4'21.923"S,0°3'10.298"E (S84)
  9. -- , Triangle 0°4'21.923"N,0°3'10.298"W (S84) 0°4'21.923"S,0°3'10.298"E (S84) 0°5'23.755"S,0°0'0.000"E (S84)
  10. -- , Triangle 0°4'21.923"N,0°3'10.298"W (S84) 0°5'23.755"S,0°0'0.000"E (S84) 0°4'21.923"S,0°3'10.298"W (S84)
  11. -- , Triangle 0°4'21.923"N,0°3'10.298"W (S84) 0°4'21.923"S,0°3'10.298"W (S84) 0°1'40.045"S,0°5'7.909"W (S84)
  12. -- , Triangle 0°1'40.045"S,0°5'7.909"W (S84) 0°1'40.045"N,0°5'7.909"W (S84) 0°4'21.923"N,0°3'10.298"W (S84)
  13. -- ]
  14. putStrLn ("Polygon triangulation, Example 19: Circle\n\
  15. \ triangles = " ++ (show ts) ++ "\n")

Example 20: Triangulation of an arc

  1. example20 :: IO()
  2. example20 = do
  3. let p = Polygon.arc (Geodetic.s84Pos 0 0) (Length.kilometres 10) (Angle.decimalDegrees 10) (Angle.decimalDegrees 20) 5
  4. let ts = fmap Polygon.triangulate p
  5. -- Right [ Triangle 0°5'4.230"N,0°1'50.730"E (S84) 0°5'18.836"N,0°0'56.219"E (S84) 0°5'16.081"N,0°1'10.073"E (S84)
  6. -- , Triangle 0°5'4.230"N,0°1'50.730"E (S84) 0°5'16.081"N,0°1'10.073"E (S84) 0°5'12.723"N,0°1'23.794"E (S84)
  7. -- , Triangle 0°5'12.723"N,0°1'23.794"E (S84) 0°5'8.770"N,0°1'37.355"E (S84) 0°5'4.230"N,0°1'50.730"E (S84)
  8. -- ]
  9. putStrLn ("Polygon triangulation, Example 20: Arc\n\
  10. \ triangles = " ++ (show ts) ++ "\n")
  1. main :: IO()
  2. main = do
  3. example1
  4. example2
  5. example3
  6. example4
  7. example5
  8. example6
  9. example7
  10. example8
  11. example9
  12. example10
  13. example11
  14. example12
  15. example13
  16. example14
  17. example15
  18. example16
  19. example17
  20. example18
  21. example19
  22. example20