diff --git a/coordinate.cabal b/coordinate.cabal
--- a/coordinate.cabal
+++ b/coordinate.cabal
@@ -1,5 +1,5 @@
 name:               coordinate
-version:            0.0.3
+version:            0.0.4
 license:            BSD3
 license-File:       etc/LICENCE
 author:             Tony Morris <ʇǝu˙sıɹɹoɯʇ@ןןǝʞsɐɥ>
@@ -38,14 +38,14 @@
                     src
 
   exposed-modules:
-                    Data.GPS.Coordinate
-                    Data.GPS.Coordinate.Coordinate
-                    Data.GPS.Coordinate.DegreesLatitude
-                    Data.GPS.Coordinate.DegreesLongitude
-                    Data.GPS.Coordinate.Latitude
-                    Data.GPS.Coordinate.Longitude
-                    Data.GPS.Coordinate.Minutes
-                    Data.GPS.Coordinate.Seconds
+                    Data.Geo.Coordinate
+                    Data.Geo.Coordinate.Coordinate
+                    Data.Geo.Coordinate.DegreesLatitude
+                    Data.Geo.Coordinate.DegreesLongitude
+                    Data.Geo.Coordinate.Latitude
+                    Data.Geo.Coordinate.Longitude
+                    Data.Geo.Coordinate.Minutes
+                    Data.Geo.Coordinate.Seconds
 
 test-suite doctests
   type:
diff --git a/src/Data/GPS/Coordinate.hs b/src/Data/GPS/Coordinate.hs
deleted file mode 100644
--- a/src/Data/GPS/Coordinate.hs
+++ /dev/null
@@ -1,9 +0,0 @@
-module Data.GPS.Coordinate(module C) where
-
-import Data.GPS.Coordinate.Coordinate as C
-import Data.GPS.Coordinate.DegreesLatitude as C
-import Data.GPS.Coordinate.DegreesLongitude as C
-import Data.GPS.Coordinate.Latitude as C
-import Data.GPS.Coordinate.Longitude as C
-import Data.GPS.Coordinate.Minutes as C
-import Data.GPS.Coordinate.Seconds as C
diff --git a/src/Data/GPS/Coordinate/Coordinate.hs b/src/Data/GPS/Coordinate/Coordinate.hs
deleted file mode 100644
--- a/src/Data/GPS/Coordinate/Coordinate.hs
+++ /dev/null
@@ -1,74 +0,0 @@
-module Data.GPS.Coordinate.Coordinate(
-  Coordinate
-, HasCoordinate(..)
-, coordinateLatLon
-, coordinateLonLat
-, coordinateDMSLatLon
-, coordinateLatDMSLon
-, coordinateDMSLatDMSLon
-) where
-
-import Prelude(Eq, Show, Ord, id, (.))
-import Control.Lens(Iso', Lens', iso, lens, mapping, swapped, withIso)
-import Data.GPS.Coordinate.Latitude
-import Data.GPS.Coordinate.Longitude
-import Data.GPS.Coordinate.DegreesLatitude
-import Data.GPS.Coordinate.DegreesLongitude
-import Data.GPS.Coordinate.Minutes
-import Data.GPS.Coordinate.Seconds
-
-data Coordinate =
-  Coordinate
-    Latitude
-    Longitude
-  deriving (Eq, Ord, Show)
-
-coordinateLatLon ::
-  Iso' (Latitude, Longitude) Coordinate
-coordinateLatLon =
-  iso (\(lat, lon) -> Coordinate lat lon) (\(Coordinate lat lon) -> (lat, lon))
-
-coordinateLonLat ::
-  Iso' (Longitude, Latitude) Coordinate
-coordinateLonLat =
-  swapped . coordinateLatLon
-
-coordinateDMSLatLon ::
-  Iso' ((DegreesLatitude, Minutes, Seconds), Longitude) Coordinate
-coordinateDMSLatLon =
-  swapped . mapping dmsLatitude . coordinateLonLat
-
-coordinateLatDMSLon ::
-  Iso' (Latitude, (DegreesLongitude, Minutes, Seconds)) Coordinate
-coordinateLatDMSLon =
-  mapping dmsLongitude . coordinateLatLon
-
-coordinateDMSLatDMSLon ::
-  Iso' ((DegreesLatitude, Minutes, Seconds), (DegreesLongitude, Minutes, Seconds)) Coordinate
-coordinateDMSLatDMSLon =
-  iso (\((td, tm, ts), (nd, nm, ns)) -> Coordinate (withIso dmsLatitude (\k _ -> k (td, tm, ts))) (withIso dmsLongitude (\k _ -> k (nd, nm, ns))))
-      (\(Coordinate lat lon) -> (withIso dmsLatitude (\_ k -> k lat), withIso dmsLongitude (\_ k -> k lon)))
-
-class HasCoordinate t where
-  coordinate ::
-    Lens' t Coordinate
-
-instance HasCoordinate Coordinate where
-  coordinate =
-    id
-
-instance HasLatitude Coordinate where
-  latitude =
-    lens (\(Coordinate lat _) -> lat) (\(Coordinate _ lon) lat -> Coordinate lat lon)
-
-instance HasLongitude Coordinate where
-  longitude =
-    lens (\(Coordinate _ lon) -> lon) (\(Coordinate lat _) lon -> Coordinate lat lon)
-
-instance HasDegreesLatitude Coordinate where
-  degreesLatitude =
-    latitude . degreesLatitude
-
-instance HasDegreesLongitude Coordinate where
-  degreesLongitude =
-    longitude . degreesLongitude
diff --git a/src/Data/GPS/Coordinate/DegreesLatitude.hs b/src/Data/GPS/Coordinate/DegreesLatitude.hs
deleted file mode 100644
--- a/src/Data/GPS/Coordinate/DegreesLatitude.hs
+++ /dev/null
@@ -1,57 +0,0 @@
-module Data.GPS.Coordinate.DegreesLatitude(
-  DegreesLatitude
-, HasDegreesLatitude(..)
-, nDegreesLatitude
-) where
-
-import Prelude(Int, Bool(..), Eq, Show, Ord(..), id, (&&))
-import Data.Maybe(Maybe(..))
-import Control.Lens(Prism', Lens', prism')
-
--- $setup
--- >>> import Control.Lens((#), (^?))
--- >>> import Data.Foldable(all)
--- >>> import Prelude(Eq(..))
-
-newtype DegreesLatitude =
-  DegreesLatitude Int
-  deriving (Eq, Ord, Show)
-
--- | A prism on degrees latitude to an integer between -90 and 90 exclusive.
---
--- >>> 7 ^? nDegreesLatitude
--- Just (DegreesLatitude 7)
---
--- >>> 0 ^? nDegreesLatitude
--- Just (DegreesLatitude 0)
---
--- >>> 89 ^? nDegreesLatitude
--- Just (DegreesLatitude 89)
---
--- >>> 90 ^? nDegreesLatitude
--- Nothing
---
--- >>> (-89) ^? nDegreesLatitude
--- Just (DegreesLatitude (-89))
---
--- >>> (-90) ^? nDegreesLatitude
--- Nothing
---
--- prop> all (\m -> nDegreesLatitude # m == n) (n ^? nDegreesLatitude)
-nDegreesLatitude ::
-  Prism' Int DegreesLatitude
-nDegreesLatitude =
-  prism'
-    (\(DegreesLatitude i) -> i)
-    (\i -> case i > -90 && i < 90 of
-             True -> Just (DegreesLatitude i)
-             False -> Nothing)
-
-class HasDegreesLatitude t where
-  degreesLatitude ::
-    Lens' t DegreesLatitude
-
-instance HasDegreesLatitude DegreesLatitude where
-  degreesLatitude =
-    id
-
diff --git a/src/Data/GPS/Coordinate/DegreesLongitude.hs b/src/Data/GPS/Coordinate/DegreesLongitude.hs
deleted file mode 100644
--- a/src/Data/GPS/Coordinate/DegreesLongitude.hs
+++ /dev/null
@@ -1,56 +0,0 @@
-module Data.GPS.Coordinate.DegreesLongitude(
-  DegreesLongitude
-, HasDegreesLongitude(..)
-, nDegreesLongitude
-) where
-
-import Prelude(Int, Bool(..), Eq, Show, Ord(..), id, (&&))
-import Data.Maybe(Maybe(..))
-import Control.Lens(Prism', Lens', prism')
-
--- $setup
--- >>> import Control.Lens((#), (^?))
--- >>> import Data.Foldable(all)
--- >>> import Prelude(Eq(..))
-
-newtype DegreesLongitude =
-  DegreesLongitude Int
-  deriving (Eq, Ord, Show)
-
--- | A prism on degrees longitude to an integer between -180 and 180 exclusive.
---
--- >>> 7 ^? nDegreesLongitude
--- Just (DegreesLongitude 7)
---
--- >>> 0 ^? nDegreesLongitude
--- Just (DegreesLongitude 0)
---
--- >>> 179 ^? nDegreesLongitude
--- Just (DegreesLongitude 179)
---
--- >>> 180 ^? nDegreesLongitude
--- Nothing
---
--- >>> (-179) ^? nDegreesLongitude
--- Just (DegreesLongitude (-179))
---
--- >>> (-180) ^? nDegreesLongitude
--- Nothing
---
--- prop> all (\m -> nDegreesLongitude # m == n) (n ^? nDegreesLongitude)
-nDegreesLongitude ::
-  Prism' Int DegreesLongitude
-nDegreesLongitude  =
-  prism'
-    (\(DegreesLongitude i) -> i)
-    (\i -> case i > -180 && i < 180 of
-             True -> Just (DegreesLongitude i)
-             False -> Nothing)
-
-class HasDegreesLongitude t where
-  degreesLongitude ::
-    Lens' t DegreesLongitude
-
-instance HasDegreesLongitude DegreesLongitude where
-  degreesLongitude =
-    id
diff --git a/src/Data/GPS/Coordinate/Latitude.hs b/src/Data/GPS/Coordinate/Latitude.hs
deleted file mode 100644
--- a/src/Data/GPS/Coordinate/Latitude.hs
+++ /dev/null
@@ -1,109 +0,0 @@
-module Data.GPS.Coordinate.Latitude(
-  Latitude
-, HasLatitude(..)
-, dmsLatitude
-, fracLatitude
-) where
-
-import Prelude(Double, Eq, Show, Ord(..), Num(..), Bool(..), Monad(..), id, (&&), properFraction, fromIntegral)
-import Control.Lens(Iso', Prism', Lens', iso, prism', lens, (#), (^?))
-import Data.GPS.Coordinate.DegreesLatitude
-import Data.GPS.Coordinate.Minutes
-import Data.GPS.Coordinate.Seconds
-
--- $setup
--- >>> import Control.Lens((^?))
-
-data Latitude =
-  Latitude
-    DegreesLatitude
-    Minutes
-    Seconds
-  deriving (Eq, Ord, Show)
-
--- | An isomorphism on the triple of degrees latitude, minutes, seconds to a latitude.
---
--- >>> do deg <- 7 ^? nDegreesLatitude; min <- 7 ^? nMinutes; sec <- 7 ^? nSeconds; (deg, min, sec) ^? dmsLatitude
--- Just (Latitude (DegreesLatitude 7) (Minutes 7) (Seconds 7.0000))
---
--- >>> do deg <- 89 ^? nDegreesLatitude; min <- 59 ^? nMinutes; sec <- 59 ^? nSeconds; (deg, min, sec) ^? dmsLatitude
--- Just (Latitude (DegreesLatitude 89) (Minutes 59) (Seconds 59.0000))
---
--- >>> do deg <- (-7) ^? nDegreesLatitude; min <- 7 ^? nMinutes; sec <- 7 ^? nSeconds; (deg, min, sec) ^? dmsLatitude
--- Just (Latitude (DegreesLatitude (-7)) (Minutes 7) (Seconds 7.0000))
---
--- >>> do deg <- (-89) ^? nDegreesLatitude; min <- 59 ^? nMinutes; sec <- 59 ^? nSeconds; (deg, min, sec) ^? dmsLatitude
--- Just (Latitude (DegreesLatitude (-89)) (Minutes 59) (Seconds 59.0000))
---
--- >>> do deg <- 90 ^? nDegreesLatitude; min <- 59 ^? nMinutes; sec <- 59 ^? nSeconds; (deg, min, sec) ^? dmsLatitude
--- Nothing
---
--- >>> do deg <- 89 ^? nDegreesLatitude; min <- 60 ^? nMinutes; sec <- 59 ^? nSeconds; (deg, min, sec) ^? dmsLatitude
--- Nothing
---
--- >>> do deg <- 89 ^? nDegreesLatitude; min <- 59 ^? nMinutes; sec <- 60 ^? nSeconds; (deg, min, sec) ^? dmsLatitude
--- Nothing
-dmsLatitude ::
-  Iso' (DegreesLatitude, Minutes, Seconds) Latitude
-dmsLatitude =
-  iso (\(d, m, s) -> Latitude d m s) (\(Latitude d m s) -> (d, m, s))
-
--- | A prism on latitude to a double between -90 and 90 inclusive.
---
--- >>> 7 ^? fracLatitude
--- Just (Latitude (DegreesLatitude 7) (Minutes 0) (Seconds 0.0000))
---
--- >>> (-7) ^? fracLatitude
--- Just (Latitude (DegreesLatitude (-7)) (Minutes 0) (Seconds 0.0000))
---
--- >>> 7.12 ^? fracLatitude
--- Just (Latitude (DegreesLatitude 7) (Minutes 7) (Seconds 12.0000))
---
--- >>> (-7.12) ^? fracLatitude
--- Just (Latitude (DegreesLatitude (-7)) (Minutes 7) (Seconds 12.0000))
---
--- >>> 90 ^? fracLatitude
--- Nothing
---
--- >>> (-90) ^? fracLatitude
--- Nothing
---
--- >>> 15.63791 ^? fracLatitude
--- Just (Latitude (DegreesLatitude 15) (Minutes 38) (Seconds 16.4760))
---
--- >>> 89.1 ^? fracLatitude
--- Just (Latitude (DegreesLatitude 89) (Minutes 5) (Seconds 60.0000))
---
--- >>> 89.2 ^? fracLatitude
--- Just (Latitude (DegreesLatitude 89) (Minutes 12) (Seconds 0.0000))
-fracLatitude ::
-  Prism' Double Latitude
-fracLatitude =
-  prism' (\(Latitude d m s) ->
-    fromIntegral (nDegreesLatitude # d) + nSeconds # s + fromIntegral (nMinutes # m * 60))
-    (\x -> let (d, z) = properFraction x
-               (m, s) = properFraction ((z :: Double) * 60)
-           in do d' <- d ^? nDegreesLatitude
-                 m' <- abs m ^? nMinutes
-                 s' <- (abs s * 60) ^? nSeconds
-                 return (Latitude d' m' s'))
-
-class HasLatitude t where
-  latitude ::
-    Lens' t Latitude
-
-instance HasLatitude Latitude where
-  latitude =
-    id
-
-instance HasDegreesLatitude Latitude where
-  degreesLatitude =
-    lens (\(Latitude d _ _) -> d) (\(Latitude _ m s) d -> Latitude d m s)
-
-instance HasMinutes Latitude where
-  minutes =
-    lens (\(Latitude _ m _) -> m) (\(Latitude d _ s) m -> Latitude d m s)
-
-instance HasSeconds Latitude where
-  seconds =
-    lens (\(Latitude _ _ s) -> s) (\(Latitude d m _) s -> Latitude d m s)
diff --git a/src/Data/GPS/Coordinate/Longitude.hs b/src/Data/GPS/Coordinate/Longitude.hs
deleted file mode 100644
--- a/src/Data/GPS/Coordinate/Longitude.hs
+++ /dev/null
@@ -1,106 +0,0 @@
-module Data.GPS.Coordinate.Longitude(
-  Longitude
-, HasLongitude(..)
-, dmsLongitude
-, fracLongitude
-) where
-
-import Prelude(Double, Eq, Show, Ord(..), Num(..), Bool(..), Monad(..), id, (&&), properFraction, fromIntegral)
-import Control.Lens(Iso', Prism', Lens', iso, prism', lens, (#), (^?))
-import Data.GPS.Coordinate.DegreesLongitude
-import Data.GPS.Coordinate.Minutes
-import Data.GPS.Coordinate.Seconds
-
-data Longitude =
-  Longitude
-    DegreesLongitude
-    Minutes
-    Seconds
-  deriving (Eq, Ord, Show)
-
--- | An isomorphism on the triple of degrees longitude, minutes, seconds to a longitude.
---
--- >>> do deg <- 7 ^? nDegreesLongitude; min <- 7 ^? nMinutes; sec <- 7 ^? nSeconds; (deg, min, sec) ^? dmsLongitude
--- Just (Longitude (DegreesLongitude 7) (Minutes 7) (Seconds 7.0000))
---
--- >>> do deg <- 179 ^? nDegreesLongitude; min <- 59 ^? nMinutes; sec <- 59 ^? nSeconds; (deg, min, sec) ^? dmsLongitude
--- Just (Longitude (DegreesLongitude 179) (Minutes 59) (Seconds 59.0000))
---
--- >>> do deg <- (-7) ^? nDegreesLongitude; min <- 7 ^? nMinutes; sec <- 7 ^? nSeconds; (deg, min, sec) ^? dmsLongitude
--- Just (Longitude (DegreesLongitude (-7)) (Minutes 7) (Seconds 7.0000))
---
--- >>> do deg <- (-179) ^? nDegreesLongitude; min <- 59 ^? nMinutes; sec <- 59 ^? nSeconds; (deg, min, sec) ^? dmsLongitude
--- Just (Longitude (DegreesLongitude (-179)) (Minutes 59) (Seconds 59.0000))
---
--- >>> do deg <- 180 ^? nDegreesLongitude; min <- 59 ^? nMinutes; sec <- 59 ^? nSeconds; (deg, min, sec) ^? dmsLongitude
--- Nothing
---
--- >>> do deg <- 179 ^? nDegreesLongitude; min <- 60 ^? nMinutes; sec <- 59 ^? nSeconds; (deg, min, sec) ^? dmsLongitude
--- Nothing
---
--- >>> do deg <- 179 ^? nDegreesLongitude; min <- 59 ^? nMinutes; sec <- 60 ^? nSeconds; (deg, min, sec) ^? dmsLongitude
--- Nothing
-dmsLongitude ::
-  Iso' (DegreesLongitude, Minutes, Seconds) Longitude
-dmsLongitude =
-  iso (\(d, m, s) -> Longitude d m s) (\(Longitude d m s) -> (d, m, s))
-
--- | A prism on longitude to a double between -180 and 180 inclusive.
---
--- >>> 7 ^? fracLongitude
--- Just (Longitude (DegreesLongitude 7) (Minutes 0) (Seconds 0.0000))
---
--- >>> (-7) ^? fracLongitude
--- Just (Longitude (DegreesLongitude (-7)) (Minutes 0) (Seconds 0.0000))
---
--- >>> 7.12 ^? fracLongitude
--- Just (Longitude (DegreesLongitude 7) (Minutes 7) (Seconds 12.0000))
---
--- >>> (-7.12) ^? fracLongitude
--- Just (Longitude (DegreesLongitude (-7)) (Minutes 7) (Seconds 12.0000))
---
--- >>> 180 ^? fracLongitude
--- Nothing
---
--- >>> (-180) ^? fracLongitude
--- Nothing
---
--- >>> 15.63791 ^? fracLongitude
--- Just (Longitude (DegreesLongitude 15) (Minutes 38) (Seconds 16.4760))
---
--- >>> 179.1 ^? fracLongitude
--- Just (Longitude (DegreesLongitude 179) (Minutes 5) (Seconds 60.0000))
---
--- >>> 179.2 ^? fracLongitude
--- Just (Longitude (DegreesLongitude 179) (Minutes 11) (Seconds 60.0000))
-fracLongitude ::
-  Prism' Double Longitude
-fracLongitude =
-  prism' (\(Longitude d m s) ->
-    fromIntegral (nDegreesLongitude  # d) + nSeconds # s + fromIntegral (nMinutes # m * 60))
-    (\x -> let (d, z) = properFraction x
-               (m, s) = properFraction ((z :: Double) * 60)
-           in do d' <- d ^? nDegreesLongitude
-                 m' <- abs m ^? nMinutes
-                 s' <- (abs s * 60) ^? nSeconds
-                 return (Longitude d' m' s'))
-
-class HasLongitude t where
-  longitude ::
-    Lens' t Longitude
-
-instance HasLongitude Longitude where
-  longitude =
-    id
-
-instance HasDegreesLongitude Longitude where
-  degreesLongitude =
-    lens (\(Longitude d _ _) -> d) (\(Longitude _ m s) d -> Longitude d m s)
-
-instance HasMinutes Longitude where
-  minutes =
-    lens (\(Longitude _ m _) -> m) (\(Longitude d _ s) m -> Longitude d m s)
-
-instance HasSeconds Longitude where
-  seconds =
-    lens (\(Longitude _ _ s) -> s) (\(Longitude d m _) s -> Longitude d m s)
diff --git a/src/Data/GPS/Coordinate/Minutes.hs b/src/Data/GPS/Coordinate/Minutes.hs
deleted file mode 100644
--- a/src/Data/GPS/Coordinate/Minutes.hs
+++ /dev/null
@@ -1,50 +0,0 @@
-module Data.GPS.Coordinate.Minutes(
-  Minutes
-, HasMinutes(..)
-, nMinutes
-) where
-
-import Prelude(Functor, Int, Bool(..), Eq, Show, Ord(..), (&&), id)
-import Data.Maybe(Maybe(..))
-import Control.Lens(Prism', Lens', prism')
-
--- $setup
--- >>> import Control.Lens((#), (^?))
--- >>> import Data.Foldable(all)
--- >>> import Prelude(Eq(..))
-
-newtype Minutes =
-  Minutes Int
-  deriving (Eq, Ord, Show)
-
--- | A prism on minutes to an integer between 0 and 59 inclusive.
---
--- >>> 7 ^? nMinutes
--- Just (Minutes 7)
---
--- >>> 0 ^? nMinutes
--- Just (Minutes 0)
---
--- >>> 59 ^? nMinutes
--- Just (Minutes 59)
---
--- >>> 60 ^? nMinutes
--- Nothing
---
--- prop> all (\m -> nMinutes # m == n) (n ^? nMinutes)
-nMinutes ::
-  Prism' Int Minutes
-nMinutes =
-  prism'
-    (\(Minutes i) -> i)
-    (\i -> case i >= 0 && i < 60 of
-             True -> Just (Minutes i)
-             False -> Nothing)
-
-class HasMinutes t where
-  minutes ::
-    Lens' t Minutes
-
-instance HasMinutes Minutes where
-  minutes =
-    id
diff --git a/src/Data/GPS/Coordinate/Seconds.hs b/src/Data/GPS/Coordinate/Seconds.hs
deleted file mode 100644
--- a/src/Data/GPS/Coordinate/Seconds.hs
+++ /dev/null
@@ -1,60 +0,0 @@
-module Data.GPS.Coordinate.Seconds(
-  Seconds
-, HasSeconds(..)
-, nSeconds
-) where
-
-import Prelude(Double, Bool(..), Eq, Show(..), Ord(..), id, (&&), (++), showParen, showString)
-import Data.Maybe(Maybe(..))
-import Control.Lens(Prism', Lens', prism')
-import Text.Printf(printf)
-
--- $setup
--- >>> import Control.Lens((#), (^?))
--- >>> import Data.Foldable(all)
--- >>> import Prelude(Eq(..))
-
-newtype Seconds =
-  Seconds Double
-  deriving (Eq, Ord)
-
--- | A show instance that prints to 4 decimal places.
--- This is to take floating-point rounding errors into account.
-instance Show Seconds where
-  showsPrec n (Seconds d) =
-    showParen (n > 10) (showString ("Seconds " ++ printf "%0.4f" d))
-
--- | A prism on seconds to a double between 0 inclusive and 60 exclusive.
---
--- >>> 7 ^? nSeconds
--- Just (Seconds 7.0000)
---
--- >>> 0 ^? nSeconds
--- Just (Seconds 0.0000)
---
--- >>> 59 ^? nSeconds
--- Just (Seconds 59.0000)
---
--- >>> 59.99 ^? nSeconds
--- Just (Seconds 59.9900)
---
--- >>> 60 ^? nSeconds
--- Nothing
---
--- prop> all (\m -> nSeconds # m == n) (n ^? nSeconds)
-nSeconds ::
-  Prism' Double Seconds
-nSeconds =
-  prism'
-    (\(Seconds d) -> d)
-    (\d -> case d >= 0 && d < 60 of
-             True -> Just (Seconds d)
-             False -> Nothing)
-
-class HasSeconds t where
-  seconds ::
-    Lens' t Seconds
-
-instance HasSeconds Seconds where
-  seconds =
-    id
diff --git a/src/Data/Geo/Coordinate.hs b/src/Data/Geo/Coordinate.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Geo/Coordinate.hs
@@ -0,0 +1,9 @@
+module Data.Geo.Coordinate(module C) where
+
+import Data.Geo.Coordinate.Coordinate as C
+import Data.Geo.Coordinate.DegreesLatitude as C
+import Data.Geo.Coordinate.DegreesLongitude as C
+import Data.Geo.Coordinate.Latitude as C
+import Data.Geo.Coordinate.Longitude as C
+import Data.Geo.Coordinate.Minutes as C
+import Data.Geo.Coordinate.Seconds as C
diff --git a/src/Data/Geo/Coordinate/Coordinate.hs b/src/Data/Geo/Coordinate/Coordinate.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Geo/Coordinate/Coordinate.hs
@@ -0,0 +1,74 @@
+module Data.Geo.Coordinate.Coordinate(
+  Coordinate
+, HasCoordinate(..)
+, coordinateLatLon
+, coordinateLonLat
+, coordinateDMSLatLon
+, coordinateLatDMSLon
+, coordinateDMSLatDMSLon
+) where
+
+import Prelude(Eq, Show, Ord, id, (.))
+import Control.Lens(Iso', Lens', iso, lens, mapping, swapped, withIso)
+import Data.Geo.Coordinate.Latitude
+import Data.Geo.Coordinate.Longitude
+import Data.Geo.Coordinate.DegreesLatitude
+import Data.Geo.Coordinate.DegreesLongitude
+import Data.Geo.Coordinate.Minutes
+import Data.Geo.Coordinate.Seconds
+
+data Coordinate =
+  Coordinate
+    Latitude
+    Longitude
+  deriving (Eq, Ord, Show)
+
+coordinateLatLon ::
+  Iso' (Latitude, Longitude) Coordinate
+coordinateLatLon =
+  iso (\(lat, lon) -> Coordinate lat lon) (\(Coordinate lat lon) -> (lat, lon))
+
+coordinateLonLat ::
+  Iso' (Longitude, Latitude) Coordinate
+coordinateLonLat =
+  swapped . coordinateLatLon
+
+coordinateDMSLatLon ::
+  Iso' ((DegreesLatitude, Minutes, Seconds), Longitude) Coordinate
+coordinateDMSLatLon =
+  swapped . mapping dmsLatitude . coordinateLonLat
+
+coordinateLatDMSLon ::
+  Iso' (Latitude, (DegreesLongitude, Minutes, Seconds)) Coordinate
+coordinateLatDMSLon =
+  mapping dmsLongitude . coordinateLatLon
+
+coordinateDMSLatDMSLon ::
+  Iso' ((DegreesLatitude, Minutes, Seconds), (DegreesLongitude, Minutes, Seconds)) Coordinate
+coordinateDMSLatDMSLon =
+  iso (\((td, tm, ts), (nd, nm, ns)) -> Coordinate (withIso dmsLatitude (\k _ -> k (td, tm, ts))) (withIso dmsLongitude (\k _ -> k (nd, nm, ns))))
+      (\(Coordinate lat lon) -> (withIso dmsLatitude (\_ k -> k lat), withIso dmsLongitude (\_ k -> k lon)))
+
+class HasCoordinate t where
+  coordinate ::
+    Lens' t Coordinate
+
+instance HasCoordinate Coordinate where
+  coordinate =
+    id
+
+instance HasLatitude Coordinate where
+  latitude =
+    lens (\(Coordinate lat _) -> lat) (\(Coordinate _ lon) lat -> Coordinate lat lon)
+
+instance HasLongitude Coordinate where
+  longitude =
+    lens (\(Coordinate _ lon) -> lon) (\(Coordinate lat _) lon -> Coordinate lat lon)
+
+instance HasDegreesLatitude Coordinate where
+  degreesLatitude =
+    latitude . degreesLatitude
+
+instance HasDegreesLongitude Coordinate where
+  degreesLongitude =
+    longitude . degreesLongitude
diff --git a/src/Data/Geo/Coordinate/DegreesLatitude.hs b/src/Data/Geo/Coordinate/DegreesLatitude.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Geo/Coordinate/DegreesLatitude.hs
@@ -0,0 +1,57 @@
+module Data.Geo.Coordinate.DegreesLatitude(
+  DegreesLatitude
+, HasDegreesLatitude(..)
+, nDegreesLatitude
+) where
+
+import Prelude(Int, Bool(..), Eq, Show, Ord(..), id, (&&))
+import Data.Maybe(Maybe(..))
+import Control.Lens(Prism', Lens', prism')
+
+-- $setup
+-- >>> import Control.Lens((#), (^?))
+-- >>> import Data.Foldable(all)
+-- >>> import Prelude(Eq(..))
+
+newtype DegreesLatitude =
+  DegreesLatitude Int
+  deriving (Eq, Ord, Show)
+
+-- | A prism on degrees latitude to an integer between -90 and 90 exclusive.
+--
+-- >>> 7 ^? nDegreesLatitude
+-- Just (DegreesLatitude 7)
+--
+-- >>> 0 ^? nDegreesLatitude
+-- Just (DegreesLatitude 0)
+--
+-- >>> 89 ^? nDegreesLatitude
+-- Just (DegreesLatitude 89)
+--
+-- >>> 90 ^? nDegreesLatitude
+-- Nothing
+--
+-- >>> (-89) ^? nDegreesLatitude
+-- Just (DegreesLatitude (-89))
+--
+-- >>> (-90) ^? nDegreesLatitude
+-- Nothing
+--
+-- prop> all (\m -> nDegreesLatitude # m == n) (n ^? nDegreesLatitude)
+nDegreesLatitude ::
+  Prism' Int DegreesLatitude
+nDegreesLatitude =
+  prism'
+    (\(DegreesLatitude i) -> i)
+    (\i -> case i > -90 && i < 90 of
+             True -> Just (DegreesLatitude i)
+             False -> Nothing)
+
+class HasDegreesLatitude t where
+  degreesLatitude ::
+    Lens' t DegreesLatitude
+
+instance HasDegreesLatitude DegreesLatitude where
+  degreesLatitude =
+    id
+
diff --git a/src/Data/Geo/Coordinate/DegreesLongitude.hs b/src/Data/Geo/Coordinate/DegreesLongitude.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Geo/Coordinate/DegreesLongitude.hs
@@ -0,0 +1,56 @@
+module Data.Geo.Coordinate.DegreesLongitude(
+  DegreesLongitude
+, HasDegreesLongitude(..)
+, nDegreesLongitude
+) where
+
+import Prelude(Int, Bool(..), Eq, Show, Ord(..), id, (&&))
+import Data.Maybe(Maybe(..))
+import Control.Lens(Prism', Lens', prism')
+
+-- $setup
+-- >>> import Control.Lens((#), (^?))
+-- >>> import Data.Foldable(all)
+-- >>> import Prelude(Eq(..))
+
+newtype DegreesLongitude =
+  DegreesLongitude Int
+  deriving (Eq, Ord, Show)
+
+-- | A prism on degrees longitude to an integer between -180 and 180 exclusive.
+--
+-- >>> 7 ^? nDegreesLongitude
+-- Just (DegreesLongitude 7)
+--
+-- >>> 0 ^? nDegreesLongitude
+-- Just (DegreesLongitude 0)
+--
+-- >>> 179 ^? nDegreesLongitude
+-- Just (DegreesLongitude 179)
+--
+-- >>> 180 ^? nDegreesLongitude
+-- Nothing
+--
+-- >>> (-179) ^? nDegreesLongitude
+-- Just (DegreesLongitude (-179))
+--
+-- >>> (-180) ^? nDegreesLongitude
+-- Nothing
+--
+-- prop> all (\m -> nDegreesLongitude # m == n) (n ^? nDegreesLongitude)
+nDegreesLongitude ::
+  Prism' Int DegreesLongitude
+nDegreesLongitude  =
+  prism'
+    (\(DegreesLongitude i) -> i)
+    (\i -> case i > -180 && i < 180 of
+             True -> Just (DegreesLongitude i)
+             False -> Nothing)
+
+class HasDegreesLongitude t where
+  degreesLongitude ::
+    Lens' t DegreesLongitude
+
+instance HasDegreesLongitude DegreesLongitude where
+  degreesLongitude =
+    id
diff --git a/src/Data/Geo/Coordinate/Latitude.hs b/src/Data/Geo/Coordinate/Latitude.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Geo/Coordinate/Latitude.hs
@@ -0,0 +1,109 @@
+module Data.Geo.Coordinate.Latitude(
+  Latitude
+, HasLatitude(..)
+, dmsLatitude
+, fracLatitude
+) where
+
+import Prelude(Double, Eq, Show, Ord(..), Num(..), Bool(..), Monad(..), id, (&&), properFraction, fromIntegral)
+import Control.Lens(Iso', Prism', Lens', iso, prism', lens, (#), (^?))
+import Data.Geo.Coordinate.DegreesLatitude
+import Data.Geo.Coordinate.Minutes
+import Data.Geo.Coordinate.Seconds
+
+-- $setup
+-- >>> import Control.Lens((^?))
+
+data Latitude =
+  Latitude
+    DegreesLatitude
+    Minutes
+    Seconds
+  deriving (Eq, Ord, Show)
+
+-- | An isomorphism on the triple of degrees latitude, minutes, seconds to a latitude.
+--
+-- >>> do deg <- 7 ^? nDegreesLatitude; min <- 7 ^? nMinutes; sec <- 7 ^? nSeconds; (deg, min, sec) ^? dmsLatitude
+-- Just (Latitude (DegreesLatitude 7) (Minutes 7) (Seconds 7.0000))
+--
+-- >>> do deg <- 89 ^? nDegreesLatitude; min <- 59 ^? nMinutes; sec <- 59 ^? nSeconds; (deg, min, sec) ^? dmsLatitude
+-- Just (Latitude (DegreesLatitude 89) (Minutes 59) (Seconds 59.0000))
+--
+-- >>> do deg <- (-7) ^? nDegreesLatitude; min <- 7 ^? nMinutes; sec <- 7 ^? nSeconds; (deg, min, sec) ^? dmsLatitude
+-- Just (Latitude (DegreesLatitude (-7)) (Minutes 7) (Seconds 7.0000))
+--
+-- >>> do deg <- (-89) ^? nDegreesLatitude; min <- 59 ^? nMinutes; sec <- 59 ^? nSeconds; (deg, min, sec) ^? dmsLatitude
+-- Just (Latitude (DegreesLatitude (-89)) (Minutes 59) (Seconds 59.0000))
+--
+-- >>> do deg <- 90 ^? nDegreesLatitude; min <- 59 ^? nMinutes; sec <- 59 ^? nSeconds; (deg, min, sec) ^? dmsLatitude
+-- Nothing
+--
+-- >>> do deg <- 89 ^? nDegreesLatitude; min <- 60 ^? nMinutes; sec <- 59 ^? nSeconds; (deg, min, sec) ^? dmsLatitude
+-- Nothing
+--
+-- >>> do deg <- 89 ^? nDegreesLatitude; min <- 59 ^? nMinutes; sec <- 60 ^? nSeconds; (deg, min, sec) ^? dmsLatitude
+-- Nothing
+dmsLatitude ::
+  Iso' (DegreesLatitude, Minutes, Seconds) Latitude
+dmsLatitude =
+  iso (\(d, m, s) -> Latitude d m s) (\(Latitude d m s) -> (d, m, s))
+
+-- | A prism on latitude to a double between -90 and 90 inclusive.
+--
+-- >>> 7 ^? fracLatitude
+-- Just (Latitude (DegreesLatitude 7) (Minutes 0) (Seconds 0.0000))
+--
+-- >>> (-7) ^? fracLatitude
+-- Just (Latitude (DegreesLatitude (-7)) (Minutes 0) (Seconds 0.0000))
+--
+-- >>> 7.12 ^? fracLatitude
+-- Just (Latitude (DegreesLatitude 7) (Minutes 7) (Seconds 12.0000))
+--
+-- >>> (-7.12) ^? fracLatitude
+-- Just (Latitude (DegreesLatitude (-7)) (Minutes 7) (Seconds 12.0000))
+--
+-- >>> 90 ^? fracLatitude
+-- Nothing
+--
+-- >>> (-90) ^? fracLatitude
+-- Nothing
+--
+-- >>> 15.63791 ^? fracLatitude
+-- Just (Latitude (DegreesLatitude 15) (Minutes 38) (Seconds 16.4760))
+--
+-- >>> 89.1 ^? fracLatitude
+-- Just (Latitude (DegreesLatitude 89) (Minutes 5) (Seconds 60.0000))
+--
+-- >>> 89.2 ^? fracLatitude
+-- Just (Latitude (DegreesLatitude 89) (Minutes 12) (Seconds 0.0000))
+fracLatitude ::
+  Prism' Double Latitude
+fracLatitude =
+  prism' (\(Latitude d m s) ->
+    fromIntegral (nDegreesLatitude # d) + nSeconds # s + fromIntegral (nMinutes # m * 60))
+    (\x -> let (d, z) = properFraction x
+               (m, s) = properFraction ((z :: Double) * 60)
+           in do d' <- d ^? nDegreesLatitude
+                 m' <- abs m ^? nMinutes
+                 s' <- (abs s * 60) ^? nSeconds
+                 return (Latitude d' m' s'))
+
+class HasLatitude t where
+  latitude ::
+    Lens' t Latitude
+
+instance HasLatitude Latitude where
+  latitude =
+    id
+
+instance HasDegreesLatitude Latitude where
+  degreesLatitude =
+    lens (\(Latitude d _ _) -> d) (\(Latitude _ m s) d -> Latitude d m s)
+
+instance HasMinutes Latitude where
+  minutes =
+    lens (\(Latitude _ m _) -> m) (\(Latitude d _ s) m -> Latitude d m s)
+
+instance HasSeconds Latitude where
+  seconds =
+    lens (\(Latitude _ _ s) -> s) (\(Latitude d m _) s -> Latitude d m s)
diff --git a/src/Data/Geo/Coordinate/Longitude.hs b/src/Data/Geo/Coordinate/Longitude.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Geo/Coordinate/Longitude.hs
@@ -0,0 +1,106 @@
+module Data.Geo.Coordinate.Longitude(
+  Longitude
+, HasLongitude(..)
+, dmsLongitude
+, fracLongitude
+) where
+
+import Prelude(Double, Eq, Show, Ord(..), Num(..), Bool(..), Monad(..), id, (&&), properFraction, fromIntegral)
+import Control.Lens(Iso', Prism', Lens', iso, prism', lens, (#), (^?))
+import Data.Geo.Coordinate.DegreesLongitude
+import Data.Geo.Coordinate.Minutes
+import Data.Geo.Coordinate.Seconds
+
+data Longitude =
+  Longitude
+    DegreesLongitude
+    Minutes
+    Seconds
+  deriving (Eq, Ord, Show)
+
+-- | An isomorphism on the triple of degrees longitude, minutes, seconds to a longitude.
+--
+-- >>> do deg <- 7 ^? nDegreesLongitude; min <- 7 ^? nMinutes; sec <- 7 ^? nSeconds; (deg, min, sec) ^? dmsLongitude
+-- Just (Longitude (DegreesLongitude 7) (Minutes 7) (Seconds 7.0000))
+--
+-- >>> do deg <- 179 ^? nDegreesLongitude; min <- 59 ^? nMinutes; sec <- 59 ^? nSeconds; (deg, min, sec) ^? dmsLongitude
+-- Just (Longitude (DegreesLongitude 179) (Minutes 59) (Seconds 59.0000))
+--
+-- >>> do deg <- (-7) ^? nDegreesLongitude; min <- 7 ^? nMinutes; sec <- 7 ^? nSeconds; (deg, min, sec) ^? dmsLongitude
+-- Just (Longitude (DegreesLongitude (-7)) (Minutes 7) (Seconds 7.0000))
+--
+-- >>> do deg <- (-179) ^? nDegreesLongitude; min <- 59 ^? nMinutes; sec <- 59 ^? nSeconds; (deg, min, sec) ^? dmsLongitude
+-- Just (Longitude (DegreesLongitude (-179)) (Minutes 59) (Seconds 59.0000))
+--
+-- >>> do deg <- 180 ^? nDegreesLongitude; min <- 59 ^? nMinutes; sec <- 59 ^? nSeconds; (deg, min, sec) ^? dmsLongitude
+-- Nothing
+--
+-- >>> do deg <- 179 ^? nDegreesLongitude; min <- 60 ^? nMinutes; sec <- 59 ^? nSeconds; (deg, min, sec) ^? dmsLongitude
+-- Nothing
+--
+-- >>> do deg <- 179 ^? nDegreesLongitude; min <- 59 ^? nMinutes; sec <- 60 ^? nSeconds; (deg, min, sec) ^? dmsLongitude
+-- Nothing
+dmsLongitude ::
+  Iso' (DegreesLongitude, Minutes, Seconds) Longitude
+dmsLongitude =
+  iso (\(d, m, s) -> Longitude d m s) (\(Longitude d m s) -> (d, m, s))
+
+-- | A prism on longitude to a double between -180 and 180 inclusive.
+--
+-- >>> 7 ^? fracLongitude
+-- Just (Longitude (DegreesLongitude 7) (Minutes 0) (Seconds 0.0000))
+--
+-- >>> (-7) ^? fracLongitude
+-- Just (Longitude (DegreesLongitude (-7)) (Minutes 0) (Seconds 0.0000))
+--
+-- >>> 7.12 ^? fracLongitude
+-- Just (Longitude (DegreesLongitude 7) (Minutes 7) (Seconds 12.0000))
+--
+-- >>> (-7.12) ^? fracLongitude
+-- Just (Longitude (DegreesLongitude (-7)) (Minutes 7) (Seconds 12.0000))
+--
+-- >>> 180 ^? fracLongitude
+-- Nothing
+--
+-- >>> (-180) ^? fracLongitude
+-- Nothing
+--
+-- >>> 15.63791 ^? fracLongitude
+-- Just (Longitude (DegreesLongitude 15) (Minutes 38) (Seconds 16.4760))
+--
+-- >>> 179.1 ^? fracLongitude
+-- Just (Longitude (DegreesLongitude 179) (Minutes 5) (Seconds 60.0000))
+--
+-- >>> 179.2 ^? fracLongitude
+-- Just (Longitude (DegreesLongitude 179) (Minutes 11) (Seconds 60.0000))
+fracLongitude ::
+  Prism' Double Longitude
+fracLongitude =
+  prism' (\(Longitude d m s) ->
+    fromIntegral (nDegreesLongitude  # d) + nSeconds # s + fromIntegral (nMinutes # m * 60))
+    (\x -> let (d, z) = properFraction x
+               (m, s) = properFraction ((z :: Double) * 60)
+           in do d' <- d ^? nDegreesLongitude
+                 m' <- abs m ^? nMinutes
+                 s' <- (abs s * 60) ^? nSeconds
+                 return (Longitude d' m' s'))
+
+class HasLongitude t where
+  longitude ::
+    Lens' t Longitude
+
+instance HasLongitude Longitude where
+  longitude =
+    id
+
+instance HasDegreesLongitude Longitude where
+  degreesLongitude =
+    lens (\(Longitude d _ _) -> d) (\(Longitude _ m s) d -> Longitude d m s)
+
+instance HasMinutes Longitude where
+  minutes =
+    lens (\(Longitude _ m _) -> m) (\(Longitude d _ s) m -> Longitude d m s)
+
+instance HasSeconds Longitude where
+  seconds =
+    lens (\(Longitude _ _ s) -> s) (\(Longitude d m _) s -> Longitude d m s)
diff --git a/src/Data/Geo/Coordinate/Minutes.hs b/src/Data/Geo/Coordinate/Minutes.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Geo/Coordinate/Minutes.hs
@@ -0,0 +1,50 @@
+module Data.Geo.Coordinate.Minutes(
+  Minutes
+, HasMinutes(..)
+, nMinutes
+) where
+
+import Prelude(Functor, Int, Bool(..), Eq, Show, Ord(..), (&&), id)
+import Data.Maybe(Maybe(..))
+import Control.Lens(Prism', Lens', prism')
+
+-- $setup
+-- >>> import Control.Lens((#), (^?))
+-- >>> import Data.Foldable(all)
+-- >>> import Prelude(Eq(..))
+
+newtype Minutes =
+  Minutes Int
+  deriving (Eq, Ord, Show)
+
+-- | A prism on minutes to an integer between 0 and 59 inclusive.
+--
+-- >>> 7 ^? nMinutes
+-- Just (Minutes 7)
+--
+-- >>> 0 ^? nMinutes
+-- Just (Minutes 0)
+--
+-- >>> 59 ^? nMinutes
+-- Just (Minutes 59)
+--
+-- >>> 60 ^? nMinutes
+-- Nothing
+--
+-- prop> all (\m -> nMinutes # m == n) (n ^? nMinutes)
+nMinutes ::
+  Prism' Int Minutes
+nMinutes =
+  prism'
+    (\(Minutes i) -> i)
+    (\i -> case i >= 0 && i < 60 of
+             True -> Just (Minutes i)
+             False -> Nothing)
+
+class HasMinutes t where
+  minutes ::
+    Lens' t Minutes
+
+instance HasMinutes Minutes where
+  minutes =
+    id
diff --git a/src/Data/Geo/Coordinate/Seconds.hs b/src/Data/Geo/Coordinate/Seconds.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Geo/Coordinate/Seconds.hs
@@ -0,0 +1,60 @@
+module Data.Geo.Coordinate.Seconds(
+  Seconds
+, HasSeconds(..)
+, nSeconds
+) where
+
+import Prelude(Double, Bool(..), Eq, Show(..), Ord(..), id, (&&), (++), showParen, showString)
+import Data.Maybe(Maybe(..))
+import Control.Lens(Prism', Lens', prism')
+import Text.Printf(printf)
+
+-- $setup
+-- >>> import Control.Lens((#), (^?))
+-- >>> import Data.Foldable(all)
+-- >>> import Prelude(Eq(..))
+
+newtype Seconds =
+  Seconds Double
+  deriving (Eq, Ord)
+
+-- | A show instance that prints to 4 decimal places.
+-- This is to take floating-point rounding errors into account.
+instance Show Seconds where
+  showsPrec n (Seconds d) =
+    showParen (n > 10) (showString ("Seconds " ++ printf "%0.4f" d))
+
+-- | A prism on seconds to a double between 0 inclusive and 60 exclusive.
+--
+-- >>> 7 ^? nSeconds
+-- Just (Seconds 7.0000)
+--
+-- >>> 0 ^? nSeconds
+-- Just (Seconds 0.0000)
+--
+-- >>> 59 ^? nSeconds
+-- Just (Seconds 59.0000)
+--
+-- >>> 59.99 ^? nSeconds
+-- Just (Seconds 59.9900)
+--
+-- >>> 60 ^? nSeconds
+-- Nothing
+--
+-- prop> all (\m -> nSeconds # m == n) (n ^? nSeconds)
+nSeconds ::
+  Prism' Double Seconds
+nSeconds =
+  prism'
+    (\(Seconds d) -> d)
+    (\d -> case d >= 0 && d < 60 of
+             True -> Just (Seconds d)
+             False -> Nothing)
+
+class HasSeconds t where
+  seconds ::
+    Lens' t Seconds
+
+instance HasSeconds Seconds where
+  seconds =
+    id
