Skip to content

Commit

Permalink
Merge pull request #25 from thehappycheese/add-batch-unified
Browse files Browse the repository at this point in the history
Add-batch-unified
  • Loading branch information
thehappycheese authored Jun 13, 2024
2 parents 87d73cd + 7be8cf5 commit e14e5e9
Show file tree
Hide file tree
Showing 20 changed files with 759 additions and 69 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@
"markdownlint.config": {
"MD033": false,
"MD028":false
}
},
"editor.inlayHints.enabled": "offUnlessPressed"
}
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "nicklinref"
version = "1.0.0"
version = "1.1.0"
description = "nicklinref is a server that slices segments of the Main Roads Western Australia road network based on slk"
authors = ["thehappycheese"]
edition = "2021"
Expand Down
18 changes: 17 additions & 1 deletion __static_http/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,26 @@ add_features(new URLSearchParams(window.location.search)).then(success => succes
//////////////////////////////////////////////////////////////////////
// Get Geometry
// Optionally, use a Fetch_Queue to avoid browser error from too many parallel requests
// For which the fetch API itself provides no convienient work-around
// For which the fetch API itself provides no convenient work-around
///////////////////////////////////////////////////////////////////////

async function add_features(url_params, fetch_pool = undefined) {


if(url_params.has("format") && url_params.has("items")){
url_params.set("format","wkt")
return fetch("/batch2/?"+url_params.toString())
.then(response=>response.json())
.then(items=>items
.filter(item=>item)
.map(item=>{
let read_features = new ol.format.WKT().readFeatures(item,{ featureProjection, dataProjection });
layer_geojson.getSource().addFeatures(read_features);
})
).then(()=>true);
}


f = url_params.get("f") ?? "geojson";
if(!(f.toLowerCase()==="latlon" || f.toLowerCase()==="latlondir")){
url_params.set("f","wkt");// geojson is default
Expand Down
4 changes: 4 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## [1.1.0] 2024-06-13

- Add routes `/batch2`, `/line` and `/point` which all support both a `GET` and `POST` method

## [1.0.0] 2023-06-12

- drop support for case insensitive queries
Expand Down
93 changes: 81 additions & 12 deletions README.md → readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ create and visualise geometry based on live data. Below is a screenshot of NickM
- [3.3.3. `f=` Parameter](#333-f-parameter)
- [3.4. Browser - `/show/` Page](#34-browser---show-page)
- [3.5. Advanced - `/batch/` Route](#35-advanced---batch-route)
- [3.6. Additional Usage Notes](#36-additional-usage-notes)
- [3.6.1. SLK, True Distance and Chainage](#361-slk-true-distance-and-chainage)
- [3.6.2. Supported Network Types](#362-supported-network-types)
- [3.6.3. Coordinate Reference System (CRS)](#363-coordinate-reference-system-crs)
- [3.6. `/batch2` unified batch requests](#36-batch2-unified-batch-requests)
- [3.7. `/point` and `/line` routes](#37-point-and-line-routes)
- [3.8. Additional Usage Notes](#38-additional-usage-notes)
- [3.8.1. SLK, True Distance and Chainage](#381-slk-true-distance-and-chainage)
- [3.8.2. Supported Network Types](#382-supported-network-types)
- [3.8.3. Coordinate Reference System (CRS)](#383-coordinate-reference-system-crs)
- [4. Running the Server Yourself](#4-running-the-server-yourself)
- [4.1. Installation](#41-installation)
- [4.2. Compilation](#42-compilation)
Expand Down Expand Up @@ -343,18 +345,85 @@ The output of the script above is shown below:
</details>
### 3.6. Additional Usage Notes
### 3.6. `/batch2` unified batch requests
#### 3.6.1. SLK, True Distance and Chainage
A new batch request route has been added to allow requesting both line and point features.
This route accepts either a `POST`or `GET` requests.
This version of the batch route does not use the complex binary protocol described above.
The format of the request is as follows;
```json
{
"format":"wkt",
"items":[
{
"road":"H001",
"slk_from":10,
"slk_to":20,
"offset":10
},
{
"road":"H016",
"slk":10
},
{
"road":"H015",
"slk":10
}
]
}
```
When url encoded the above query should look like;
```text
format=wkt&items=%5B%7B%27road%27%3A+%27H001%27%2C+%27slk_from%27%3A+10%2C+%27slk_to%27%3A+20%2C+%27offset%27%3A+10%7D%2C+%7B%27road%27%3A+%27H016%27%2C+%27slk%27%3A+10%7D%2C+%7B%27road%27%3A+%27H015%27%2C+%27slk%27%3A+10%7D%5D
```
Formats supported are restricted to `wkt`, `geojson` or `json`
The result type is always a JSON list which is the same length as the `"items"` specified in the request.
Items that did not return a result may be either `null` or, due to a long outstanding issue, an invalid empty geometry like `MULTIPOINT ()` or a GeoJSON object with 0 coordinates.
If `format=wkt` is specified, then each item will be either `null` or a `"wkt string"`. The result should look like;
```json
200
["MULTILINESTRING ((115.94759478043025 -32.02724359866268,...., 116.00968185354726 -32.0797516372457))","MULTIPOINT ((115.8018372737292 -31.890062043719624),(115.80208275968369 -31.88999214761082))","MULTIPOINT ((115.85393141776753 -32.048215776792965),(115.85365425352151 -32.04809166414051))"]
```
### 3.7. `/point` and `/line` routes
The `/point` and `/line` routes support BOTH `GET` and `POST` requests.
> Note: The first version of this server supported only `GET` requests against the
> root path `/` using url query parameters. To add support `POST` requests, I
> had to create two new routes `/line` and `/point`.
>
> For backward compatibility `GET`, requests against root `/` will continue to
> work.
`POST` requests made against `/line` or `/route` must use the same parameters as
documented above, but formatted as JSON in the body of the request. For example
`GET localhost:8080/?road=H001&slk=10` becomes `POST localhost:8080/point` with
JSON body `{"road":"H001", "slk":10}`
### 3.8. Additional Usage Notes
#### 3.8.1. SLK, True Distance and Chainage
SLK stands for "Straight Line Kilometre" and is sometimes called 'chainage' or
'kilometrage' in other contexts.
At Main Roads Western Australia SLK refers to an "adjusted" linear measure which
has discontinuities called 'Points of Equation' (POE) (there are between 100 and 200
points of equation throughout the state road network) where there is an abrupt
increase or decrease in SLK. This is done so that when asset locations are
recorded by SLK, these records are not invalidated when a road realignment
has discontinuities called 'Points of Equation' (POE) (there are between 100 and
200 points of equation throughout the state road network) where there is an
abrupt increase or decrease in SLK. This is done so that when asset locations
are recorded by SLK, these records are not invalidated when a road realignment
project modifies the length of a road.
This software has no special compensation to handle POE discontinuities. Please
Expand All @@ -365,7 +434,7 @@ The non-adjusted linear measure is called "True Distance".
This software is only capable of looking up Lat/Lon from SLK. True distance is
not yet supported.
#### 3.6.2. Supported Network Types
#### 3.8.2. Supported Network Types
This tool is capable of querying all road network types included in this dataset
<https://portal-mainroads.opendata.arcgis.com/datasets/mainroads::road-network/about>
Expand All @@ -379,7 +448,7 @@ This tool is capable of querying all road network types included in this dataset
| Miscellaneous Road | ✔️ |
| Crossover | ✔️ |
#### 3.6.3. Coordinate Reference System (CRS)
#### 3.8.3. Coordinate Reference System (CRS)
The coordinate system of the returned geometry depends on the coordinate system
downloaded from `NLR_DATA_SOURCE_URL`.
Expand Down
4 changes: 1 addition & 3 deletions src/data/cached/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,4 @@ mod layer;
pub use layer::Layer;

mod feature;
pub use feature::Feature;

pub use nickslinetoolsrust::vector2::Vector2;
pub use feature::Feature;
12 changes: 4 additions & 8 deletions src/filters/custom_rejection_handler.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use crate::helpers::ErrorWithStaticMessage;
use warp::{
http::{StatusCode},
reply::Response,
Rejection, Reply, reject::{InvalidQuery, MethodNotAllowed},
http::StatusCode, reject::{InvalidQuery, MethodNotAllowed, UnsupportedMediaType}, reply::Response, Rejection, Reply
};

pub async fn custom_rejection_handler(rejection: Rejection) -> Result<Response, Rejection> {
Expand All @@ -13,23 +11,21 @@ pub async fn custom_rejection_handler(rejection: Rejection) -> Result<Response,
if rejection.is_not_found() {
code = StatusCode::NOT_FOUND;
message = "Not Found".to_owned();

} else if let Some(custom_reject) = rejection.find::<ErrorWithStaticMessage>() {
code = StatusCode::INTERNAL_SERVER_ERROR;
message = custom_reject.get_message().to_owned();

} else if let Some(custom_reject) = rejection.find::<InvalidQuery>() {
code = StatusCode::BAD_REQUEST;
message = custom_reject.to_string();

} else if let Some(custom_reject) = rejection.find::<MethodNotAllowed>() {
code = StatusCode::METHOD_NOT_ALLOWED;
message = custom_reject.to_string();

} else if let Some(custom_reject) = rejection.find::<UnsupportedMediaType>(){
code = StatusCode::UNSUPPORTED_MEDIA_TYPE;
message = custom_reject.to_string();
} else {
code = StatusCode::INTERNAL_SERVER_ERROR;
message = "Unexpected error".to_owned();

}

let res = warp::http::Response::builder()
Expand Down
46 changes: 27 additions & 19 deletions src/filters/get_combined_filters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,36 @@ use warp::{Filter, wrap_fn, filters::BoxedFilter, reply::Response, fs::File, Rep

use crate::{data::IndexedData, settings::Settings};

/// get the combined routes / filters to handle each feature of the server
pub async fn get_combined_filters(settings:&Settings, indexed_data:Arc<IndexedData>) -> Result<BoxedFilter<(Response,)>, Box<dyn Error>> {

// define each "filter" (aka "route") of the server
// each filter corresponds to a feature or capability
let filter_static_folder = warp::fs::dir(settings.NLR_STATIC_HTTP.clone());
let filter_show = warp::path("show").and(filter_static_folder);
let filter_lines = super::lines(indexed_data.clone());
let route_points = super::points(indexed_data.clone());
let route_lines_batch = super::lines_batch(indexed_data.clone());

// chain filters together into a single filter
let x = filter_show.map(|r:File| r.into_response()).or(
filter_lines
.or(route_points)
let filter_show =
warp::path("show")
.and(
warp::fs::dir(settings.NLR_STATIC_HTTP.clone())
.map(|r:File| r.into_response())
);
let filter_lines = super::lines(indexed_data.clone());
let filter_points = super::points(indexed_data.clone());
let filter_lines_batch = super::lines_batch(indexed_data.clone());
let filter_unified_batch = super::unified_batch(indexed_data.clone());

// Chain filters together into a single filter
Ok(
filter_show
.or(
route_lines_batch
.with(warp::compression::gzip())
)
.recover(super::custom_rejection_handler)
.with(wrap_fn(super::echo_x_request_id))
).unify();
Ok(x.boxed())
filter_lines
.or(filter_points)
.or(filter_unified_batch)
.or(
filter_lines_batch
.with(warp::compression::gzip())
)
.recover(super::custom_rejection_handler)
.with(wrap_fn(super::echo_x_request_id))
).unify()
.boxed()
)
}

#[cfg(test)]
Expand Down
27 changes: 25 additions & 2 deletions src/filters/lines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,31 @@ use super::{
pub fn lines(
indexed_data: Arc<IndexedData>,
) -> impl Filter<Extract = (String,), Error = Rejection> + Clone {
warp::get()
warp::path::end()
.and(warp::get())
.and(with_shared_data(indexed_data.clone()))
.and(warp::query())
.and_then(|
indexed_data: Arc<IndexedData>,
query: QueryParametersLine
| async move {
if query.m {
get_linestring_m(&query, &indexed_data).map_err(|err|err.as_rejection())
} else {
get_linestring(&query, &indexed_data).map_err(|err|err.as_rejection())
}
})
// New version of the endpoint must be descriminated by the `/line` route
// this new version will accept both GET and POST requests
.or(
warp::path("line")
.and(with_shared_data(indexed_data.clone()))
.and(
warp::get().and(warp::query())
.or(warp::post().and(warp::body::json()))
.unify()
)
.and_then(|
indexed_data: Arc<IndexedData>,
query: QueryParametersLine
| async move {
Expand All @@ -26,4 +47,6 @@ pub fn lines(
get_linestring(&query, &indexed_data).map_err(|err|err.as_rejection())
}
})
}
)
.unify()
}
11 changes: 7 additions & 4 deletions src/filters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ pub use echo_x_request_id::echo_x_request_id;
pub mod query_parameters;

mod lines;
pub use lines::lines;
use lines::lines;

mod points;
pub use points::points;
use points::points;

mod lines_batch;
pub use lines_batch::lines_batch;
use lines_batch::lines_batch;

mod custom_rejection_handler;
pub use custom_rejection_handler::custom_rejection_handler;
Expand All @@ -21,4 +21,7 @@ mod get_combined_filters;
pub use get_combined_filters::get_combined_filters;

mod with_shared_data;
pub use with_shared_data::with_shared_data;
pub use with_shared_data::with_shared_data;

mod unified_batch;
use unified_batch::unified_batch;
21 changes: 20 additions & 1 deletion src/filters/points.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,32 @@ use super::{
pub fn points(
indexed_data: Arc<IndexedData>
) -> impl Filter<Extract = (String,), Error = warp::Rejection> + Clone {
warp::get()
warp::path::end()
.and(warp::get())
.and(with_shared_data(indexed_data.clone()))
.and(warp::query())
.and_then(|
indexed_data: Arc<IndexedData>,
query: QueryParametersPoint
| async move {
get_points (&query, &indexed_data).map_err(|err|err.as_rejection())
})
// New version of the endpoint must be descriminated by the `/point` route
// this new version will accept both GET and POST requests
.or(
warp::path("point")
.and(with_shared_data(indexed_data.clone()))
.and(
warp::get().and(warp::query())
.or(warp::post().and(warp::body::json()))
.unify()
)
.and_then(|
indexed_data: Arc<IndexedData>,
query: QueryParametersPoint
| async move {
get_points(&query, &indexed_data).map_err(|err|err.as_rejection())
})
)
.unify()
}
Loading

0 comments on commit e14e5e9

Please sign in to comment.