packages feed

osm-download 0.1.2 → 0.2

raw patch · 2 files changed

+322/−91 lines, 2 filesdep +acid-statedep +conduitdep +containersdep ~gpsdep ~http-conduitPVP ok

version bump matches the API change (PVP)

Dependencies added: acid-state, conduit, containers, http-types, monadIO, mtl, safecopy, stm, stm-chans, time, transformers, transformers-base

Dependency ranges changed: gps, http-conduit

API changes (from Hackage documentation)

- Network.OSM: data TileID
+ Network.OSM: OSMCfg :: String -> FilePath -> Maybe (TileID -> Zoom -> IO (Either Status ByteString)) -> Int -> Int -> Bool -> OSMConfig
+ Network.OSM: TID :: (Int, Int) -> TileID
+ Network.OSM: baseUrl :: OSMConfig -> String
+ Network.OSM: cache :: OSMConfig -> FilePath
+ Network.OSM: data OSM m a
+ Network.OSM: data OSMConfig
+ Network.OSM: data OSMState
+ Network.OSM: defaultOSMConfig :: IO OSMConfig
+ Network.OSM: downloadTile :: String -> Zoom -> TileID -> IO (Either Status ByteString)
+ Network.OSM: evalOSM :: MonadIO m => OSM m a -> OSMConfig -> m a
+ Network.OSM: getBestFitTiles :: (Coordinate a, MonadIO m) => [a] -> OSM m [[Either Status ByteString]]
+ Network.OSM: getTile :: MonadIO m => TileID -> Zoom -> OSM m (Either Status ByteString)
+ Network.OSM: getTiles :: MonadIO m => [[TileID]] -> Zoom -> OSM m [[Either Status ByteString]]
+ Network.OSM: instance Data TileCache
+ Network.OSM: instance Data TileID
+ Network.OSM: instance Eq TileCoords
+ Network.OSM: instance IsAcidic TileCache
+ Network.OSM: instance Method QueryTC
+ Network.OSM: instance Method UpdateTC
+ Network.OSM: instance Monad m => Monad (OSM m)
+ Network.OSM: instance Monad m => MonadState OSMState (OSM m)
+ Network.OSM: instance MonadIO m => MonadIO (OSM m)
+ Network.OSM: instance MonadTrans OSM
+ Network.OSM: instance Ord TileCoords
+ Network.OSM: instance QueryEvent QueryTC
+ Network.OSM: instance SafeCopy QueryTC
+ Network.OSM: instance SafeCopy TileCache
+ Network.OSM: instance SafeCopy TileID
+ Network.OSM: instance SafeCopy UpdateTC
+ Network.OSM: instance Show TileCoords
+ Network.OSM: instance Typeable QueryTC
+ Network.OSM: instance Typeable TileCache
+ Network.OSM: instance Typeable TileID
+ Network.OSM: instance Typeable UpdateTC
+ Network.OSM: instance UpdateEvent UpdateTC
+ Network.OSM: networkEnabled :: OSMConfig -> Bool
+ Network.OSM: newtype TileID
+ Network.OSM: noCacheAction :: OSMConfig -> Maybe (TileID -> Zoom -> IO (Either Status ByteString))
+ Network.OSM: nrConcurrentDownloads :: OSMConfig -> Int
+ Network.OSM: nrQueuedDownloads :: OSMConfig -> Int
+ Network.OSM: selectTilesWithFixedDimensions :: Coordinate a => (Int, Int) -> a -> Zoom -> [[TileID]]
+ Network.OSM: type Zoom = Int
+ Network.OSM: unTID :: TileID -> (Int, Int)
- Network.OSM: determineTileCoords :: (Lat a, Lon a) => [a] -> Int -> Maybe TileCoords
+ Network.OSM: determineTileCoords :: Coordinate a => [a] -> Zoom -> Maybe TileCoords
- Network.OSM: downloadBestFitTiles :: (Lat a, Lon a) => String -> [a] -> IO [[ByteString]]
+ Network.OSM: downloadBestFitTiles :: Coordinate a => String -> [a] -> IO [[Either Status ByteString]]
- Network.OSM: downloadTiles :: String -> [[TileID]] -> Int -> IO [[ByteString]]
+ Network.OSM: downloadTiles :: String -> Zoom -> [[TileID]] -> IO [[Either Status ByteString]]
- Network.OSM: pixelPosForCoord :: (Lon a, Lat a, Integral t) => [a] -> TileCoords -> Int -> (t, t)
+ Network.OSM: pixelPosForCoord :: (Coordinate a, Integral t) => a -> TileCoords -> Zoom -> (t, t)

Files

Network/OSM.hs view
@@ -1,28 +1,67 @@+{-# LANGUAGE TemplateHaskell, TypeFamilies, DeriveDataTypeable,+    FlexibleInstances, MultiParamTypeClasses, OverloadedStrings +    , GeneralizedNewtypeDeriving, FlexibleContexts #-} module Network.OSM-  (  -- * Types-    TileID+  (  -- * Basic Types+    TileID(..)   , TileCoords(..)-     -- * High Level Operations+  , Zoom+    -- * Types for tile cacheing+  , OSMConfig(..)+  , OSMState+  , OSM+    -- * High-level (cacheing) Operations+  , evalOSM+  , getBestFitTiles+  , getTiles+  , getTile+  , defaultOSMConfig+    -- * Network Operations   , downloadBestFitTiles+  , downloadTiles+  , downloadTile   , osmTileURL+  -- * Helper Functions   , pixelPosForCoord-  -- * Low level and helper functions+  , selectTilesWithFixedDimensions   , determineTileCoords   , selectedTiles-  , downloadTiles   -- * Legal   , copyrightText   )where -import Data.GPS-import Network.HTTP.Conduit-import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as L import Control.Monad+import Control.Monad.Base (liftBase) import Data.Bits+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as L+import Data.GPS import Data.Maybe import Data.Word+import Network.HTTP.Conduit+import Network.HTTP.Types ( Status, statusOK, ResponseHeaders+                          , parseSimpleQuery, statusServiceUnavailable) +-- For the cacheing+import Control.Concurrent.MonadIO (forkIO)+import Control.Concurrent.STM+import Control.Concurrent.STM.TBChan+import Control.Monad.IO.Class (MonadIO(..))+import Control.Monad.Reader (ask)+import Control.Monad.State+import Data.Acid+import Data.Char (isDigit)+import Data.Conduit+import Data.Data+import Data.SafeCopy+import Data.Time+import Data.Typeable+import Paths_osm_download+import qualified Data.ByteString.Char8 as BC+import qualified Data.Map as M++type Zoom = Int+ -- | The official OSM tile server. osmTileURL :: String osmTileURL = "http://tile.openstreetmap.org"@@ -35,27 +74,69 @@                      , maxX :: Int                        , minY :: Int                        , maxY :: Int -                     } +                     } deriving (Eq, Ord, Show) -data TileID = TID { unTID :: (Int, Int) } deriving (Eq, Ord, Show)-       --- OSM defined a of converting a coordinate and zoom level to a list of tiles-tileNumbers :: Double -> Double -> Int -> [(Int,Int)]-tileNumbers latitude longitude zoom = -             let xtile = ((longitude+180) / 360) * fromInteger (shift (1::Integer) zoom)+-- |A TileID, along with a zoom level, uniquely identifies a single+-- OSM map tile.  The standard size is 256x256 pixels for such a tile.+newtype TileID = TID { unTID :: (Int, Int) } deriving (Eq, Ord, Show, Data, Typeable)++-- |An on-disk cache of tiles.  Failure to use a local cache results in+-- excessive requests to the tile server.  OSM admins might ban such users.+newtype TileCache = TC (M.Map (TileID,Zoom) (UTCTime,B.ByteString))+  deriving (Data, Typeable)++updateTC :: UTCTime -> (TileID,Zoom) -> B.ByteString -> Update TileCache ()+updateTC expire tid bs = do+  TC tc <- get+  let tc' = M.insert tid (expire,bs) tc+  put (TC tc')++queryTC :: (TileID,Zoom) -> Query TileCache (Maybe (UTCTime,B.ByteString))+queryTC tid = do+  TC st <- ask+  return $ M.lookup tid st++$(deriveSafeCopy 1 'base ''TileID)+$(deriveSafeCopy 1 'base ''TileCache)+$(makeAcidic ''TileCache ['updateTC, 'queryTC])++tileNumbers :: Integral t => Double -> Double -> Zoom -> [(t, t)]+tileNumbers t g z =+  let (a,b) = tileNumbers' t g z+      bounds x = [ceiling x, floor x]+  in [(xt,yt) | xt <- bounds a, yt <- bounds b]++-- |OSM defined method of converting a coordinate and zoom level to a tile+tileNumbers' :: Double -> Double -> Zoom -> (Double,Double)+tileNumbers' latitude longitude zoom = +             let n = 2^zoom+                 xtile = ((longitude+180) / 360) * n                  tmp = log (tan (latitude*pi / 180) + secant (latitude * pi / 180))-                 ytile = ((1-tmp / pi) / 2.0) * fromInteger (shift (1::Integer) zoom)-                 bounds x = [ceiling x, floor x]-             in [(xt,yt) | xt <- bounds xtile, yt <- bounds ytile]+                 ytile = ((1-tmp / pi) / 2.0) * n+             in (xtile,ytile)  secant :: Floating a => a -> a secant a = 1 / cos a +-- |Given a width, height and center, compute the tiles needed to fill the display+--+-- THIS ASSUMES tiles are 256x256 pixels!+selectTilesWithFixedDimensions :: (Coordinate a) => (Int,Int) -> a -> Zoom -> [[TileID]]+selectTilesWithFixedDimensions (w,h) center z =+  let (x,y) = tileNumbers' (lat center) (lon center) z+      nrColumns2, nrRows2 :: Int+      nrColumns2 = 1 + ceiling (fromIntegral w / 512)+      nrRows2    = 1 + ceiling (fromIntegral h / 512)+      -- FIXME hardcoding the tile server tile pixel width+  in [ [ TID (xp, yp) | xp <- [truncate x - nrColumns2..truncate x + nrColumns2]] +         | yp <- [truncate y - nrRows2..truncate y + nrRows2] ] +       -- FIXME not handling boundary conditions, such as +/-180 longitude!+ -- |Computes the rectangular map region to download based on GPS points and a zoom level-determineTileCoords :: (Lat a, Lon a) => [a] -> Int -> Maybe TileCoords+determineTileCoords :: (Coordinate a) => [a] -> Zoom -> Maybe TileCoords determineTileCoords [] _ = Nothing determineTileCoords wpts z =-    let (xs,ys) = unzip $ concatMap (\w -> tileNumbers (realToFrac $ lat w) (realToFrac $ lon w) z) wpts+    let (xs,ys) = unzip $ concatMap (\w -> tileNumbers (lat w) (lon w) z) wpts     in Just $ TileCoords          { maxX = maximum xs          , minX = minimum xs@@ -65,7 +146,7 @@  maxNumAutoTiles = 32 -zoomCalc :: TileCoords -> Int+zoomCalc :: TileCoords -> Zoom zoomCalc tCoords =     let numxtiles = maxX tCoords - minX tCoords + 1        numytiles = maxY tCoords - minY tCoords + 1@@ -82,25 +163,40 @@ selectedTiles c = map (\j -> [TID (i,j) | i <- [minX c..maxX c]]) [minY c .. maxY c]  -- | Formats the URL string-urlStr :: String -> Int -> Int -> Int -> String-urlStr base xTile yTile zoom = base ++"/"++show zoom++"/"++show xTile++"/"++show yTile++".png"+urlStr :: String -> TileID -> Zoom -> String+urlStr base (TID (xTile, yTile)) zoom = base ++"/"++show zoom++"/"++show xTile++"/"++show yTile++".png"  -- | Takes the boundaries of the OSM tiles and downloads the tiles, -- keeping them in proper grid patterns for latter stiching or -- side-by-side display.-downloadTiles :: String -> [[TileID]] -> Int -> IO [[B.ByteString]]-downloadTiles base ts zoom = do+downloadTiles :: String -> Zoom -> [[TileID]] -> IO [[Either Status B.ByteString]]+downloadTiles base zoom ts = runResourceT $ do+  man <- newManager+  mapM (mapM (liftM (fmap snd) . downloadTile' man base zoom)) ts+  +-- |Download a single tile form a given OSM server URL.+downloadTile :: String -> Zoom -> TileID -> IO (Either Status B.ByteString)+downloadTile base zoom t = runResourceT $ do+  man <- newManager+  liftM (fmap snd) (downloadTile' man base zoom t)++downloadTile' :: Manager -> String -> Zoom -> TileID -> ResourceT IO (Either Status (ResponseHeaders,B.ByteString))+downloadTile' man base zoom t = do   let packIt = B.concat . L.toChunks-  mapM (mapM (\(x,y) -> liftM packIt $ simpleHttp (urlStr base x y zoom))) (map (map unTID) ts)+  url' <- liftBase (parseUrl (urlStr base t zoom))+  rsp <- httpLbs url' man+  if statusCode rsp == statusOK+    then return (Right (responseHeaders rsp, packIt (responseBody rsp)))+    else return (Left $ statusCode rsp)  projectMercToLat :: Floating a => a -> a projectMercToLat rely = (180 / pi) * atan (sinh rely)  -- | Used by @pixelPosForCoord@ for N,S,E,W coordinates for (x,y) values-project :: Int -> Int -> Int -> (Double,Double,Double,Double)+project :: Double -> Double -> Zoom -> (Double,Double,Double,Double) project x y zoom =    let unit = 1.0 / (2.0 ** fromIntegral zoom)-      rely1 = fromIntegral y * unit+      rely1 = y * unit       rely2 = rely1 + unit       limity = pi       rangey = 2.0 * limity@@ -109,22 +205,21 @@       lat1 = projectMercToLat rely1'       lat2 = projectMercToLat rely2'       unit' = 360.0 / (2.0 ** fromIntegral zoom)-      long1 = (-180.0) + fromIntegral x * unit'+      long1 = (-180.0) + x * unit'   in (lat2,long1,lat1,long1+unit') -- S,W,N,E   --- | Takes a WptType, the OSM tile boundaries, and a zoom level then+-- | Takes a coordinate, the OSM tile boundaries, and a zoom level then -- generates (x,y) points to be placed on the Image.-pixelPosForCoord :: (Lon a, Lat a, Integral t) => [a] -> TileCoords -> Int -> (t, t)-pixelPosForCoord [] _ _ = (0,0)-pixelPosForCoord [wpt] tCoord zoom =-             let lat' = value $ lat wpt-                 lon' = value $ lon wpt-                 tile = maximum $ tileNumbers lat' lon' zoom-                 xoffset = (fst tile - minX tCoord) * 256-                 yoffset = (snd tile - minY tCoord) * 256+pixelPosForCoord :: (Coordinate a, Integral t) => a -> TileCoords -> Zoom -> (t, t)+pixelPosForCoord wpt tCoord zoom =+             let lat' = lat wpt+                 lon' = lon wpt+                 tile = tileNumbers' lat' lon' zoom+                 xoffset = (fst tile - fromIntegral (minX tCoord)) * 256+                 yoffset = (snd tile - fromIntegral (minY tCoord)) * 256                  (south,west,north,east) = (uncurry project tile zoom)-                 x = round $ (lon' - west) * 256.0 / (east - west) + fromIntegral xoffset-                 y = round $ (lat' - north) * 256.0 / (south - north) + fromIntegral yoffset+                 x = round $ (lon' - west) * 256.0 / (east - west) + xoffset+                 y = round $ (lat' - north) * 256.0 / (south - north) + yoffset              in (x,y)  -- | The suggested copyright text in accordance with@@ -139,17 +234,175 @@ -- The returned files should all be in an approriate grid for row/column display. -- See the test files of Main.hs and Main2.hs for examples of Repa stiching tiles -- into a single image or side by side display of individual tiles.-downloadBestFitTiles :: (Lat a, Lon a) => String -> [a] -> IO [[B.ByteString]]+downloadBestFitTiles :: (Coordinate a) => String -> [a] -> IO [[Either Status B.ByteString]] downloadBestFitTiles base points = do+  let (coords,zoom) = bestFitCoordinates points+      tids = selectedTiles coords+  downloadTiles base zoom tids++bestFitCoordinates :: (Coordinate a) => [a] -> (TileCoords, Zoom)+bestFitCoordinates points =    let tiles = determineTileCoords points 16       zoom = fmap zoomCalc tiles       tiles' = join               . fmap (determineTileCoords points)              $ zoom-  case tiles' of-     Nothing -> return []-     Just coord -> -         let tids = selectedTiles coord-         in concatMapM (downloadTiles base tids) (maybeToList zoom)+  in case (tiles',zoom) of+      (Just coord, Just z) -> (coord,z)+      _                    -> (TileCoords 0 0 0 0,0) -concatMapM f = liftM concat . mapM f++-- |The cacheing operations run in their own monad that describe the+-- location of the cache, the tile server URL, and the worker threads+-- the retrieve tiles.+data OSMConfig = OSMCfg+                { baseUrl      :: String+                , cache       :: FilePath                  -- ^ Path of the tile cache+                , noCacheAction :: Maybe (TileID -> Zoom -> IO (Either Status B.ByteString))+                                                           -- ^ Action to take if the tile is not cached.+                                                           --   Return 'Just' val for a default value.+                                                           --   Return 'Nothing' to wait for a tile server.+                , nrQueuedDownloads     :: Int             -- ^ Max download queue size+                , nrConcurrentDownloads :: Int             -- ^ Number of threads the tile downloading+                                                           --   can concurrently run in.  Tileserver+                                                           --   admins request this be no more than 2.+                , networkEnabled :: Bool                   -- ^ True if we should use the network to+                                                           -- download Tiles+                }++-- |The OSM operations maintain a list of tiles needing refreshed (for+-- local caching), the state of the local cache, and initial+-- configuration options.+data OSMState = OSMSt +                { acid        :: AcidState TileCache+                , neededTiles :: TBChan (TileID,Zoom)+                , cfg         :: OSMConfig }++-- |A Monad transformer allowing you acquire OSM maps+newtype OSM m a = OSM { runOSM :: StateT OSMState m a }+         deriving (Monad, MonadTrans, MonadState OSMState)++instance (MonadIO m) => MonadIO (OSM m) where+  liftIO = lift . liftIO++-- |evalOSM allows you to query an OSM server and the local cache.+-- Take note - the 'OSMConfig' thread limit is enforced per-evalOSM.+-- Running many evalOSM processes can result in a violation of the+-- limit and incur admin wrath.+evalOSM :: MonadIO m => OSM m a -> OSMConfig -> m a+evalOSM m cfg = do+  tc <- liftIO $ newTBChanIO (nrQueuedDownloads cfg)+  acid <- liftIO $ openLocalStateFrom (cache cfg) (TC M.empty)+  liftIO $ mapM_ forkIO $ replicate (nrConcurrentDownloads cfg) (monitorTileQueue cfg acid tc)+  let s = OSMSt acid tc cfg+  evalStateT (runOSM m) s++-- Pulls requested tiles off the queue, downloads them, and adds them+-- to the cache.  We need to re-check the cache to make sure someone+-- hasn't already inserted it while the item was queued.  We leave the+-- possibility that it is being downloaded in parallel by another+-- 'monitorTileQueue' as acceptable duplication of work.+monitorTileQueue :: OSMConfig -> AcidState TileCache -> TBChan (TileID, Zoom) -> IO ()+monitorTileQueue cfg acid tc = forever $ do+  (t,z) <- atomically $ readTBChan tc+  b <- liftIO $ query acid (QueryTC (t,z))+  case b of+    Nothing -> doDownload t z+    Just (exp,_) -> do+      now <- getCurrentTime+      when (exp < now) (doDownload t z)+ where  +   doDownload t z = do+     let addr = buildUrl cfg t z+     tileE <- downloadTileAndExprTime addr z t+     case tileE of+       Left err -> return ()+       Right (exp,bs)  -> update acid (UpdateTC exp (t,z) bs) >> createCheckpoint acid++-- |A default configuration using the main OSM server as a tile server+-- and a cabal-generated directory for the cache directory+defaultOSMConfig :: IO OSMConfig+defaultOSMConfig = do+  cache <- getDataFileName "TileCache"+  return $ OSMCfg osmTileURL cache Nothing 1024 2 True++buildUrl :: OSMConfig -> TileID -> Zoom -> String+buildUrl cfg t z = urlStr (baseUrl cfg) t z++-- |Like 'downloadBestFitTiles' but uses the cached copies when available.+getBestFitTiles :: (Coordinate a, MonadIO m)+                     => [a] -> OSM m [[Either Status B.ByteString]]+getBestFitTiles cs = do+  let (coords,zoom) = bestFitCoordinates cs+      tids = selectedTiles coords+  getTiles tids zoom++-- |Like 'downloadTiles' but uses the cached copies when available+getTiles :: (MonadIO m)+            => [[TileID]] +            -> Zoom +            -> OSM m [[Either Status B.ByteString]]+getTiles ts z = mapM (mapM (\t -> getTile t z)) ts++downloadTileAndExprTime ::    String +                           -> Zoom +                           -> TileID +                           -> IO (Either Status (UTCTime,B.ByteString))+downloadTileAndExprTime base z t = do+  res <- runResourceT $ newManager >>= \m -> downloadTile' m base z t+  case res of+    Right (hdrs,bs) -> do+      now <- getCurrentTime+      let maxSec = cacheLength hdrs+          delTime = addUTCTime (fromIntegral maxSec) now+      return $ Right (delTime,bs)+    Left e -> return (Left e)++-- |Like 'downloadTile' but uses a cached copy when available.+-- Downloaded copies are added to the cache.+-- +-- When the cached copy is out of date it will still be returned but a+-- new copy will be downloaded and added to the cache concurrently.+getTile :: (MonadIO m) => TileID -> Zoom -> OSM m (Either Status B.ByteString)+getTile t zoom = do+  st  <- gets acid+  ch  <- gets neededTiles+  nca <- gets (noCacheAction . cfg)+  b <- liftIO $ query st (QueryTC (t,zoom))+  case b of+    Nothing -> do+      case nca of+        Nothing  -> blockingTileDownloadUpdateCache+        Just act -> liftIO $ do+          atomically $ writeTBChan ch (t,zoom)+          act t zoom+    Just (expTime,x)  -> do+      liftIO $ do+         now <- getCurrentTime+         let exp = expTime < now+         when exp (atomically (tryWriteTBChan ch (t,zoom)) >> return ())+      return (Right x)+  where+    blockingTileDownloadUpdateCache = do+      st  <- gets acid+      net <- gets (networkEnabled . cfg)+      base <- gets (baseUrl . cfg)+      if net +       then do+        res <- liftIO $ downloadTileAndExprTime base zoom t+        case res of+           Right (delTime,bs) -> do+             liftIO $ do+               update st (UpdateTC delTime (t,zoom) bs)+               createCheckpoint st+             return (Right bs)+           Left err -> return (Left err)+       else return (Left statusServiceUnavailable)++-- | Determine the lenth of time to cache an HTTP response (in seconds)+cacheLength :: ResponseHeaders -> Int+cacheLength hdrs =+  let v = lookup "Cache-Control" hdrs+      c = fmap parseSimpleQuery v+      age = join . fmap (lookup "max-age") $ c+  in fromMaybe (7 * 24 * 60 * 60) (fmap (read . filter isDigit . ('0' :) . BC.unpack) $ age)
osm-download.cabal view
@@ -1,58 +1,36 @@--- osm-download.cabal auto-generated by cabal init. For additional--- options, see--- http://www.haskell.org/cabal/release/cabal-latest/doc/users-guide/authors.html#pkg-descr.--- The name of the package. Name:                osm-download---- The package version. See the Haskell package versioning policy--- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for--- standards guiding when and how versions should be incremented.-Version:             0.1.2---- A short (one-line) description of the package.+Version:             0.2 Synopsis:            Download Open Street Map tiles---- A longer description of the package.--- Description:         ---- The license under which the package is released.+Description:         Download and locally cache open street map tiles based on HTTP+                     cache control headers. License:             BSD3---- The file containing the license text. License-file:        LICENSE---- The package author(s). Author:              Rob Stewart, Thomas M. DuBuisson---- An email address to which users can send suggestions, bug reports,--- and patches. Maintainer:          Thomas.DuBuisson@gmail.com---- A copyright notice.--- Copyright:           -+-- Copyright: Category:            Network- Build-type:          Simple---- Extra files to be distributed with the package, such as examples or--- a README. -- Extra-source-files:  ---- Constraint on the version of Cabal needed to build this package.-Cabal-version:       >=1.2-+Cabal-version:       >=1.6  Library-  -- Modules exported by the library.   Exposed-modules:     Network.OSM-  -  -- Packages needed in order to build this package.-  Build-depends:       base >= 4 && < 5, http-conduit, bytestring, gps-  -  -- Modules not exported by this package.-  -- Other-modules:       -  +  Build-depends:       base >= 4 && < 5+                     , conduit >= 0.0 && < 0.1+                     , http-conduit >= 1.1 && < 1.2+                     , http-types >= 0.6 && < 0.7+                     , bytestring, gps >= 1.0.1 && < 1.1+                     , containers >= 0.3 && < 0.5+                     , acid-state >= 0.6 && < 0.7+                     , safecopy >= 0.6 && < 0.7+                     , mtl >= 2.0 && < 2.1+                     , transformers-base >= 0.4 && < 0.5+                     , transformers >= 0.2 && < 0.3+                     , time >= 1.2 && < 1.3+                     , stm >= 2.2 && < 2.3 +                     , monadIO >= 0.10 && < 0.11+                     , stm-chans >= 1.2 && < 1.3+  Other-modules:      Paths_osm_download    -- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source.   -- Build-tools: