gps 0.9 → 1.0
raw patch · 3 files changed
+43/−33 lines, 3 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Data.GPS: class (LatL a, LonL a) => Coordinate a
+ Data.GPS: lat :: Coordinate a => a -> Latitude
+ Data.GPS: lon :: Coordinate a => a -> Longitude
- Data.GPS: addVector :: (LatL c, LonL c) => Vector -> c -> c
+ Data.GPS: addVector :: Coordinate c => Vector -> c -> c
- Data.GPS: avgSpeeds :: (LatL a, LonL a, TimeL a) => NominalDiffTime -> Trail a -> [(UTCTime, Speed)]
+ Data.GPS: avgSpeeds :: (Coordinate a, TimeL a) => NominalDiffTime -> Trail a -> [(UTCTime, Speed)]
- Data.GPS: betweenSpeeds :: (LatL a, LonL a, TimeL a) => Double -> Double -> PointGrouping a
+ Data.GPS: betweenSpeeds :: (Coordinate a, TimeL a) => Double -> Double -> PointGrouping a
- Data.GPS: bezierCurve :: (LatL a, LonL a, TimeL a) => [Selected (Trail a)] -> Trail a
+ Data.GPS: bezierCurve :: (Coordinate a, TimeL a) => [Selected (Trail a)] -> Trail a
- Data.GPS: bezierCurveAt :: (LatL a, LonL a, TimeL a) => [UTCTime] -> Trail a -> Trail a
+ Data.GPS: bezierCurveAt :: (Coordinate a, TimeL a) => [UTCTime] -> Trail a -> Trail a
- Data.GPS: bezierPoint :: (LatL a, LonL a) => [a] -> Double -> a
+ Data.GPS: bezierPoint :: Coordinate a => [a] -> Double -> a
- Data.GPS: circleIntersectionPoints :: (LatL a, LonL a) => (a, Distance) -> (a, Distance) -> Maybe (a, a)
+ Data.GPS: circleIntersectionPoints :: Coordinate a => (a, Distance) -> (a, Distance) -> Maybe (a, a)
- Data.GPS: closestDistance :: (LatL a, LonL a) => Trail a -> Trail a -> Maybe Distance
+ Data.GPS: closestDistance :: Coordinate a => Trail a -> Trail a -> Maybe Distance
- Data.GPS: convexHull :: (Eq c, LatL c, LonL c) => [c] -> [c]
+ Data.GPS: convexHull :: (Eq c, Coordinate c) => [c] -> [c]
- Data.GPS: distance :: (LatL a, LonL a, LatL b, LonL b) => a -> b -> Distance
+ Data.GPS: distance :: (Coordinate a, Coordinate b) => a -> b -> Distance
- Data.GPS: divideArea :: (LatL c, LonL c) => Distance -> Distance -> c -> c -> [[c]]
+ Data.GPS: divideArea :: Coordinate c => Distance -> Distance -> c -> c -> [[c]]
- Data.GPS: getDMSPair :: (LatL c, LonL c) => c -> (Latitude, Longitude)
+ Data.GPS: getDMSPair :: Coordinate c => c -> (Latitude, Longitude)
- Data.GPS: getRadianPair :: (LatL p, LonL p) => p -> (Latitude, Longitude)
+ Data.GPS: getRadianPair :: Coordinate p => p -> (Latitude, Longitude)
- Data.GPS: getVector :: (LatL a, LonL a, LatL b, LonL b) => a -> b -> Vector
+ Data.GPS: getVector :: (Coordinate a, Coordinate b) => a -> b -> Vector
- Data.GPS: heading :: (LatL a, LonL a, LatL b, LonL b) => a -> b -> Heading
+ Data.GPS: heading :: (Coordinate a, Coordinate b) => a -> b -> Heading
- Data.GPS: interpolate :: (LatL a, LonL a) => a -> a -> Double -> a
+ Data.GPS: interpolate :: Coordinate a => a -> a -> Double -> a
- Data.GPS: intersectionArcsOf :: (LatL a, LonL a) => [Circle a] -> [Arc a]
+ Data.GPS: intersectionArcsOf :: Coordinate a => [Circle a] -> [Arc a]
- Data.GPS: intersectionOf :: (LatL a, LonL a, TimeL a) => [PointGrouping a] -> PointGrouping a
+ Data.GPS: intersectionOf :: (Coordinate a, TimeL a) => [PointGrouping a] -> PointGrouping a
- Data.GPS: linearTime :: (LonL a, LatL a, TimeL a) => [a] -> [a]
+ Data.GPS: linearTime :: (Coordinate a, TimeL a) => [a] -> [a]
- Data.GPS: maximumDistanceOfArc :: (LatL a, LonL a) => a -> Arc a -> Distance
+ Data.GPS: maximumDistanceOfArc :: Coordinate a => a -> Arc a -> Distance
- Data.GPS: restLocations :: (LatL a, LonL a, TimeL a) => Distance -> NominalDiffTime -> PointGrouping a
+ Data.GPS: restLocations :: (Coordinate a, TimeL a) => Distance -> NominalDiffTime -> PointGrouping a
- Data.GPS: slidingAverageSpeed :: (LatL a, LonL a, TimeL a) => AvgMethod a -> NominalDiffTime -> Trail a -> [(UTCTime, Speed)]
+ Data.GPS: slidingAverageSpeed :: (Coordinate a, TimeL a) => AvgMethod a -> NominalDiffTime -> Trail a -> [(UTCTime, Speed)]
- Data.GPS: smoothMore :: (LatL a, LonL a, TimeL a) => Trail a -> Trail a
+ Data.GPS: smoothMore :: (Coordinate a, TimeL a) => Trail a -> Trail a
- Data.GPS: smoothRests :: (LatL a, LonL a, TimeL a) => Trail a -> Trail a
+ Data.GPS: smoothRests :: (Coordinate a, TimeL a) => Trail a -> Trail a
- Data.GPS: smoothSome :: (LatL a, LonL a, TimeL a) => Trail a -> Trail a
+ Data.GPS: smoothSome :: (Coordinate a, TimeL a) => Trail a -> Trail a
- Data.GPS: spansTime :: (LatL a, LonL a, TimeL a) => NominalDiffTime -> PointGrouping a
+ Data.GPS: spansTime :: (Coordinate a, TimeL a) => NominalDiffTime -> PointGrouping a
- Data.GPS: speed :: (LatL loc, LonL loc, TimeL loc, LatL b, LonL b, TimeL b) => loc -> b -> Maybe Speed
+ Data.GPS: speed :: (Coordinate loc, TimeL loc, Coordinate b, TimeL b) => loc -> b -> Maybe Speed
- Data.GPS: totalDistance :: (LatL a, LonL a) => [a] -> Distance
+ Data.GPS: totalDistance :: Coordinate a => [a] -> Distance
- Data.GPS: unionOf :: (LatL a, LonL a, TimeL a) => [PointGrouping a] -> PointGrouping a
+ Data.GPS: unionOf :: (Coordinate a, TimeL a) => [PointGrouping a] -> PointGrouping a
Files
- Data/GPS/Core.hs +23/−13
- Data/GPS/Trail.hs +19/−19
- gps.cabal +1/−1
Data/GPS/Core.hs view
@@ -8,6 +8,7 @@ , Trail , Circle , Arc+ , Coordinate (..) -- * Constants , north , south@@ -47,6 +48,15 @@ import Data.Geo.GPX import Data.Lens.Common +class (LatL a, LonL a) => Coordinate a where+ lat :: a -> Latitude+ lat = (^. latL)+ lon :: a -> Longitude+ lon = (^. lonL)++instance Coordinate Wpt+instance Coordinate Pt+ -- |Distances are expressed in meters type Distance = Double @@ -73,7 +83,7 @@ getUTCTime :: (TimeL a) => a -> Maybe UTCTime getUTCTime = fmap toUTCTime . (^. timeL) -distance :: (LatL a, LonL a, LatL b, LonL b) => a -> b -> Distance+distance :: (Coordinate a, Coordinate b) => a -> b -> Distance distance x y = let (lat1,lon1) = getRadianPairD x (lat2,lon2) = getRadianPairD y@@ -84,7 +94,7 @@ in radiusOfEarth * c -- | Direction two points aim toward (0 = North, pi/2 = West, pi = South, 3pi/2 = East)-heading :: (LatL a, LonL a, LatL b, LonL b) => a -> b -> Heading+heading :: (Coordinate a, Coordinate b) => a -> b -> Heading heading a b = atan2 (sin (diffLon) * cos (lat2)) (cos(lat1) * sin (lat2) - sin(lat1) * cos lat2 * cos (diffLon))@@ -93,7 +103,7 @@ (lat2, lon2) = getRadianPairD b diffLon = lon2 - lon1 -getVector :: (LatL a, LonL a, LatL b, LonL b) => a -> b -> Vector+getVector :: (Coordinate a, Coordinate b) => a -> b -> Vector getVector a b = (distance a b, heading a b) -- |Given a vector and coordinate, computes a new coordinate.@@ -106,7 +116,7 @@ -- @heading == heading start dest@ -- -- @dist == distance start dest@-addVector :: (LatL c, LonL c) => Vector -> c -> c+addVector :: (Coordinate c) => Vector -> c -> c addVector (d,h) p = (lonL ^= longitude (toDegrees lon2)) . (latL ^= latitude (toDegrees lat2)) $ p@@ -118,7 +128,7 @@ (cos (d/radiusOfEarth) - sin lat * sin lat2) -- | Speed in meters per second, only if a 'Time' was recorded for each waypoint.-speed :: (LatL loc, LonL loc, TimeL loc, LatL b, LonL b, TimeL b) => loc -> b -> Maybe Speed+speed :: (Coordinate loc, TimeL loc, Coordinate b, TimeL b) => loc -> b -> Maybe Speed speed a b = case (getUTCTime b, getUTCTime a) of (Just x, Just y) -> @@ -152,14 +162,14 @@ toDegrees = (*) (180 / pi) -getRadianPairD :: (LatL c, LonL c) => c -> (Double,Double)+getRadianPairD :: (Coordinate c) => c -> (Double,Double) getRadianPairD = (\(a,b) -> (realToFrac a, realToFrac b)) . getRadianPair -getDMSPair :: (LatL c, LonL c) => c -> (Latitude, Longitude)+getDMSPair :: (Coordinate c) => c -> (Latitude, Longitude) getDMSPair c = (c ^. latL, c ^. lonL) -- |Provides a lat/lon pair of doubles in radians-getRadianPair :: (LatL p, LonL p) => p -> (Latitude, Longitude)+getRadianPair :: (Coordinate p) => p -> (Latitude, Longitude) getRadianPair p = (toRadians (p ^. latL), toRadians (p ^. lonL)) toRadians :: Floating f => f -> f@@ -168,7 +178,7 @@ -- | @interpolate c1 c2 w@ where @0 <= w <= 1@ Gives a point on the line -- between c1 and c2 equal to c1 when @w == 0@ (weighted linearly -- toward c2).-interpolate :: (LatL a, LonL a) => a -> a -> Double -> a+interpolate :: (Coordinate a) => a -> a -> Double -> a interpolate c1 c2 w | w < 0 || w > 1 = error "Interpolate only works with a weight between zero and one" | otherwise = @@ -178,7 +188,7 @@ -- | Compute the points at which two circles intersect (assumes a flat plain). If -- the circles do not intersect or are identical then the result is @Nothing@.-circleIntersectionPoints :: (LatL a, LonL a) => (a, Distance) -> (a, Distance) -> Maybe (a,a)+circleIntersectionPoints :: (Coordinate a) => (a, Distance) -> (a, Distance) -> Maybe (a,a) circleIntersectionPoints (a,r1) (b,r2) | a ^. latL == b ^. latL && a ^. lonL == b ^. lonL && r1 == r2 = Nothing -- FIXME need approx eq | r1 + r2 < ab = Nothing@@ -196,7 +206,7 @@ -- | Find the area in which all given circles intersect. The resulting -- area is described in terms of the bounding arcs. All cirlces must -- intersect at two points.-intersectionArcsOf :: (LatL a, LonL a) => [Circle a] -> [Arc a]+intersectionArcsOf :: (Coordinate a) => [Circle a] -> [Arc a] intersectionArcsOf cs = let isArcWithinCircle circ arc = maximumDistanceOfArc (fst circ) arc <= (snd circ) isArcWithinAllCircles arc = all ($ arc) (map isArcWithinCircle cs)@@ -211,7 +221,7 @@ in [(c1,c1h1,c1h2), (c1,c1h2, c1h1), (c2,c2h1,c2h2), (c2,c2h2,c2h1)] in filter isArcWithinAllCircles . concatMap (uncurry getArcs) . choose2 $ cs -maximumDistanceOfArc :: (LatL a, LonL a) => a -> Arc a -> Distance+maximumDistanceOfArc :: (Coordinate a) => a -> Arc a -> Distance maximumDistanceOfArc pnt ((c,r), h1, h2) = let pcHeading = heading pnt c in if ((pcHeading < h1 || pcHeading > h2) && h1 < h2) || ((pcHeading > h2 && pcHeading < h1) && h1 > h2)@@ -227,7 +237,7 @@ -- southeast point (se). Because this uses floating point there might be a -- different number of points in some rows (the last might be too far east based -- on a heading from the se point).-divideArea :: (LatL c, LonL c) => Distance -> Distance -> c -> c -> [[c]]+divideArea :: (Coordinate c) => Distance -> Distance -> c -> c -> [[c]] divideArea vDist hDist nw se = let (top,left) = (nw ^. latL, nw ^. lonL) (btm,right) = (se ^. latL, se ^. lonL)
Data/GPS/Trail.hs view
@@ -82,12 +82,12 @@ -- | @avgSpeeds n points@ -- Average speed using a window of up to @n@ seconds and averaging by taking the -- Median ('AvgMedian').-avgSpeeds :: (LatL a, LonL a, TimeL a) => NominalDiffTime -> Trail a -> [(UTCTime, Speed)]+avgSpeeds :: (Coordinate a, TimeL a) => NominalDiffTime -> Trail a -> [(UTCTime, Speed)] avgSpeeds = slidingAverageSpeed AvgHarmonicMean -- | @slidingAverageSpeed m n@ Average speed using a moving window of up to @n@ seconds -- and an 'AvgMethod' of @m@.-slidingAverageSpeed :: (LatL a, LonL a, TimeL a) => +slidingAverageSpeed :: (Coordinate a, TimeL a) => AvgMethod a -> NominalDiffTime -> Trail a -> [(UTCTime, Speed)] slidingAverageSpeed _ _ [] = [] slidingAverageSpeed m minTime xs =@@ -175,7 +175,7 @@ -- and all others outside of the speed. The "speed" from point p(i) -- to p(i+1) is associated with p(i) (execpt for the first speed -- value, which is associated with both the first and second point)-betweenSpeeds :: (LatL a, LonL a, TimeL a) => Double -> Double -> PointGrouping a+betweenSpeeds :: (Coordinate a, TimeL a) => Double -> Double -> PointGrouping a betweenSpeeds low hi ps = let spds = concatMap maybeToList $ zipWith speed ps (drop 1 ps) psSpds = [(p,s) | p <- ps, s <- maybeToList (listToMaybe spds) ++ spds]@@ -189,7 +189,7 @@ -- | A "rest point" means the coordinates remain within a given distance -- for at least a particular amount of time.-restLocations :: (LatL a, LonL a, TimeL a) => Distance -> NominalDiffTime -> PointGrouping a+restLocations :: (Coordinate a, TimeL a) => Distance -> NominalDiffTime -> PointGrouping a restLocations d s ps = let consToFirst x [] = [NotSelect [x]] consToFirst x (a:as) = (fmap (x:) a) : as@@ -208,7 +208,7 @@ -- |chunking points into groups spanning at most the given time -- interval.-spansTime :: (LatL a, LonL a, TimeL a) => NominalDiffTime -> PointGrouping a+spansTime :: (Coordinate a, TimeL a) => NominalDiffTime -> PointGrouping a spansTime n ps = let times = mkTimePair ps chunk [] = []@@ -218,7 +218,7 @@ in map (Select . map fst) $ chunk times -- | intersects the given groupings-intersectionOf :: (LatL a, LonL a, TimeL a) => [PointGrouping a] -> PointGrouping a+intersectionOf :: (Coordinate a, TimeL a) => [PointGrouping a] -> PointGrouping a intersectionOf gs ps = let groupings = map ($ ps) gs -- chunk :: [[Selected [pnts]]] -> pnts -> [pnts]@@ -231,7 +231,7 @@ in chunk groupings ps -- | Union all the groupings-unionOf :: (LatL a, LonL a, TimeL a) => [PointGrouping a] -> PointGrouping a+unionOf :: (Coordinate a, TimeL a) => [PointGrouping a] -> PointGrouping a unionOf gs ps = let groupings = map ($ ps) gs chunk _ [] = []@@ -338,7 +338,7 @@ -- The current implementation assumes the times of the input -- coordinates are available and all equal (Ex: all points are 5 -- seconds apart), the results will be poor if this is not the case!-bezierCurveAt :: (LatL a, LonL a, TimeL a) => [UTCTime] -> Trail a -> Trail a+bezierCurveAt :: (Coordinate a, TimeL a) => [UTCTime] -> Trail a -> Trail a bezierCurveAt _ [] = [] bezierCurveAt selectedTimes xs = let timesDef = mkTimePair xs@@ -357,7 +357,7 @@ newTimes = [addUTCTime t (snd top) | t <- diffTimes] in zipWith ((timeL ^=) . Just . fromUTCTime) newTimes curvePoints -bezierPoint :: (LatL a, LonL a) => [a] -> Double -> a+bezierPoint :: (Coordinate a) => [a] -> Double -> a bezierPoint pnts t = go pnts where go [] = error "GPS Package: Can not create a bezier point from an empty list"@@ -368,12 +368,12 @@ -- exponentially more expensive with the length of the segement being -- transformed - it is not advisable to perform this operation on -- trail segements with more than ten points!-bezierCurve :: (LatL a, LonL a, TimeL a) => [Selected (Trail a)] -> Trail a+bezierCurve :: (Coordinate a, TimeL a) => [Selected (Trail a)] -> Trail a bezierCurve = concatMap (onSelected (bezierCurveAt []) Prelude.id) -- |Filters out any points that go backward in time (thus must not be -- valid if this is a trail)-linearTime :: (LonL a, LatL a, TimeL a) => [a] -> [a]+linearTime :: (Coordinate a, TimeL a) => [a] -> [a] linearTime [] = [] linearTime (p:ps) = go (getUTCTime p) ps where@@ -383,11 +383,11 @@ -- |Returns the closest distance between two trails (or Nothing if a -- trail is empty). Inefficient implementation: -- O( (n * m) * log (n * m) )-closestDistance :: (LatL a, LonL a) => Trail a -> Trail a -> Maybe Distance+closestDistance :: (Coordinate a) => Trail a -> Trail a -> Maybe Distance closestDistance as bs = listToMaybe $ L.sort [distance a b | a <- as, b <- bs] -- | Find the total distance traveled-totalDistance :: (LatL a, LonL a) => [a] -> Distance+totalDistance :: (Coordinate a) => [a] -> Distance totalDistance as = sum $ zipWith distance as (drop 1 as) totalTime :: TimeL a => Trail a -> NominalDiffTime@@ -397,7 +397,7 @@ -- | Uses Grahams scan to compute the convex hull of the given points. -- This operation requires sorting of the points, so don't try it unless -- you have notably more memory than the list of points will consume.-convexHull :: (Eq c, LatL c, LonL c) => [c] -> [c]+convexHull :: (Eq c, Coordinate c) => [c] -> [c] convexHull xs = let first = southMost xs in case first of@@ -417,12 +417,12 @@ Straight -> grahamScan (x:p2:p1:ps) xs _ -> grahamScan (p1:ps) (x:xs) -eastZeroHeading :: (LatL c, LonL c) => c -> c -> Heading+eastZeroHeading :: (Coordinate c) => c -> c -> Heading eastZeroHeading s = (`mod'` (2*pi)) . (+ pi/2) . heading s data Turn = LeftTurn | RightTurn | Straight deriving (Eq, Ord, Show, Read, Enum) -turn :: (LatL c, LonL c) => c -> c -> c -> Turn+turn :: (Coordinate c) => c -> c -> c -> Turn turn a b c = let h1 = eastZeroHeading a b h2 = eastZeroHeading b c@@ -440,10 +440,10 @@ -- users or as instructional examples. ------------------------------------------ -smoothRests :: (LatL a, LonL a, TimeL a) => Trail a -> Trail a+smoothRests :: (Coordinate a, TimeL a) => Trail a -> Trail a smoothRests = bezierCurve . refineGrouping (everyNPoints 8) . restLocations 30 60 -smoothSome :: (LatL a, LonL a, TimeL a) => Trail a -> Trail a+smoothSome :: (Coordinate a, TimeL a) => Trail a -> Trail a smoothSome = gSmoothSome 7 gSmoothSome n = bezierCurve . everyNPoints n@@ -453,7 +453,7 @@ (h,t) = splitAt k ps' in h ++ gSmoothSome n t -smoothMore :: (LatL a, LonL a, TimeL a) => Trail a -> Trail a+smoothMore :: (Coordinate a, TimeL a) => Trail a -> Trail a smoothMore = gSmoothMore 7 3 . gSmoothMore 3 1 . gSmoothMore 5 2 . gSmoothMore 3 1 . gSmoothMore 5 2 . gSmoothMore 7 3
gps.cabal view
@@ -1,5 +1,5 @@ name: gps-version: 0.9+version: 1.0 license: BSD3 license-file: LICENSE author: Thomas DuBuisson <thomas.dubuisson@gmail.com>