/
osrm_haskell.hs
122 lines (81 loc) · 3.55 KB
/
osrm_haskell.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Foreign.C
import Foreign.Ptr
import Data.Text (Text, unpack)
import Data.Monoid ((<>))
import Control.Exception (bracket)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Cont (ContT(..), runContT)
import Control.Applicative ((<$>), (<*>))
-- Opaque Types
newtype Config = Config { getConfig :: Ptr Config }
newtype OSRM = OSRM { getOSRM :: Ptr OSRM }
newtype RouteParams = RouteParams { getRouteParams :: Ptr RouteParams }
newtype RouteResponse = RouteResponse { getRouteResponse :: Ptr RouteResponse }
-- ABI Stability
foreign import ccall "osrmc_get_version"
getVersion :: CUInt
foreign import ccall "osrmc_is_abi_compatible"
isABICompatible :: CInt
-- API
foreign import ccall "osrmc_config_construct"
constructConfig :: CString -> IO Config
foreign import ccall "osrmc_config_destruct"
destructConfig :: Config -> IO ()
foreign import ccall "osrmc_osrm_construct"
constructOSRM :: Config -> IO OSRM
foreign import ccall "osrmc_osrm_destruct"
destructOSRM :: OSRM -> IO ()
foreign import ccall "osrmc_route_params_construct"
constructRouteParams :: IO RouteParams
foreign import ccall "osrmc_route_params_destruct"
destructRouteParams :: RouteParams -> IO ()
foreign import ccall "osrmc_params_add_coordinate"
addCoordinate :: RouteParams -> Float -> Float -> IO ()
foreign import ccall "osrmc_route"
route :: OSRM -> RouteParams -> IO RouteResponse
foreign import ccall "osrmc_route_response_destruct"
destructRouteResponse :: RouteResponse -> IO ()
foreign import ccall "osrmc_route_response_distance"
distance :: RouteResponse -> IO Float
foreign import ccall "osrmc_route_response_duration"
duration :: RouteResponse -> IO Float
-- Haskell Library Interface
data Coordinate = Coordinate { longitude :: Float
, latitude :: Float} deriving (Show)
withConfig :: Text -> (Config -> IO ()) -> IO ()
withConfig basePath body = bracket construct destruct body
where construct = withCString (unpack basePath) $ constructConfig
destruct = destructConfig
withOSRM :: Config -> (OSRM -> IO ()) -> IO ()
withOSRM config body = bracket construct destruct body
where construct = constructOSRM config
destruct = destructOSRM
withRouteParams :: (RouteParams -> IO ()) -> IO ()
withRouteParams body = bracket construct destruct body
where construct = constructRouteParams
destruct = destructRouteParams
withRoute :: OSRM -> RouteParams -> (RouteResponse -> IO ()) -> IO ()
withRoute osrm params body = bracket construct destruct body
where construct = route osrm params
destruct = destructRouteResponse
-- TODO(daniel-j-h):
-- - Error handling, EitherT
-- - Nice abstractions
main = do
let base = "/tmp/osrm-backend/test/data/monaco.osrm"
let start = Coordinate { longitude=7.419758, latitude=43.731142 }
let end = Coordinate { longitude=7.419505, latitude=43.736825 }
flip runContT return $ do
config <- ContT $ withConfig base
osrm <- ContT $ withOSRM config
params <- ContT $ withRouteParams
liftIO $ addCoordinate params (longitude start) (latitude start)
liftIO $ addCoordinate params (longitude end) (latitude end)
response <- ContT $ withRoute osrm params
dist <- liftIO $ distance response
dura <- liftIO $ duration response
liftIO . putStrLn $ "Distance: " <> (show dist) <> " meters"
liftIO . putStrLn $ "Duration: " <> (show dura) <> " seconds"