1
1
mirror of https://github.com/Pygmalion69/OpenTopoMapViewer.git synced 2025-10-06 00:02:42 +02:00

Refactor ORS client

This commit is contained in:
Pygmalion69
2025-08-29 12:37:01 +02:00
parent c7c5a246d4
commit d56e1b1797
31 changed files with 749 additions and 322 deletions

View File

@@ -45,15 +45,16 @@ import org.nitri.opentopo.viewmodel.GpxViewModel
import org.nitri.opentopo.nearby.NearbyFragment
import org.nitri.opentopo.nearby.entity.NearbyItem
import org.nitri.opentopo.util.Utils
import org.nitri.ors.api.OpenRouteServiceApi
import org.nitri.ors.client.OpenRouteServiceClient
import org.osmdroid.util.GeoPoint
import androidx.core.net.toUri
import org.nitri.ors.DefaultOrsClient
import org.nitri.ors.Ors
import org.nitri.ors.OrsClient
open class BaseMainActivity : AppCompatActivity(), MapFragment.OnFragmentInteractionListener,
GpxDetailFragment.OnFragmentInteractionListener, NearbyFragment.OnFragmentInteractionListener {
private val parser = GPXParser()
private var openRouteServiceApi: OpenRouteServiceApi? = null
private var orsClient: OrsClient? = null
private var geoPointFromIntent: GeoPointDto? = null
private var gpxUriString: String? = null
private var gpxUri: Uri? = null
@@ -83,7 +84,7 @@ open class BaseMainActivity : AppCompatActivity(), MapFragment.OnFragmentInterac
private val orsApiKeyChangesReceiver = object: BroadcastReceiver() {
override fun onReceive(p0: Context?, intent: Intent?) {
createOrsApi()
createOrsClient()
}
}
@@ -95,8 +96,8 @@ open class BaseMainActivity : AppCompatActivity(), MapFragment.OnFragmentInterac
// NOP
}
override fun getOpenRouteServiceApi(): OpenRouteServiceApi? {
return openRouteServiceApi
override fun getOpenRouteServiceClient(): OrsClient? {
return orsClient
}
private lateinit var sharedPreferences: SharedPreferences
@@ -182,13 +183,13 @@ open class BaseMainActivity : AppCompatActivity(), MapFragment.OnFragmentInterac
// Log.d("ORS", "Distance: ${result.routes.firstOrNull()?.summary?.distance} m")
// }
createOrsApi()
createOrsClient()
}
private fun createOrsApi() {
private fun createOrsClient() {
val apiKey = sharedPreferences.getString(PREF_ORS_API_KEY, "")
if (apiKey?.isNotEmpty() == true) {
openRouteServiceApi = OpenRouteServiceClient.create(apiKey, this@BaseMainActivity)
orsClient = Ors.create(apiKey, applicationContext)
}
}

View File

@@ -52,7 +52,7 @@ import org.nitri.opentopo.util.OrientationSensor
import org.nitri.opentopo.util.Utils
import org.nitri.opentopo.viewmodel.LocationViewModel
import org.nitri.opentopo.viewmodel.MarkerViewModel
import org.nitri.ors.api.OpenRouteServiceApi
import org.nitri.ors.OrsClient
import org.osmdroid.config.Configuration
import org.osmdroid.events.DelayedMapListener
import org.osmdroid.events.MapEventsReceiver
@@ -398,10 +398,10 @@ class MapFragment : Fragment(), LocationListener, PopupMenu.OnMenuItemClickListe
}
val locale = Resources.getSystem().configuration.locales.get(0)
val language = locale.toLanguageTag().lowercase()
listener?.getOpenRouteServiceApi()?.let { api ->
listener?.getOpenRouteServiceClient()?.let { client ->
val profile = sharedPreferences.getString(PREF_ORS_PROFILE, "driving-car")
profile?.let {
val directions = Directions(api, it)
val directions = Directions(client, it)
directions.getRouteGpx(coordinates, language, object : Directions.RouteGpResult {
override fun onSuccess(gpx: String) {
Log.d(TAG, "GPX: $gpx")
@@ -1060,9 +1060,9 @@ class MapFragment : Fragment(), LocationListener, PopupMenu.OnMenuItemClickListe
fun showPrivacyOptionsForm()
/**
* Get the ORS API if available
* Get the ORS client if available
*/
fun getOpenRouteServiceApi(): OpenRouteServiceApi?
fun getOpenRouteServiceClient(): OrsClient?
/**
* Parse GPX string

View File

@@ -4,17 +4,17 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.nitri.ors.api.OpenRouteServiceApi
import org.nitri.ors.repository.RouteRepository
import org.nitri.ors.OrsClient
import org.nitri.ors.helper.RouteHelper
class Directions(val api: OpenRouteServiceApi, private val profile: String) {
class Directions(val client: OrsClient, private val profile: String) {
val repository = RouteRepository(api)
val routeHelper = RouteHelper(client)
fun getRouteGpx(coordinates: List<List<Double>>, language: String, result: RouteGpResult) {
CoroutineScope(Dispatchers.IO).launch {
try {
val gpxXml = repository.getRouteGpx(coordinates, language, profile)
val gpxXml = with(routeHelper) { client.getRouteGpx(coordinates, language, profile) }
withContext(Dispatchers.Main) {
if (gpxXml.isNotBlank()) {
result.onSuccess(gpxXml)

View File

@@ -9,7 +9,7 @@ plugins {
android {
namespace = "org.nitri.ors"
compileSdk = 35
compileSdk = 36
defaultConfig {
minSdk = 24
@@ -42,6 +42,8 @@ android {
lint {
targetSdk = 35
}
publishing { singleVariant("release") { withSourcesJar() } }
}
dependencies {

View File

@@ -9,28 +9,28 @@ import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.nitri.ors.client.OpenRouteServiceClient
import org.nitri.ors.repository.ElevationRepository
import org.nitri.ors.helper.ElevationHelper
@RunWith(AndroidJUnit4::class)
class ElevationInstrumentedTest {
private fun createRepository(context: Context): ElevationRepository {
private fun create(context: Context): Pair<DefaultOrsClient, ElevationHelper> {
val apiKey = context.getString(R.string.ors_api_key)
val api = OpenRouteServiceClient.create(apiKey, context)
return ElevationRepository(api)
val client = DefaultOrsClient(apiKey, context)
val helper = ElevationHelper(client)
return client to helper
}
@Test
fun testElevation_point_successful() = runBlocking {
val context = ApplicationProvider.getApplicationContext<Context>()
val repository = createRepository(context)
val (client, helper) = create(context)
// A point near Heidelberg, Germany
val lon = 8.681495
val lat = 49.41461
val response = repository.getElevationPoint(lon = lon, lat = lat)
val response = with(helper) { client.getElevationPoint(lon = lon, lat = lat) }
assertNotNull("Elevation point response should not be null", response)
assertNotNull("Geometry should not be null", response.geometry)
@@ -49,7 +49,7 @@ class ElevationInstrumentedTest {
@Test
fun testElevation_line_successful() = runBlocking {
val context = ApplicationProvider.getApplicationContext<Context>()
val repository = createRepository(context)
val (client, helper) = create(context)
// A short line segment around Heidelberg
val coordinates = listOf(
@@ -57,7 +57,7 @@ class ElevationInstrumentedTest {
listOf(8.687872, 49.420318)
)
val response = repository.getElevationLine(coordinates = coordinates)
val response = with(helper) { client.getElevationLine(coordinates = coordinates) }
assertNotNull("Elevation line response should not be null", response)
assertEquals("Geometry type should be LineString", "LineString", response.geometry.type)

View File

@@ -8,17 +8,16 @@ import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.nitri.ors.client.OpenRouteServiceClient
import org.nitri.ors.model.export.ExportRequest
import org.nitri.ors.repository.ExportRepository
import org.nitri.ors.restclient.OpenRouteServiceRestClient
import org.nitri.ors.helper.ExportHelper
@RunWith(AndroidJUnit4::class)
class ExportRepositoryInstrumentedTest {
private fun createRepository(context: Context): ExportRepository {
private fun createRepository(context: Context): ExportHelper {
val apiKey = context.getString(R.string.ors_api_key)
val api = OpenRouteServiceClient.create(apiKey, context)
return ExportRepository(api)
val api = OpenRouteServiceRestClient.create(apiKey, context)
return ExportHelper(api)
}
@Test

View File

@@ -8,29 +8,31 @@ import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.nitri.ors.client.OpenRouteServiceClient
import org.nitri.ors.repository.GeocodeRepository
import org.nitri.ors.helper.GeocodeHelper
@RunWith(AndroidJUnit4::class)
class GeocodeInstrumentedTest {
private fun createRepository(context: Context): GeocodeRepository {
private fun create(context: Context): Pair<DefaultOrsClient, GeocodeHelper> {
val apiKey = context.getString(R.string.ors_api_key)
val api = OpenRouteServiceClient.create(apiKey, context)
return GeocodeRepository(api)
val client = DefaultOrsClient(apiKey, context)
val helper = GeocodeHelper(client)
return client to helper
}
@Test
fun testGeocode_search_successful() = runBlocking {
val context = ApplicationProvider.getApplicationContext<Context>()
val repo = createRepository(context)
val (client, helper) = create(context)
val apiKey = context.getString(R.string.ors_api_key)
val response = repo.search(
text = "Heidelberg",
apiKey = apiKey,
size = 5
)
val response = with(helper) {
client.search(
text = "Heidelberg",
apiKey = apiKey,
size = 5
)
}
assertNotNull("Search response should not be null", response)
assertTrue("Features should not be empty for Heidelberg search", response.features.isNotEmpty())
@@ -49,19 +51,21 @@ class GeocodeInstrumentedTest {
@Test
fun testGeocode_reverse_successful() = runBlocking {
val context = ApplicationProvider.getApplicationContext<Context>()
val repo = createRepository(context)
val (client, helper) = create(context)
val apiKey = context.getString(R.string.ors_api_key)
// Point near Heidelberg, Germany
val lon = 8.681495
val lat = 49.41461
val response = repo.reverse(
apiKey = apiKey,
lon = lon,
lat = lat,
size = 5
)
val response = with(helper) {
client.reverse(
apiKey = apiKey,
lon = lon,
lat = lat,
size = 5
)
}
assertNotNull("Reverse response should not be null", response)
assertTrue("Reverse should return at least one feature", response.features.isNotEmpty())
@@ -74,14 +78,16 @@ class GeocodeInstrumentedTest {
@Test
fun testGeocode_autocomplete_successful() = runBlocking {
val context = ApplicationProvider.getApplicationContext<Context>()
val repo = createRepository(context)
val (client, helper) = create(context)
val apiKey = context.getString(R.string.ors_api_key)
val response = repo.autocomplete(
apiKey = apiKey,
text = "Heidelb",
size = 5
)
val response = with(helper) {
client.autocomplete(
apiKey = apiKey,
text = "Heidelb",
size = 5
)
}
assertNotNull("Autocomplete response should not be null", response)
assertTrue("Autocomplete should return suggestions", response.features.isNotEmpty())

View File

@@ -8,22 +8,22 @@ import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.nitri.ors.client.OpenRouteServiceClient
import org.nitri.ors.repository.IsochronesRepository
import org.nitri.ors.helper.IsochronesHelper
@RunWith(AndroidJUnit4::class)
class IsochronesInstrumentedTest {
private fun createRepository(context: Context): IsochronesRepository {
private fun create(context: Context): Pair<DefaultOrsClient, IsochronesHelper> {
val apiKey = context.getString(R.string.ors_api_key)
val api = OpenRouteServiceClient.create(apiKey, context)
return IsochronesRepository(api)
val client = DefaultOrsClient(apiKey, context)
val helper = IsochronesHelper(client)
return client to helper
}
@Test
fun testIsochrones_successful() = runBlocking {
val context = ApplicationProvider.getApplicationContext<Context>()
val repository = createRepository(context)
val (client, helper) = create(context)
// Heidelberg, Germany [lon, lat]
val locations = listOf(
@@ -33,13 +33,15 @@ class IsochronesInstrumentedTest {
val range = listOf(300)
val profile = "driving-car"
val response = repository.getIsochrones(
locations = locations,
range = range,
profile = profile,
attributes = null,
rangeType = "time"
)
val response = with(helper) {
client.getIsochrones(
locations = locations,
range = range,
profile = profile,
attributes = null,
rangeType = "time"
)
}
assertNotNull("Isochrones response should not be null", response)
assertTrue("Features should not be empty", response.features.isNotEmpty())

View File

@@ -9,22 +9,22 @@ import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.nitri.ors.client.OpenRouteServiceClient
import org.nitri.ors.repository.MatrixRepository
import org.nitri.ors.helper.MatrixHelper
@RunWith(AndroidJUnit4::class)
class MatrixInstrumentedTest {
private fun createRepository(context: Context): MatrixRepository {
private fun create(context: Context): Pair<DefaultOrsClient, MatrixHelper> {
val apiKey = context.getString(R.string.ors_api_key)
val api = OpenRouteServiceClient.create(apiKey, context)
return MatrixRepository(api)
val client = DefaultOrsClient(apiKey, context)
val helper = MatrixHelper(client)
return client to helper
}
@Test
fun testMatrix_successful() = runBlocking {
val context = ApplicationProvider.getApplicationContext<Context>()
val repository = createRepository(context)
val (client, helper) = create(context)
// Two locations in/near Heidelberg, Germany [lon, lat]
val locations = listOf(
@@ -34,12 +34,14 @@ class MatrixInstrumentedTest {
val profile = "driving-car"
val metrics = listOf("duration", "distance")
val response = repository.getMatrix(
locations = locations,
profile = profile,
metrics = metrics,
resolveLocations = false
)
val response = with(helper) {
client.getMatrix(
locations = locations,
profile = profile,
metrics = metrics,
resolveLocations = false
)
}
assertNotNull("Matrix response should not be null", response)

View File

@@ -8,24 +8,24 @@ import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.nitri.ors.client.OpenRouteServiceClient
import org.nitri.ors.model.optimization.Job
import org.nitri.ors.model.optimization.Vehicle
import org.nitri.ors.repository.OptimizationRepository
import org.nitri.ors.helper.OptimizationHelper
@RunWith(AndroidJUnit4::class)
class OptimizationInstrumentedTest {
private fun createRepository(context: Context): OptimizationRepository {
private fun create(context: Context): Pair<DefaultOrsClient, OptimizationHelper> {
val apiKey = context.getString(R.string.ors_api_key)
val api = OpenRouteServiceClient.create(apiKey, context)
return OptimizationRepository(api)
val client = DefaultOrsClient(apiKey, context)
val helper = OptimizationHelper(client)
return client to helper
}
@Test
fun testOptimization_successful() = runBlocking {
val context = ApplicationProvider.getApplicationContext<Context>()
val repository = createRepository(context)
val (client, helper) = create(context)
// Simple scenario in/near Heidelberg, Germany
val vehicle = Vehicle(
@@ -47,10 +47,12 @@ class OptimizationInstrumentedTest {
)
)
val response = repository.getOptimization(
vehicles = listOf(vehicle),
jobs = jobs
)
val response = with(helper) {
client.getOptimization(
vehicles = listOf(vehicle),
jobs = jobs
)
}
// Basic assertions
assertNotNull("Optimization response should not be null", response)

View File

@@ -9,22 +9,22 @@ import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.nitri.ors.client.OpenRouteServiceClient
import org.nitri.ors.repository.PoisRepository
import org.nitri.ors.helper.PoisHelper
@RunWith(AndroidJUnit4::class)
class PoisInstrumentedTest {
private fun createRepository(context: Context): PoisRepository {
private fun create(context: Context): Pair<DefaultOrsClient, PoisHelper> {
val apiKey = context.getString(R.string.ors_api_key)
val api = OpenRouteServiceClient.create(apiKey, context)
return PoisRepository(api)
val client = DefaultOrsClient(apiKey, context)
val helper = PoisHelper(client)
return client to helper
}
@Test
fun testPois_byBbox_successful() = runBlocking {
val context = ApplicationProvider.getApplicationContext<Context>()
val repository = createRepository(context)
val (client, helper) = create(context)
// Bounding box around Heidelberg, Germany
val bbox = listOf(
@@ -32,10 +32,12 @@ class PoisInstrumentedTest {
listOf(8.70, 49.43) // maxLon, maxLat
)
val response = repository.getPoisByBbox(
bbox = bbox,
limit = 10
)
val response = with(helper) {
client.getPoisByBbox(
bbox = bbox,
limit = 10
)
}
assertNotNull("POIs response should not be null", response)
assertEquals("GeoJSON type should be FeatureCollection", "FeatureCollection", response.type)

View File

@@ -4,34 +4,31 @@ import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.runBlocking
import okhttp3.ResponseBody
import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
import org.nitri.ors.client.OpenRouteServiceClient
import org.nitri.ors.model.route.GeoJsonRouteResponse
import org.nitri.ors.repository.ExportRepository
import org.nitri.ors.repository.RouteRepository
import retrofit2.Response
import org.nitri.ors.helper.RouteHelper
@RunWith(AndroidJUnit4::class)
class RouteRepositoryInstrumentedTest {
private fun createRepository(context: Context): RouteRepository {
private fun create(context: Context): Pair<DefaultOrsClient, RouteHelper> {
val apiKey = context.getString(R.string.ors_api_key)
val api = OpenRouteServiceClient.create(apiKey, context)
return RouteRepository(api)
val client = DefaultOrsClient(apiKey, context)
val repo = RouteHelper(client)
return client to repo
}
@Test
fun testFetchRoute_successful() = runBlocking {
val context = ApplicationProvider.getApplicationContext<Context>()
val repository = createRepository(context)
val (client, repository) = create(context)
val start = Pair(8.681495, 49.41461)
val end = Pair(8.687872, 49.420318)
val route = repository.getRoute(start, end, "driving-car")
val route = with(repository) { client.getRoute(start, end, "driving-car") }
assertNotNull("Route should not be null", route)
}
@@ -39,12 +36,12 @@ class RouteRepositoryInstrumentedTest {
@Test
fun testFetchGpxRoute_successful() = runBlocking {
val context = ApplicationProvider.getApplicationContext<Context>()
val repository = createRepository(context)
val (client, repository) = create(context)
val start = Pair(8.681495, 49.41461)
val end = Pair(8.687872, 49.420318)
val gpxXml = repository.getRouteGpx(start, end,"driving-car")
val gpxXml = with(repository) { client.getRouteGpx(start, end, "driving-car") }
assertNotNull("GPX response body should not be null", gpxXml)
assert(gpxXml.contains("<gpx")) { "Response does not appear to be valid GPX" }
@@ -53,12 +50,12 @@ class RouteRepositoryInstrumentedTest {
@Test
fun testFetchGeoJsonRoute_successful() = runBlocking {
val context = ApplicationProvider.getApplicationContext<Context>()
val repository = createRepository(context)
val (client, repository) = create(context)
val start = Pair(8.681495, 49.41461)
val end = Pair(8.687872, 49.420318)
val route: GeoJsonRouteResponse = repository.getRouteGeoJson(start, end, "driving-car")
val route: GeoJsonRouteResponse = with(repository) { client.getRouteGeoJson(start, end, "driving-car") }
assertNotNull("Route should not be null", route)
}

View File

@@ -9,22 +9,22 @@ import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.nitri.ors.client.OpenRouteServiceClient
import org.nitri.ors.repository.SnapRepository
import org.nitri.ors.helper.SnapHelper
@RunWith(AndroidJUnit4::class)
class SnapInstrumentedTest {
private fun createRepository(context: Context): SnapRepository {
private fun create(context: Context): Pair<DefaultOrsClient, SnapHelper> {
val apiKey = context.getString(R.string.ors_api_key)
val api = OpenRouteServiceClient.create(apiKey, context)
return SnapRepository(api)
val client = DefaultOrsClient(apiKey, context)
val helper = SnapHelper(client)
return client to helper
}
@Test
fun testSnap_successful() = runBlocking {
val context = ApplicationProvider.getApplicationContext<Context>()
val repository = createRepository(context)
val (client, helper) = create(context)
// Two locations in/near Heidelberg, Germany [lon, lat]
val locations = listOf(
@@ -34,12 +34,14 @@ class SnapInstrumentedTest {
val profile = "driving-car"
val radius = 50 // meters
val response = repository.getSnap(
locations = locations,
radius = radius,
profile = profile,
id = "snap_test"
)
val response = with(helper) {
client.getSnap(
locations = locations,
radius = radius,
profile = profile,
id = "snap_test"
)
}
assertNotNull("Snap response should not be null", response)
assertNotNull("Metadata should be present", response.metadata)
@@ -55,7 +57,7 @@ class SnapInstrumentedTest {
@Test
fun testSnapJson_successful() = runBlocking {
val context = ApplicationProvider.getApplicationContext<Context>()
val repository = createRepository(context)
val (client, helper) = create(context)
val locations = listOf(
listOf(8.681495, 49.41461),
@@ -64,12 +66,14 @@ class SnapInstrumentedTest {
val profile = "driving-car"
val radius = 50
val response = repository.getSnapJson(
locations = locations,
radius = radius,
profile = profile,
id = "snap_json_test"
)
val response = with(repository) {
client.getSnapJson(
locations = locations,
radius = radius,
profile = profile,
id = "snap_json_test"
)
}
assertNotNull("Snap JSON response should not be null", response)
assertNotNull("Metadata should be present", response.metadata)
@@ -83,7 +87,7 @@ class SnapInstrumentedTest {
@Test
fun testSnapGeoJson_successful() = runBlocking {
val context = ApplicationProvider.getApplicationContext<Context>()
val repository = createRepository(context)
val (client, repository) = create(context)
val locations = listOf(
listOf(8.681495, 49.41461),
@@ -92,12 +96,14 @@ class SnapInstrumentedTest {
val profile = "driving-car"
val radius = 50
val response = repository.getSnapGeoJson(
locations = locations,
radius = radius,
profile = profile,
id = "snap_geojson_test"
)
val response = with(repository) {
client.getSnapGeoJson(
locations = locations,
radius = radius,
profile = profile,
id = "snap_geojson_test"
)
}
assertNotNull("Snap GeoJSON response should not be null", response)
assertEquals("GeoJSON type should be FeatureCollection", "FeatureCollection", response.type)

View File

@@ -0,0 +1,274 @@
package org.nitri.ors
import android.content.Context
import org.nitri.ors.model.elevation.ElevationLineRequest
import org.nitri.ors.model.elevation.ElevationLineResponse
import org.nitri.ors.model.elevation.ElevationPointRequest
import org.nitri.ors.model.elevation.ElevationPointResponse
import org.nitri.ors.model.export.ExportRequest
import org.nitri.ors.model.export.ExportResponse
import org.nitri.ors.model.export.TopoJsonExportResponse
import org.nitri.ors.model.geocode.GeocodeSearchResponse
import org.nitri.ors.model.isochrones.IsochronesRequest
import org.nitri.ors.model.isochrones.IsochronesResponse
import org.nitri.ors.model.matrix.MatrixRequest
import org.nitri.ors.model.matrix.MatrixResponse
import org.nitri.ors.model.optimization.OptimizationRequest
import org.nitri.ors.model.optimization.OptimizationResponse
import org.nitri.ors.model.pois.PoisGeoJsonResponse
import org.nitri.ors.model.pois.PoisRequest
import org.nitri.ors.model.route.GeoJsonRouteResponse
import org.nitri.ors.model.route.RouteRequest
import org.nitri.ors.model.route.RouteResponse
import org.nitri.ors.model.snap.SnapGeoJsonResponse
import org.nitri.ors.model.snap.SnapRequest
import org.nitri.ors.model.snap.SnapResponse
import org.nitri.ors.restclient.OpenRouteServiceRestClient
class DefaultOrsClient(apiKey: String, context: Context) : OrsClient {
private val api = OpenRouteServiceRestClient.create(apiKey, context)
override suspend fun getRoute(
profile: Profile,
routeRequest: RouteRequest
): RouteResponse {
return api.getRoute(profile.key, routeRequest)
}
override suspend fun getRouteGpx(
profile: Profile,
routeRequest: RouteRequest
): String {
return api.getRouteGpx(profile.key, routeRequest).body()?.string() ?: ""
}
override suspend fun getRouteGeoJson(
profile: String,
routeRequest: RouteRequest
): GeoJsonRouteResponse {
return api.getRouteGeoJson(profile, routeRequest)
}
override suspend fun export(
profile: String,
exportRequest: ExportRequest
): ExportResponse {
return api.export(profile, exportRequest)
}
override suspend fun exportJson(
profile: String,
exportRequest: ExportRequest
): ExportResponse {
return api.exportJson(profile, exportRequest)
}
override suspend fun exportTopoJson(
profile: String,
exportRequest: ExportRequest
): TopoJsonExportResponse {
return api.exportTopoJson(profile, exportRequest)
}
override suspend fun getIsochrones(
profile: String,
isochronesRequest: IsochronesRequest
): IsochronesResponse {
return api.getIsochrones(profile, isochronesRequest)
}
override suspend fun getMatrix(
profile: String,
matrixRequest: MatrixRequest
): MatrixResponse {
return api.getMatrix(profile, matrixRequest)
}
override suspend fun getSnap(
profile: String,
snapRequest: SnapRequest
): SnapResponse {
return api.getSnap(profile, snapRequest)
}
override suspend fun getSnapJson(
profile: String,
snapRequest: SnapRequest
): SnapResponse {
return api.getSnapJson(profile, snapRequest)
}
override suspend fun getSnapGeoJson(
profile: String,
snapRequest: SnapRequest
): SnapGeoJsonResponse {
return api.getSnapGeoJson(profile, snapRequest)
}
override suspend fun getPois(poisRequest: PoisRequest): PoisGeoJsonResponse {
return api.getPois(poisRequest)
}
override suspend fun getOptimization(optimizationRequest: OptimizationRequest): OptimizationResponse {
return api.getOptimization(optimizationRequest)
}
override suspend fun getElevationLine(
elevationLineRequest: ElevationLineRequest
): ElevationLineResponse {
return api.getElevationLine(elevationLineRequest)
}
override suspend fun getElevationPoint(
elevationPointRequest: ElevationPointRequest
): ElevationPointResponse {
return api.getElevationPoint(elevationPointRequest)
}
override suspend fun geocodeSearch(
text: String,
apiKey: String,
focusLon: Double?,
focusLat: Double?,
rectMinLon: Double?,
rectMinLat: Double?,
rectMaxLon: Double?,
rectMaxLat: Double?,
circleLon: Double?,
circleLat: Double?,
circleRadiusMeters: Double?,
boundaryGid: String?,
boundaryCountry: String?,
sourcesCsv: String?,
layersCsv: String?,
size: Int?
): GeocodeSearchResponse {
return api.geocodeSearch(
text = text,
focusLon = focusLon,
focusLat = focusLat,
rectMinLon = rectMinLon,
rectMinLat = rectMinLat,
rectMaxLon = rectMaxLon,
rectMaxLat = rectMaxLat,
circleLon = circleLon,
circleLat = circleLat,
circleRadiusMeters = circleRadiusMeters,
boundaryGid = boundaryGid,
boundaryCountry = boundaryCountry,
sourcesCsv = sourcesCsv,
layersCsv = layersCsv,
size = size,
apiKey = apiKey
)
}
override suspend fun geocodeAutocomplete(
apiKey: String,
text: String,
focusLon: Double?,
focusLat: Double?,
rectMinLon: Double?,
rectMinLat: Double?,
rectMaxLon: Double?,
rectMaxLat: Double?,
circleLon: Double?,
circleLat: Double?,
circleRadius: Double?,
country: String?,
sources: List<String>?,
layers: List<String>?,
size: Int?
): GeocodeSearchResponse {
return api.autocomplete(
apiKey = apiKey,
text = text,
focusLon = focusLon,
focusLat = focusLat,
rectMinLon = rectMinLon,
rectMinLat = rectMinLat,
rectMaxLon = rectMaxLon,
rectMaxLat = rectMaxLat,
circleLon = circleLon,
circleLat = circleLat,
circleRadius = circleRadius,
country = country,
sources = sources,
layers = layers,
size = size
)
}
override suspend fun geocodeStructured(
apiKey: String,
address: String?,
neighbourhood: String?,
borough: String?,
locality: String?,
county: String?,
region: String?,
country: String?,
postalcode: String?,
focusLon: Double?,
focusLat: Double?,
rectMinLon: Double?,
rectMinLat: Double?,
rectMaxLon: Double?,
rectMaxLat: Double?,
circleLon: Double?,
circleLat: Double?,
circleRadiusMeters: Double?,
boundaryCountry: String?,
layers: List<String>?,
sources: List<String>?,
size: Int?
): GeocodeSearchResponse {
return api.geocodeStructured(
apiKey = apiKey,
address = address,
neighbourhood = neighbourhood,
borough = borough,
locality = locality,
county = county,
region = region,
country = country,
postalcode = postalcode,
focusLon = focusLon,
focusLat = focusLat,
rectMinLon = rectMinLon,
rectMinLat = rectMinLat,
rectMaxLon = rectMaxLon,
rectMaxLat = rectMaxLat,
circleLon = circleLon,
circleLat = circleLat,
circleRadiusMeters = circleRadiusMeters,
boundaryCountry = boundaryCountry,
layers = layers,
sources = sources,
size = size
)
}
override suspend fun geocodeReverse(
apiKey: String,
lon: Double,
lat: Double,
radiusKm: Double?,
size: Int?,
layers: List<String>?,
sources: List<String>?,
boundaryCountry: String?
): GeocodeSearchResponse {
return api.geocodeReverse(
apiKey = apiKey,
lon = lon,
lat = lat,
radiusKm = radiusKm,
size = size,
layers = layers,
sources = sources,
boundaryCountry = boundaryCountry
)
}
}

View File

@@ -1,10 +0,0 @@
package org.nitri.ors
import android.content.Context
import org.nitri.ors.client.OpenRouteServiceClient
import org.nitri.ors.repository.RouteRepository
class OpenRouteService(apiKey: String, context: Context) {
private val api = OpenRouteServiceClient.create(apiKey, context)
val routeRepository = RouteRepository(api)
}

View File

@@ -0,0 +1,9 @@
package org.nitri.ors
import android.content.Context
object Ors {
/** If you already construct the Retrofit API elsewhere, inject it here. */
@JvmStatic
fun create(apiKey: String, context: Context): OrsClient = DefaultOrsClient(apiKey, context)
}

View File

@@ -0,0 +1,145 @@
package org.nitri.ors
import org.nitri.ors.model.elevation.ElevationLineRequest
import org.nitri.ors.model.elevation.ElevationLineResponse
import org.nitri.ors.model.elevation.ElevationPointRequest
import org.nitri.ors.model.elevation.ElevationPointResponse
import org.nitri.ors.model.export.ExportRequest
import org.nitri.ors.model.export.ExportResponse
import org.nitri.ors.model.export.TopoJsonExportResponse
import org.nitri.ors.model.geocode.GeocodeSearchResponse
import org.nitri.ors.model.isochrones.IsochronesRequest
import org.nitri.ors.model.isochrones.IsochronesResponse
import org.nitri.ors.model.matrix.MatrixRequest
import org.nitri.ors.model.matrix.MatrixResponse
import org.nitri.ors.model.optimization.OptimizationRequest
import org.nitri.ors.model.optimization.OptimizationResponse
import org.nitri.ors.model.pois.PoisGeoJsonResponse
import org.nitri.ors.model.pois.PoisRequest
import org.nitri.ors.model.route.GeoJsonRouteResponse
import org.nitri.ors.model.route.RouteRequest
import org.nitri.ors.model.route.RouteResponse
import org.nitri.ors.model.snap.SnapGeoJsonResponse
import org.nitri.ors.model.snap.SnapRequest
import org.nitri.ors.model.snap.SnapResponse
enum class Profile(val key: String) {
DRIVING_CAR("driving-car"),
DRIVING_HGV("driving-hgv"),
CYCLING_REGULAR("cycling-regular"),
CYCLING_ROAD("cycling-road"),
CYCLING_MOUNTAIN("cycling-mountain"),
CYCLING_ELECTRIC("cycling-electric"),
FOOT_WALKING("foot-walking"),
FOOT_HIKING("foot-hiking"),
WHEELCHAIR("wheelchair")
}
interface OrsClient {
// Directions
suspend fun getRoute(profile: Profile, routeRequest: RouteRequest): RouteResponse
suspend fun getRouteGpx(profile: Profile, routeRequest: RouteRequest): String
suspend fun getRouteGeoJson(profile: String, routeRequest: RouteRequest): GeoJsonRouteResponse
// Export
suspend fun export(profile: String, exportRequest: ExportRequest): ExportResponse
suspend fun exportJson(profile: String, exportRequest: ExportRequest): ExportResponse
suspend fun exportTopoJson(profile: String, exportRequest: ExportRequest): TopoJsonExportResponse
// Isochrones
suspend fun getIsochrones(profile: String, isochronesRequest: IsochronesRequest): IsochronesResponse
// Matrix
suspend fun getMatrix(profile: String, matrixRequest: MatrixRequest): MatrixResponse
// Snapping
suspend fun getSnap(profile: String, snapRequest: SnapRequest): SnapResponse
suspend fun getSnapJson(profile: String, snapRequest: SnapRequest): SnapResponse
suspend fun getSnapGeoJson(profile: String, snapRequest: SnapRequest): SnapGeoJsonResponse
// POIs
suspend fun getPois(poisRequest: PoisRequest): PoisGeoJsonResponse
// Optimization
suspend fun getOptimization(optimizationRequest: OptimizationRequest): OptimizationResponse
// Elevation
suspend fun getElevationLine(elevationLineRequest: ElevationLineRequest): ElevationLineResponse
suspend fun getElevationPoint(elevationPointRequest: ElevationPointRequest): ElevationPointResponse
// Geocode
suspend fun geocodeSearch(
text: String,
apiKey: String,
focusLon: Double? = null,
focusLat: Double? = null,
rectMinLon: Double? = null,
rectMinLat: Double? = null,
rectMaxLon: Double? = null,
rectMaxLat: Double? = null,
circleLon: Double? = null,
circleLat: Double? = null,
circleRadiusMeters: Double? = null,
boundaryGid: String? = null,
boundaryCountry: String? = null,
sourcesCsv: String? = null,
layersCsv: String? = null,
size: Int? = 10,
): GeocodeSearchResponse
suspend fun geocodeAutocomplete(
apiKey: String,
text: String,
focusLon: Double? = null,
focusLat: Double? = null,
rectMinLon: Double? = null,
rectMinLat: Double? = null,
rectMaxLon: Double? = null,
rectMaxLat: Double? = null,
circleLon: Double? = null,
circleLat: Double? = null,
circleRadius: Double? = null,
country: String? = null,
sources: List<String>? = null,
layers: List<String>? = null,
size: Int? = null,
): GeocodeSearchResponse
suspend fun geocodeStructured(
apiKey: String,
address: String? = null,
neighbourhood: String? = null,
borough: String? = null,
locality: String? = null,
county: String? = null,
region: String? = null,
country: String? = null,
postalcode: String? = null,
focusLon: Double? = null,
focusLat: Double? = null,
rectMinLon: Double? = null,
rectMinLat: Double? = null,
rectMaxLon: Double? = null,
rectMaxLat: Double? = null,
circleLon: Double? = null,
circleLat: Double? = null,
circleRadiusMeters: Double? = null,
boundaryCountry: String? = null,
layers: List<String>? = null,
sources: List<String>? = null,
size: Int? = null,
): GeocodeSearchResponse
suspend fun geocodeReverse(
apiKey: String,
lon: Double,
lat: Double,
radiusKm: Double? = null,
size: Int? = null,
layers: List<String>? = null,
sources: List<String>? = null,
boundaryCountry: String? = null,
): GeocodeSearchResponse
}

View File

@@ -33,7 +33,6 @@ import retrofit2.http.Query
interface OpenRouteServiceApi {
// Directions
@GET("v2/directions/{profile}")
suspend fun getRouteSimple(
@Path("profile") profile: String,

View File

@@ -1,29 +1,22 @@
package org.nitri.ors.repository
package org.nitri.ors.helper
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import org.nitri.ors.api.OpenRouteServiceApi
import org.nitri.ors.OrsClient
import org.nitri.ors.model.elevation.ElevationLineRequest
import org.nitri.ors.model.elevation.ElevationLineResponse
import org.nitri.ors.model.elevation.ElevationPointRequest
import org.nitri.ors.model.elevation.ElevationPointResponse
class ElevationRepository(private val api: OpenRouteServiceApi) {
/**
* Calls the ORS elevation/line POST endpoint with a prepared request.
*/
suspend fun getElevationLine(request: ElevationLineRequest): ElevationLineResponse =
api.getElevationLine(request)
class ElevationHelper(private val orsClient: OrsClient) {
/**
* Convenience helper to request elevation for a LineString provided as list of [lon, lat] pairs.
* Builds a GeoJSON LineString payload and requests GeoJSON output.
*/
suspend fun getElevationLine(
suspend fun OrsClient.getElevationLine(
coordinates: List<List<Double>>, // [[lon,lat], [lon,lat], ...]
dataset: String? = null,
formatOut: String = "geojson",
@@ -44,7 +37,7 @@ class ElevationRepository(private val api: OpenRouteServiceApi) {
geometry = geometry,
dataset = dataset
)
return api.getElevationLine(request)
return getElevationLine(request)
}
/**
@@ -55,7 +48,7 @@ class ElevationRepository(private val api: OpenRouteServiceApi) {
* @param formatOut either "geojson" or "point"
* @param dataset optional dataset (e.g., "srtm")
*/
suspend fun getElevationPoint(
suspend fun OrsClient.getElevationPoint(
lon: Double,
lat: Double,
formatOut: String = "geojson",
@@ -67,6 +60,6 @@ class ElevationRepository(private val api: OpenRouteServiceApi) {
dataset = dataset,
geometry = listOf(lon, lat)
)
return api.getElevationPoint(request)
return getElevationPoint(request)
}
}

View File

@@ -1,11 +1,11 @@
package org.nitri.ors.repository
package org.nitri.ors.helper
import org.nitri.ors.api.OpenRouteServiceApi
import org.nitri.ors.model.export.ExportRequest
import org.nitri.ors.model.export.ExportResponse
import org.nitri.ors.model.export.TopoJsonExportResponse
class ExportRepository(private val api: OpenRouteServiceApi) {
class ExportHelper(private val api: OpenRouteServiceApi) {
suspend fun export(bbox: List<List<Double>>, geometry: Boolean? = null, profile: String): ExportResponse {
val request = ExportRequest(

View File

@@ -1,21 +1,19 @@
package org.nitri.ors.repository
package org.nitri.ors.helper
import org.nitri.ors.api.OpenRouteServiceApi
import org.nitri.ors.OrsClient
import org.nitri.ors.model.geocode.GeocodeSearchResponse
/**
* Repository for ORS Geocoding endpoints using GET requests only.
*
* Note: ORS Pelias geocoding requires an api_key query parameter even though
* the client also sends an Authorization header. Therefore, methods here accept
* apiKey explicitly and pass it through to the API interface.
* Methods are member extensions on OrsClient and delegate to OrsClient.
*/
class GeocodeRepository(private val api: OpenRouteServiceApi) {
class GeocodeHelper(private val orsClient: OrsClient) {
/**
* Forward geocoding search.
*/
suspend fun search(
suspend fun OrsClient.search(
text: String,
apiKey: String,
focusLon: Double? = null,
@@ -33,8 +31,9 @@ class GeocodeRepository(private val api: OpenRouteServiceApi) {
layersCsv: String? = null,
size: Int? = 10,
): GeocodeSearchResponse {
return api.geocodeSearch(
return geocodeSearch(
text = text,
apiKey = apiKey,
focusLon = focusLon,
focusLat = focusLat,
rectMinLon = rectMinLon,
@@ -48,15 +47,14 @@ class GeocodeRepository(private val api: OpenRouteServiceApi) {
boundaryCountry = boundaryCountry,
sourcesCsv = sourcesCsv,
layersCsv = layersCsv,
size = size,
apiKey = apiKey
size = size
)
}
/**
* Autocomplete search; returns suggestions for a partial query.
*/
suspend fun autocomplete(
suspend fun OrsClient.autocomplete(
apiKey: String,
text: String,
focusLon: Double? = null,
@@ -73,7 +71,7 @@ class GeocodeRepository(private val api: OpenRouteServiceApi) {
layers: List<String>? = null,
size: Int? = null,
): GeocodeSearchResponse {
return api.autocomplete(
return geocodeAutocomplete(
apiKey = apiKey,
text = text,
focusLon = focusLon,
@@ -95,7 +93,7 @@ class GeocodeRepository(private val api: OpenRouteServiceApi) {
/**
* Structured forward geocoding using address fields.
*/
suspend fun structured(
suspend fun OrsClient.structured(
apiKey: String,
address: String? = null,
neighbourhood: String? = null,
@@ -119,7 +117,7 @@ class GeocodeRepository(private val api: OpenRouteServiceApi) {
sources: List<String>? = null,
size: Int? = null,
): GeocodeSearchResponse {
return api.geocodeStructured(
return geocodeStructured(
apiKey = apiKey,
address = address,
neighbourhood = neighbourhood,
@@ -148,7 +146,7 @@ class GeocodeRepository(private val api: OpenRouteServiceApi) {
/**
* Reverse geocoding for a point.
*/
suspend fun reverse(
suspend fun OrsClient.reverse(
apiKey: String,
lon: Double,
lat: Double,
@@ -158,7 +156,7 @@ class GeocodeRepository(private val api: OpenRouteServiceApi) {
sources: List<String>? = null,
boundaryCountry: String? = null,
): GeocodeSearchResponse {
return api.geocodeReverse(
return geocodeReverse(
apiKey = apiKey,
lon = lon,
lat = lat,

View File

@@ -1,14 +1,12 @@
package org.nitri.ors.repository
package org.nitri.ors.helper
import org.nitri.ors.api.OpenRouteServiceApi
import org.nitri.ors.model.export.ExportRequest
import org.nitri.ors.model.export.ExportResponse
import org.nitri.ors.model.export.TopoJsonExportResponse
import org.nitri.ors.OrsClient
import org.nitri.ors.model.isochrones.IsochronesRequest
import org.nitri.ors.model.isochrones.IsochronesResponse
class IsochronesRepository(private val api: OpenRouteServiceApi) {
suspend fun getIsochrones(
class IsochronesHelper(private val orsClient: OrsClient) {
suspend fun OrsClient.getIsochrones(
locations: List<List<Double>>,
range: List<Int>,
profile: String,
@@ -18,7 +16,6 @@ class IsochronesRepository(private val api: OpenRouteServiceApi) {
val request = IsochronesRequest(
locations, range, rangeType, attributes
)
return api.getIsochrones(profile, request)
return getIsochrones(profile, request)
}
}

View File

@@ -0,0 +1,31 @@
package org.nitri.ors.helper
import org.nitri.ors.OrsClient
import org.nitri.ors.model.matrix.MatrixRequest
import org.nitri.ors.model.matrix.MatrixResponse
class MatrixHelper(private val orsClient: OrsClient) {
/**
* Calls the ORS Matrix endpoint for the given profile.
*/
suspend fun OrsClient.getMatrix(
locations: List<List<Double>>,
profile: String,
metrics: List<String>? = null,
sources: List<Int>? = null,
destinations: List<Int>? = null,
resolveLocations: Boolean? = null,
id: String? = null,
): MatrixResponse {
val request = MatrixRequest(
locations = locations,
destinations = destinations,
id = id,
metrics = metrics,
resolveLocations = resolveLocations,
sources = sources
)
return getMatrix(profile, request)
}
}

View File

@@ -1,7 +1,7 @@
package org.nitri.ors.repository
package org.nitri.ors.helper
import kotlinx.serialization.json.JsonElement
import org.nitri.ors.api.OpenRouteServiceApi
import org.nitri.ors.OrsClient
import org.nitri.ors.model.optimization.CustomMatrix
import org.nitri.ors.model.optimization.Job
import org.nitri.ors.model.optimization.OptimizationRequest
@@ -10,24 +10,14 @@ import org.nitri.ors.model.optimization.Shipment
import org.nitri.ors.model.optimization.Vehicle
/**
* Repository for the OpenRouteService Optimization endpoint.
*
* This is a thin wrapper around the Retrofit API, similar to other repositories
* in this package. The repository builds the OptimizationRequest from the
* provided arguments.
* Repository for the OpenRouteService Optimization endpoint using OrsClient.
*/
class OptimizationRepository(private val api: OpenRouteServiceApi) {
class OptimizationHelper(private val orsClient: OrsClient) {
/**
* Calls the ORS Optimization endpoint with provided arguments and builds the request.
*
* @param vehicles Required list of vehicles.
* @param jobs Optional list of jobs.
* @param shipments Optional list of shipments.
* @param matrices Optional custom matrices keyed by profile.
* @param options Optional free-form options.
*/
suspend fun getOptimization(
suspend fun OrsClient.getOptimization(
vehicles: List<Vehicle>,
jobs: List<Job>? = null,
shipments: List<Shipment>? = null,
@@ -41,6 +31,6 @@ class OptimizationRepository(private val api: OpenRouteServiceApi) {
matrices = matrices,
options = options
)
return api.getOptimization(request)
return getOptimization(request)
}
}

View File

@@ -1,12 +1,12 @@
package org.nitri.ors.repository
package org.nitri.ors.helper
import org.nitri.ors.api.OpenRouteServiceApi
import org.nitri.ors.OrsClient
import org.nitri.ors.model.pois.GeoJsonGeometry
import org.nitri.ors.model.pois.Geometry
import org.nitri.ors.model.pois.PoisGeoJsonResponse
import org.nitri.ors.model.pois.PoisRequest
class PoisRepository(private val api: OpenRouteServiceApi) {
class PoisHelper(private val orsClient: OrsClient) {
/**
* Query POIs within a bounding box.
@@ -17,7 +17,7 @@ class PoisRepository(private val api: OpenRouteServiceApi) {
* @param sortby Optional sort field
* @param buffer Optional buffer in meters applied to the geometry
*/
suspend fun getPoisByBbox(
suspend fun OrsClient.getPoisByBbox(
bbox: List<List<Double>>,
filters: Map<String, String>? = null,
limit: Int? = null,
@@ -30,7 +30,7 @@ class PoisRepository(private val api: OpenRouteServiceApi) {
limit = limit,
sortby = sortby
)
return api.getPois(request)
return getPois(request)
}
/**
@@ -39,7 +39,7 @@ class PoisRepository(private val api: OpenRouteServiceApi) {
* @param point [lon, lat]
* @param buffer Buffer radius in meters
*/
suspend fun getPoisByPoint(
suspend fun OrsClient.getPoisByPoint(
point: List<Double>,
buffer: Int,
filters: Map<String, String>? = null,
@@ -55,6 +55,6 @@ class PoisRepository(private val api: OpenRouteServiceApi) {
limit = limit,
sortby = sortby
)
return api.getPois(request)
return getPois(request)
}
}

View File

@@ -0,0 +1,65 @@
package org.nitri.ors.helper
import org.nitri.ors.OrsClient
import org.nitri.ors.Profile
import org.nitri.ors.model.route.GeoJsonRouteResponse
import org.nitri.ors.model.route.RouteRequest
import org.nitri.ors.model.route.RouteResponse
class RouteHelper(private val orsClient: OrsClient) {
private fun profileFromKey(key: String): Profile =
Profile.values().firstOrNull { it.key == key }
?: throw IllegalArgumentException("Unknown profile key: $key")
suspend fun OrsClient.getRoute(
start: Pair<Double, Double>,
end: Pair<Double, Double>,
profile: String
): RouteResponse {
val request = RouteRequest(
coordinates = listOf(
listOf(start.first, start.second),
listOf(end.first, end.second)
)
)
return getRoute(profileFromKey(profile), request)
}
suspend fun OrsClient.getRouteGpx(
start: Pair<Double, Double>,
end: Pair<Double, Double>,
profile: String
): String {
val request = RouteRequest(
coordinates = listOf(
listOf(start.first, start.second),
listOf(end.first, end.second)
)
)
return getRouteGpx(profileFromKey(profile), request)
}
suspend fun OrsClient.getRouteGpx(
coordinates: List<List<Double>>,
language: String,
profile: String
): String {
val request = RouteRequest(coordinates = coordinates, language = language)
return getRouteGpx(profileFromKey(profile), request)
}
suspend fun OrsClient.getRouteGeoJson(
start: Pair<Double, Double>,
end: Pair<Double, Double>,
profile: String
): GeoJsonRouteResponse {
val request = RouteRequest(
coordinates = listOf(
listOf(start.first, start.second),
listOf(end.first, end.second)
)
)
return getRouteGeoJson(profile, request)
}
}

View File

@@ -1,21 +1,16 @@
package org.nitri.ors.repository
package org.nitri.ors.helper
import org.nitri.ors.api.OpenRouteServiceApi
import org.nitri.ors.OrsClient
import org.nitri.ors.model.snap.SnapGeoJsonResponse
import org.nitri.ors.model.snap.SnapRequest
import org.nitri.ors.model.snap.SnapResponse
class SnapRepository(private val api: OpenRouteServiceApi) {
class SnapHelper(private val orsClient: OrsClient) {
/**
* Calls the ORS Snap endpoint for the given profile.
*
* @param locations List of [lon, lat] coordinates to snap.
* @param radius Maximum radius (meters) around given coordinates to search for graph edges.
* @param profile ORS profile, e.g. "driving-car", "foot-hiking", etc.
* @param id Optional arbitrary request id echoed back by the service.
*/
suspend fun getSnap(
suspend fun OrsClient.getSnap(
locations: List<List<Double>>,
radius: Int,
profile: String,
@@ -26,13 +21,13 @@ class SnapRepository(private val api: OpenRouteServiceApi) {
radius = radius,
id = id
)
return api.getSnap(profile, request)
return getSnap(profile, request)
}
/**
* Calls the ORS Snap JSON endpoint.
*/
suspend fun getSnapJson(
suspend fun OrsClient.getSnapJson(
locations: List<List<Double>>,
radius: Int,
profile: String,
@@ -43,13 +38,13 @@ class SnapRepository(private val api: OpenRouteServiceApi) {
radius = radius,
id = id
)
return api.getSnapJson(profile, request)
return getSnapJson(profile, request)
}
/**
* Calls the ORS Snap GeoJSON endpoint.
*/
suspend fun getSnapGeoJson(
suspend fun OrsClient.getSnapGeoJson(
locations: List<List<Double>>,
radius: Int,
profile: String,
@@ -60,6 +55,6 @@ class SnapRepository(private val api: OpenRouteServiceApi) {
radius = radius,
id = id
)
return api.getSnapGeoJson(profile, request)
return getSnapGeoJson(profile, request)
}
}

View File

@@ -1,39 +0,0 @@
package org.nitri.ors.repository
import org.nitri.ors.api.OpenRouteServiceApi
import org.nitri.ors.model.matrix.MatrixRequest
import org.nitri.ors.model.matrix.MatrixResponse
class MatrixRepository(private val api: OpenRouteServiceApi) {
/**
* Calls the ORS Matrix endpoint for the given profile.
*
* @param locations List of [lon, lat] coordinate pairs.
* @param profile ORS profile, e.g. "driving-car", "foot-hiking", etc.
* @param metrics Optional list of metrics to include (e.g., ["duration"], ["distance"], or both).
* @param sources Optional list of indices into locations used as sources.
* @param destinations Optional list of indices into locations used as destinations.
* @param resolveLocations Optional flag to resolve/snaps locations to the network.
* @param id Optional arbitrary request id.
*/
suspend fun getMatrix(
locations: List<List<Double>>,
profile: String,
metrics: List<String>? = null,
sources: List<Int>? = null,
destinations: List<Int>? = null,
resolveLocations: Boolean? = null,
id: String? = null,
): MatrixResponse {
val request = MatrixRequest(
locations = locations,
destinations = destinations,
id = id,
metrics = metrics,
resolveLocations = resolveLocations,
sources = sources
)
return api.getMatrix(profile, request)
}
}

View File

@@ -1,37 +0,0 @@
package org.nitri.ors.repository
import org.nitri.ors.api.OpenRouteServiceApi
import org.nitri.ors.model.route.GeoJsonRouteResponse
import org.nitri.ors.model.route.RouteRequest
import org.nitri.ors.model.route.RouteResponse
class RouteRepository(private val api: OpenRouteServiceApi) {
suspend fun getRoute(start: Pair<Double, Double>, end: Pair<Double, Double>, profile: String): RouteResponse {
val request = RouteRequest(coordinates = listOf(
listOf(start.first, start.second),
listOf(end.first, end.second)
))
return api.getRoute(profile, request)
}
suspend fun getRouteGpx(start: Pair<Double, Double>, end: Pair<Double, Double>, profile: String): String {
val request = RouteRequest(coordinates = listOf(
listOf(start.first, start.second),
listOf(end.first, end.second)
))
return api.getRouteGpx(profile, request).body()?.string() ?: ""
}
suspend fun getRouteGpx(coordinates: List<List<Double>>, language: String, profile: String): String {
val request = RouteRequest(coordinates = coordinates, language = language)
return api.getRouteGpx(profile, request).body()?.string() ?: ""
}
suspend fun getRouteGeoJson(start: Pair<Double, Double>, end: Pair<Double, Double>, profile: String): GeoJsonRouteResponse {
val request = RouteRequest(coordinates = listOf(
listOf(start.first, start.second),
listOf(end.first, end.second)
))
return api.getRouteGeoJson(profile, request)
}
}

View File

@@ -1,4 +1,4 @@
package org.nitri.ors.client
package org.nitri.ors.restclient
import android.content.Context
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
@@ -11,7 +11,7 @@ import org.nitri.ors.api.OpenRouteServiceApi
import okhttp3.MediaType.Companion.toMediaType
import java.util.concurrent.TimeUnit
object OpenRouteServiceClient {
object OpenRouteServiceRestClient {
fun create(apiKey: String, context: Context): OpenRouteServiceApi {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)

View File

@@ -1,28 +1,26 @@
package org.nitri.ors
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.nitri.ors.api.OpenRouteServiceApi
import org.nitri.ors.model.route.Route
import org.nitri.ors.model.route.RouteRequest
import org.nitri.ors.model.route.RouteResponse
import org.nitri.ors.model.route.RouteSummary
import org.nitri.ors.repository.RouteRepository
import org.nitri.ors.helper.RouteHelper
class RouteRepositoryTest {
class RouteHelperTest {
private lateinit var api: OpenRouteServiceApi
private lateinit var repository: RouteRepository
private lateinit var client: OrsClient
private lateinit var routeHelper: RouteHelper
@Before
fun setUp() {
api = mock(OpenRouteServiceApi::class.java)
repository = RouteRepository(api)
client = mock(OrsClient::class.java)
routeHelper = RouteHelper(client)
}
@Test
@@ -49,11 +47,11 @@ class RouteRepositoryTest {
)
)
`when`(api.getRoute(profile, expectedRequest)).thenReturn(expectedResponse)
`when`(client.getRoute(Profile.DRIVING_CAR, expectedRequest)).thenReturn(expectedResponse)
val result = repository.getRoute(start, end, profile)
val result = with(routeHelper) { client.getRoute(start, end, profile) }
assert(result == expectedResponse)
verify(api).getRoute(profile, expectedRequest)
verify(client).getRoute(Profile.DRIVING_CAR, expectedRequest)
}
}