Skip to content

Commit fe4faeb

Browse files
committed
Add matrix endpoint
1 parent 44424e6 commit fe4faeb

File tree

5 files changed

+223
-0
lines changed

5 files changed

+223
-0
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package org.nitri.ors
2+
3+
import android.content.Context
4+
import androidx.test.core.app.ApplicationProvider
5+
import androidx.test.ext.junit.runners.AndroidJUnit4
6+
import kotlinx.coroutines.runBlocking
7+
import org.junit.Assert.assertEquals
8+
import org.junit.Assert.assertNotNull
9+
import org.junit.Assert.assertTrue
10+
import org.junit.Test
11+
import org.junit.runner.RunWith
12+
import org.nitri.ors.client.OpenRouteServiceClient
13+
import org.nitri.ors.repository.MatrixRepository
14+
15+
@RunWith(AndroidJUnit4::class)
16+
class MatrixInstrumentedTest {
17+
18+
private fun createRepository(context: Context): MatrixRepository {
19+
val apiKey = context.getString(R.string.ors_api_key)
20+
val api = OpenRouteServiceClient.create(apiKey, context)
21+
return MatrixRepository(api)
22+
}
23+
24+
@Test
25+
fun testMatrix_successful() = runBlocking {
26+
val context = ApplicationProvider.getApplicationContext<Context>()
27+
val repository = createRepository(context)
28+
29+
// Two locations in/near Heidelberg, Germany [lon, lat]
30+
val locations = listOf(
31+
listOf(8.681495, 49.41461), // Heidelberg center
32+
listOf(8.687872, 49.420318) // Nearby point
33+
)
34+
val profile = "driving-car"
35+
val metrics = listOf("duration", "distance")
36+
37+
val response = repository.getMatrix(
38+
locations = locations,
39+
profile = profile,
40+
metrics = metrics,
41+
resolveLocations = false
42+
)
43+
44+
assertNotNull("Matrix response should not be null", response)
45+
46+
// Durations and distances should be present when requested
47+
val durations = response.durations
48+
val distances = response.distances
49+
assertNotNull("Durations should be present when requested", durations)
50+
assertNotNull("Distances should be present when requested", distances)
51+
52+
// Expect a 2x2 matrix for two input points
53+
durations!!
54+
distances!!
55+
assertEquals("Durations should have size equal to number of sources", 2, durations.size)
56+
assertEquals("Distances should have size equal to number of sources", 2, distances.size)
57+
assertEquals("Each durations row should have size equal to number of destinations", 2, durations[0].size)
58+
assertEquals("Each distances row should have size equal to number of destinations", 2, distances[0].size)
59+
60+
// Sanity: diagonal (origin to same point) should be zero or near-zero for distance
61+
assertTrue("Distance from a point to itself should be >= 0", distances[0][0] >= 0.0)
62+
assertTrue("Duration from a point to itself should be >= 0", durations[0][0] >= 0.0)
63+
64+
// Metadata should be included
65+
assertNotNull("Metadata should be present", response.metadata)
66+
}
67+
}

ors-client/src/main/java/org/nitri/ors/api/OpenRouteServiceApi.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package org.nitri.ors.api
22

3+
import org.nitri.ors.model.matrix.MatrixResponse
34
import okhttp3.ResponseBody
45
import org.nitri.ors.model.export.ExportRequest
56
import org.nitri.ors.model.export.ExportResponse
67
import org.nitri.ors.model.export.TopoJsonExportResponse
78
import org.nitri.ors.model.isochrones.IsochronesRequest
89
import org.nitri.ors.model.isochrones.IsochronesResponse
10+
import org.nitri.ors.model.matrix.MatrixRequest
911
import org.nitri.ors.model.route.GeoJsonRouteResponse
1012
import org.nitri.ors.model.route.RouteRequest
1113
import org.nitri.ors.model.route.RouteResponse
@@ -75,5 +77,12 @@ interface OpenRouteServiceApi {
7577
@Body request: IsochronesRequest
7678
): IsochronesResponse
7779

80+
// Matrix endpoint
81+
@POST("v2/matrix/{profile}")
82+
suspend fun getMatrix(
83+
@Path("profile") profile: String,
84+
@Body request: MatrixRequest
85+
): MatrixResponse
86+
7887

7988
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package org.nitri.ors.model.matrix
2+
3+
import kotlinx.serialization.SerialName
4+
import kotlinx.serialization.Serializable
5+
6+
/**
7+
* Represents a request for the matrix service.
8+
*
9+
* @property locations A list of coordinates as `[longitude, latitude]` pairs.
10+
* @property destinations Optional. A list of indices that refers to the list of locations (starting with `0`).
11+
* `{ "locations": [[9.70093,48.477473],[9.207916,49.153868],[37.573242,55.801281],[115.663757,38.106467]], "destinations": [0,1] }`
12+
* will calculate distances from origin 0 to destinations 0 and 1, and from origin 1 to destinations 0 and 1.
13+
* Default is all locations.
14+
* @property id Optional. An arbitrary identification string of the request.
15+
* This field is not used by the service and only returned in the response.
16+
* @property metrics Optional. Specifies a list of returned metrics.
17+
* - `distance` - Returns distance matrix in meters.
18+
* - `duration` - Returns duration matrix in seconds.
19+
* Default is `["duration"]`.
20+
* @property resolveLocations Optional. Specifies whether given locations are resolved or not. If `true`, locations are resolved to the road network,
21+
* if `false` just the locations are used.
22+
* Default is `false`.
23+
* @property sources Optional. A list of indices that refers to the list of locations (starting with `0`).
24+
* `{ "locations": [[9.70093,48.477473],[9.207916,49.153868],[37.573242,55.801281],[115.663757,38.106467]], "sources": [0,1] }`
25+
* will calculate distances from origin 0 to all destinations and from origin 1 to all destinations.
26+
* Default is all locations.
27+
*/
28+
@Serializable
29+
data class MatrixRequest(
30+
31+
/** A list of coordinates as `[longitude, latitude]` pairs. */
32+
val locations: List<List<Double>>,
33+
34+
/** Optional. A list of indices that refers to the list of locations (starting with `0`). */
35+
val destinations: List<Int>? = null,
36+
37+
/** Optional. An arbitrary identification string of the request. */
38+
val id: String? = null,
39+
40+
/** Optional. Specifies a list of returned metrics. */
41+
val metrics: List<String>? = null,
42+
43+
@SerialName("resolve_locations")
44+
/** Optional. Specifies whether given locations are resolved or not. */
45+
val resolveLocations: Boolean? = null,
46+
47+
/** Optional. A list of indices that refers to the list of locations (starting with `0`). */
48+
val sources: List<Int>? = null
49+
)
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package org.nitri.ors.model.matrix
2+
3+
import kotlinx.serialization.SerialName
4+
import kotlinx.serialization.Serializable
5+
import org.nitri.ors.model.meta.Metadata
6+
7+
/**
8+
* Represents the response from an ORS Matrix API request.
9+
*
10+
* The matrix response contains information about travel times or distances
11+
* between a set of source and destination points.
12+
*
13+
* @property durations A list of lists representing the travel duration matrix.
14+
* Present only if 'durations' was requested in the `metrics` parameter.
15+
* The outer list corresponds to sources, and the inner list corresponds to destinations.
16+
* Each value is the duration in seconds.
17+
* @property distances A list of lists representing the travel distance matrix.
18+
* Present only if 'distances' was requested in the `metrics` parameter.
19+
* The outer list corresponds to sources, and the inner list corresponds to destinations.
20+
* Each value is the distance in meters.
21+
* @property sources A list of enriched waypoint information for the source locations.
22+
* This is typically present when `resolve_locations=true` is used in the request,
23+
* but might sometimes be included even if `resolve_locations=false`.
24+
* @property destinations A list of enriched waypoint information for the destination locations.
25+
* This is typically present when `resolve_locations=true` is used in the request,
26+
* but might sometimes be included even if `resolve_locations=false`.
27+
* @property metadata Standard ORS metadata including information about the service, engine, query, etc.
28+
*/
29+
@Serializable
30+
data class MatrixResponse(
31+
// Present only if requested in `metrics`
32+
val durations: List<List<Double>>? = null,
33+
val distances: List<List<Double>>? = null,
34+
35+
// Enriched waypoint info when resolve_locations=true (or sometimes even if false)
36+
val sources: List<MatrixWaypoint>? = null,
37+
val destinations: List<MatrixWaypoint>? = null,
38+
39+
// Standard ORS metadata (service, engine, query, etc.)
40+
val metadata: Metadata? = null
41+
)
42+
/**
43+
* Represents a waypoint used in the ORS Matrix API response, providing details about a specific location.
44+
*
45+
* @property location The coordinates of the waypoint as a list of [longitude, latitude].
46+
* @property name An optional name associated with the waypoint, which ORS may include.
47+
* @property snappedDistance The distance in meters from the input coordinate to the snapped network coordinate.
48+
* This is an optional extra that ORS may include.
49+
*/
50+
@Serializable
51+
data class MatrixWaypoint(
52+
// [lon, lat]
53+
val location: List<Double>,
54+
55+
// Optional extras ORS may include
56+
val name: String? = null,
57+
@SerialName("snapped_distance")
58+
val snappedDistance: Double? = null
59+
)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.nitri.ors.repository
2+
3+
import org.nitri.ors.api.OpenRouteServiceApi
4+
import org.nitri.ors.model.matrix.MatrixRequest
5+
import org.nitri.ors.model.matrix.MatrixResponse
6+
7+
class MatrixRepository(private val api: OpenRouteServiceApi) {
8+
9+
/**
10+
* Calls the ORS Matrix endpoint for the given profile.
11+
*
12+
* @param locations List of [lon, lat] coordinate pairs.
13+
* @param profile ORS profile, e.g. "driving-car", "foot-hiking", etc.
14+
* @param metrics Optional list of metrics to include (e.g., ["duration"], ["distance"], or both).
15+
* @param sources Optional list of indices into locations used as sources.
16+
* @param destinations Optional list of indices into locations used as destinations.
17+
* @param resolveLocations Optional flag to resolve/snaps locations to the network.
18+
* @param id Optional arbitrary request id.
19+
*/
20+
suspend fun getMatrix(
21+
locations: List<List<Double>>,
22+
profile: String,
23+
metrics: List<String>? = null,
24+
sources: List<Int>? = null,
25+
destinations: List<Int>? = null,
26+
resolveLocations: Boolean? = null,
27+
id: String? = null,
28+
): MatrixResponse {
29+
val request = MatrixRequest(
30+
locations = locations,
31+
destinations = destinations,
32+
id = id,
33+
metrics = metrics,
34+
resolveLocations = resolveLocations,
35+
sources = sources
36+
)
37+
return api.getMatrix(profile, request)
38+
}
39+
}

0 commit comments

Comments
 (0)