Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d1ea4ba
fix: use as graph timestamp ORS import date rather than OSM replicati…
aoles Oct 9, 2025
05e2f5d
fix: use correct graph timestamp for matching
takb Oct 14, 2025
70b026d
fix: address remaining timestamp issues
aoles Oct 14, 2025
7ef0556
feat: improved dynamic data update logic
takb Oct 14, 2025
9dcdb23
feat: enable matching to specific feature types such as bridges
aoles Oct 15, 2025
be4b784
feat: add borders to possible feature types for matching
aoles Oct 16, 2025
c393066
refactor: split point matching method into several smaller ones
aoles Oct 16, 2025
cd5ded5
feat: unset deleted mappings in EV
takb Oct 17, 2025
8c26a95
refactor: streamline internal interface of MatchingRequest
aoles Oct 20, 2025
c764d3c
test: add api tests for matching to specific features
aoles Oct 20, 2025
fd7294a
refactor: use parametrized test for matching features
aoles Oct 20, 2025
b6d4c5b
feat: `maximum_search_radius` config parameter to matching endpoint
aoles Nov 7, 2025
b2df0a6
chore: address SonarQube issue
aoles Nov 10, 2025
6ca0424
feat: dynamic data stats on status endpoint
takb Nov 10, 2025
575dd92
fix: Sonar issues
takb Nov 10, 2025
b9d52a9
fix: add LogieRoads.UNSPECIFIED
takb Nov 11, 2025
28c22d7
fix: make DynamicDataService constructor return if disabled
takb Nov 11, 2025
e6f067d
refactor: reduce method complexity to address SonarQube issue
aoles Nov 11, 2025
2bb6f98
fix: make DynamicDataService.update() silent if disabled
takb Nov 13, 2025
4ca0efd
chore(config): automatic conversion of application.yml to ors-config.…
takb Nov 13, 2025
a272d0d
fix: add testcontainers annotations to `status/ResultTest` (#2178)
Copilot Nov 14, 2025
4546a8f
fix: add access modifier to the `ghStorage` field
aoles Nov 14, 2025
22f9b85
fix: Issues raised by copilot
takb Nov 14, 2025
f79d5f8
fix: move tests for the Status Endpoint with DynamicData content
takb Nov 14, 2025
61fb79b
feat: distinguish between missing and unknown `type` property
aoles Nov 16, 2025
be156d1
fix: unsafe type cast of geojson 'features'
aoles Nov 16, 2025
1e24495
docs: add description of matching endpoint
aoles Nov 16, 2025
b60db02
test: modify and add tests for matching service
aoles Nov 17, 2025
4809866
fix: tweaks to error handling and reporting
aoles Nov 17, 2025
b4de4e6
docs: add matching API error codes
aoles Nov 17, 2025
dc953d3
docs: extend matching service description by sample query and hints
aoles Nov 18, 2025
0dc5ae9
feat: Split MatchingService response ids in array of arrays
takb Nov 18, 2025
2ab1866
fix: avoid unnecessary error message in DynamicDataService
takb Nov 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/.vitepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ export default withMermaid(defineVersionedConfig({
{text: 'Isochrones', link: '/api-reference/endpoints/isochrones/'},
{text: 'Matrix', link: '/api-reference/endpoints/matrix/'},
{text: 'Snapping', link: '/api-reference/endpoints/snapping/'},
{text: 'Export (not live)', link: '/api-reference/endpoints/export/'},
{text: 'Export', link: '/api-reference/endpoints/export/'},
{text: 'Matching', link: '/api-reference/endpoints/matching/'},
{text: 'Health (not live)', link: '/api-reference/endpoints/health/'},
{text: 'Status (not live)', link: '/api-reference/endpoints/status/'},
{text: 'POI', link: '/api-reference/endpoints/poi/'},
Expand Down
10 changes: 0 additions & 10 deletions docs/api-reference/endpoints/export/index.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
---
cbf: [2,4,5]
---

# Export Endpoint

:::warning NOTE
This endpoint is not available in the public API,
but you can use it when running an own instance of openrouteservice.
You can easily create requests with the [swagger-ui](/api-reference/index.md#swagger-ui).
:::

Export the base graph for different modes of transport.

In the request, the desired routing profile is specified as the penultimate path parameter, and the desired format as
Expand Down
3 changes: 2 additions & 1 deletion docs/api-reference/endpoints/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ Openrouteservice offers a set of endpoints for different spatial purposes:
* [Isochrones Service](isochrones/index.md): Obtain areas of reachability from given locations
* [Matrix Service](matrix/index.md): Obtain one-to-many, many-to-one and many-to-many matrices for time and distance
* [Snapping Service](snapping/index.md): Snap coordinates to the road network
* [Export Service](export/index.md): Export the routing graph for different modes of transport

## Technical Endpoints

Furthermore, there are technical endpoints that are :warning: _not available_ in our live API:

* [Export Service](export/index.md): Export the base graph for different modes of transport
* [Matching Service](matching/index.md): Match spatial features to the road network
* [Health Service](health/index.md): Get information on the health of the running openrouteservice instance
* [Status Service](status/index.md): Get information on the status of the openrouteservice instance

Expand Down
127 changes: 127 additions & 0 deletions docs/api-reference/endpoints/matching/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# Matching Endpoint

:::warning NOTE
This endpoint is not available in the public API, but you can use it when running an own instance of openrouteservice.
:::

The matching endpoint can be used to match point, linear and polygonal spatial features to the edges of the routing graph representing the street network for a specific means of transportation.

The matching of point geometries is performed by snapping to the nearest edge in the graph, similar to the [snapping endpoint](../snapping/index.md).
Linear geometries are matched using a Hidden Markov Model (HMM) based map matching algorithm.
Polygon geometries are matched by intersecting them with the edges of the routing graph.

The routing profile has to be specified as path parameter.
The geometric `features` for matching are provided formatted as a GeoJSON `FeatureCollection` object.

The endpoint returns a JSON containing a list of matched edge ids.

:::warning HINT
The returned edge ids are the internal ids of the routing graph and can be used for further processing, e.g. with the [export endpoint](../export/index.md). They shall not be confused with OpenStreetMap way ids.
:::


## Sample query and result

Request:
```shell
curl -X 'POST' \
'http://localhost:8082/ors/v2/match/driving-car' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"features": {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"type": "bridge"
},
"geometry": {
"type": "Point",
"coordinates": [
8.685990,
49.40267
]
}
},
{
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [
[
8.684963,
49.40252
],
[
8.684932,
49.40199
],
[
8.685466,
49.40126
],
[
8.686026,
49.40092
]
]
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
8.684143,
49.40336
],
[
8.684779,
49.40302
],
[
8.685354,
49.40340
],
[
8.684697,
49.40380
],
[
8.684143,
49.40336
]
]
]
}
}
]
}
}'
```

Response:
```json
{
"edge_ids":[
7472,
11185,
2242,
5908,
741,
5913,
5914,
11548,
7471
],
"graph_timestamp":"2025-11-17T14:18:55Z"
}
```

:::warning NOTE
Internal edge ids are subject to change between graph rebuilds.
:::
15 changes: 14 additions & 1 deletion docs/api-reference/error-codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ The following table describes the supported HTTP status codes.
| 503 | The server is currently unavailable due to overload or maintenance. |



## Internal Error Codes

The following sections describes the list of possible internal error codes that might be provided by different openrouteservice
Expand Down Expand Up @@ -133,3 +132,17 @@ Endpoints.
| 8010 | Point not found. |
| 8011 | Unknown parameter. |
| 8099 | Unknown internal error. |

### Matching API

[//]: # (keep in sync with org.heigit.ors.matching.MatchingErrorCodes)

| Error Code | Description |
|:----------:|-------------------------------------------------------|
| 9000 | Unable to parse JSON request. |
| 9001 | Required parameter is missing. |
| 9002 | Invalid parameter format. |
| 9003 | Invalid parameter value. |
| 9007 | Unsupported export format. |
| 9011 | Unknown parameter. |
| 9099 | Unknown internal error. |
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ public static class EndpointSnapProperties {
@Setter(AccessLevel.PACKAGE)
public static class EndpointMatchProperties {
private boolean enabled;
private int maximumSearchRadius;
private String attribution;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@

package org.heigit.ors.api.controllers;

import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
Expand All @@ -28,16 +32,17 @@
import org.heigit.ors.api.responses.matching.MatchingResponse;
import org.heigit.ors.api.services.MatchingService;
import org.heigit.ors.common.EncoderNameEnum;
import org.heigit.ors.exceptions.MissingParameterException;
import org.heigit.ors.exceptions.ParameterValueException;
import org.heigit.ors.exceptions.StatusCodeException;
import org.heigit.ors.exceptions.*;
import org.heigit.ors.matching.MatchingErrorCodes;
import org.heigit.ors.matching.MatchingRequest;
import org.json.simple.JSONObject;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.*;

@RestController
Expand Down Expand Up @@ -131,6 +136,28 @@ public ResponseEntity<String> postMatching(@Parameter(description = "Specifies t
return new ResponseEntity<>(jsonResponse.toJSONString(), headers, HttpStatus.OK);
}

@ExceptionHandler({HttpMessageNotReadableException.class, HttpMessageConversionException.class, Exception.class})
public ResponseEntity<Object> handleReadingBodyException(final Exception e) {
final Throwable cause = e.getCause();
if (cause instanceof UnrecognizedPropertyException exception) {
return errorHandler.handleUnknownParameterException(new UnknownParameterException(MatchingErrorCodes.UNKNOWN_PARAMETER, exception.getPropertyName()));
} else if (cause instanceof InvalidFormatException exception) {
return errorHandler.handleStatusCodeException(new ParameterValueException(MatchingErrorCodes.INVALID_PARAMETER_FORMAT, exception.getValue().toString()));
} else if (cause instanceof InvalidDefinitionException exception) {
return errorHandler.handleStatusCodeException(new ParameterValueException(MatchingErrorCodes.INVALID_PARAMETER_VALUE, exception.getPath().get(0).getFieldName()));
} else if (cause instanceof MismatchedInputException exception) {
return errorHandler.handleStatusCodeException(new ParameterValueException(MatchingErrorCodes.INVALID_PARAMETER_FORMAT, exception.getPath().get(0).getFieldName()));
} else if (cause instanceof ConversionFailedException exception) {
return errorHandler.handleStatusCodeException(new ParameterValueException(MatchingErrorCodes.INVALID_PARAMETER_VALUE, (String) exception.getValue()));
} else {
// Check if we are missing the body as a whole
if (e.getLocalizedMessage() != null && e.getLocalizedMessage().startsWith("Required request body is missing")) {
return errorHandler.handleStatusCodeException(new EmptyElementException(MatchingErrorCodes.MISSING_PARAMETER, "Request body could not be read"));
}
return errorHandler.handleGenericException(e);
}
}

@ExceptionHandler(StatusCodeException.class)
public ResponseEntity<Object> handleException(final StatusCodeException e) {
return errorHandler.handleStatusCodeException(e);
Expand Down
Loading
Loading