@@ -10,11 +10,9 @@ import com.valhalla.api.models.CostingOptions
1010import com.valhalla.api.models.DistanceUnit
1111import com.valhalla.api.models.PedestrianCostingOptions
1212import com.valhalla.api.models.RouteRequest
13- import com.valhalla.api.models.RouteResponse
1413import com.valhalla.api.models.RoutingWaypoint
1514import com.valhalla.config.ValhallaConfigBuilder
16- import com.valhalla.valhalla.Valhalla
17- import com.valhalla.valhalla.ValhallaResponse
15+ import com.valhalla.valhalla.config.ValhallaConfigManager
1816import java.io.File
1917
2018/* *
@@ -38,25 +36,47 @@ class ValhallaRouter(private val context: Context) {
3836
3937 private val moshi = Moshi .Builder ().add(KotlinJsonAdapterFactory ()).build()
4038 private val profiles by lazy { ValhallaProfiles (context) }
41-
42- /* * Built once with the tiles directory, the tiles themselves are read on every request */
43- private var valhalla: Valhalla ? = null
39+ private val configManager by lazy { ValhallaConfigManager (context) }
4440
4541 /* *
4642 * Returns the valhalla response json - the web layer decodes the shape and the elevation.
4743 */
4844 fun route (request : ValhallaRouteRequest , tilesDir : File ): String {
49- val response = engine(tilesDir).route(toRouteRequest(request))
50- return when (response) {
51- is ValhallaResponse .Json -> moshi.adapter(RouteResponse ::class .java).toJson(response.jsonResponse)
52- else -> throw IllegalStateException (" Valhalla answered in a format that was not asked for" )
53- }
45+ val requestJson = moshi.adapter(RouteRequest ::class .java).toJson(toRouteRequest(request))
46+ return routeRaw(requestJson, ensureConfig(tilesDir))
5447 }
5548
56- private fun engine (tilesDir : File ): Valhalla {
57- return valhalla
58- ? : Valhalla (context, ValhallaConfigBuilder ().withTileDir(tilesDir.absolutePath).build())
59- .also { valhalla = it }
49+ /* *
50+ * Sends an already serialized request to the native engine and returns its raw response.
51+ *
52+ * `Valhalla.route()` would be the way to do this, but the published valhalla-mobile is built
53+ * against valhalla-models 0.1.1, where `RouteRequest` still had a nested `DirectionsOptions`
54+ * that 0.2.0 flattened away, so calling it against any current models throws
55+ * NoSuchMethodError - see https://github.com/Rallista/valhalla-mobile/issues/60. The request
56+ * above is built and serialized here from the very same models, so nothing is skipped or
57+ * guessed, and this only reaches valhalla's own entry point, which is `internal`.
58+ *
59+ * Once valhalla-mobile is released against current models this whole method goes away, and
60+ * route() above becomes:
61+ * val response = Valhalla(context, config).route(toRouteRequest(request))
62+ */
63+ private fun routeRaw (requestJson : String , configPath : String ): String {
64+ val valhallaKotlinClass = Class .forName(" com.valhalla.valhalla.ValhallaKotlin" )
65+ val valhallaKotlin = valhallaKotlinClass.getDeclaredConstructor().newInstance()
66+ val routeMethod = valhallaKotlinClass.getMethod(" route" , String ::class .java, String ::class .java)
67+ return routeMethod.invoke(valhallaKotlin, requestJson, configPath) as String
68+ }
69+
70+ /* *
71+ * Writes valhalla.json if it isn't there yet. The tiles directory never changes, so the config
72+ * stays valid when more slices are extracted into it.
73+ */
74+ private fun ensureConfig (tilesDir : File ): String {
75+ val configPath = configManager.getAbsolutePath()
76+ if (! File (configPath).exists()) {
77+ configManager.writeConfig(ValhallaConfigBuilder ().withTileDir(tilesDir.absolutePath).build())
78+ }
79+ return configPath
6080 }
6181
6282 private fun toRouteRequest (request : ValhallaRouteRequest ): RouteRequest {
0 commit comments