Skip to content

Commit

Permalink
feat: add bounds to tilejson endpoints (#260) (h/t @jaspervercnocke)
Browse files Browse the repository at this point in the history
  • Loading branch information
stepankuzmin committed Oct 10, 2021
1 parent d22cc77 commit 40b0a0c
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 11 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ jobs:
psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U postgres -d test -f tests/fixtures/table_source.sql
psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U postgres -d test -f tests/fixtures/points1_source.sql
psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U postgres -d test -f tests/fixtures/points2_source.sql
psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U postgres -d test -f tests/fixtures/points3857_source.sql
psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U postgres -d test -f tests/fixtures/function_source.sql
psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U postgres -d test -f tests/fixtures/function_source_query_params.sql
env:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/grcov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U postgres -d test -f tests/fixtures/table_source.sql
psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U postgres -d test -f tests/fixtures/points1_source.sql
psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U postgres -d test -f tests/fixtures/points2_source.sql
psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U postgres -d test -f tests/fixtures/points3857_source.sql
psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U postgres -d test -f tests/fixtures/function_source.sql
psql -h $POSTGRES_HOST -p $POSTGRES_PORT -U postgres -d test -f tests/fixtures/function_source_query_params.sql
env:
Expand Down
12 changes: 12 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ num_cpus = "1.13"
postgres = { version = "0.19.1", features = ["with-time-0_2", "with-uuid-0_8", "with-serde_json-1"] }
postgres-native-tls = "0.5.0"
postgres-protocol = "0.6.2"
postgis = "0.9.0"
r2d2 = "0.8"
r2d2_postgres = "0.18"
semver = "1.0"
Expand Down
1 change: 1 addition & 0 deletions benches/sources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ fn mock_table_source(schema: &str, table: &str) -> TableSource {
table: table.to_owned(),
id_column: None,
geometry_column: "geom".to_owned(),
bounds: None,
srid: 3857,
extent: Some(4096),
buffer: Some(64),
Expand Down
19 changes: 19 additions & 0 deletions src/composite_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,21 @@ impl CompositeSource {

format!("{} {}", bounds_cte, tile_query)
}

pub fn get_bounds(&self) -> Option<Vec<f32>> {
self.table_sources
.iter()
.filter_map(|table_source| table_source.bounds.as_ref())
.map(|bounds| bounds.to_vec())
.reduce(|a, b| {
vec![
if a[0] < b[0] { a[0] } else { b[0] },
if a[1] < b[1] { a[1] } else { b[1] },
if a[2] > b[2] { a[2] } else { b[2] },
if a[3] > b[3] { a[3] } else { b[3] },
]
})
}
}

impl Source for CompositeSource {
Expand All @@ -60,6 +75,10 @@ impl Source for CompositeSource {
tilejson_builder.scheme("xyz");
tilejson_builder.name(&self.id);

if let Some(bounds) = self.get_bounds() {
tilejson_builder.bounds(bounds);
};

Ok(tilejson_builder.finalize())
}

Expand Down
23 changes: 21 additions & 2 deletions src/dev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ pub fn mock_table_sources() -> Option<TableSources> {
table: "table_source".to_owned(),
id_column: None,
geometry_column: "geom".to_owned(),
srid: 3857,
bounds: Some(vec![-180.0, -90.0, 180.0, 90.0]),
srid: 4326,
extent: Some(4096),
buffer: Some(64),
clip_geom: Some(true),
Expand All @@ -33,7 +34,8 @@ pub fn mock_table_sources() -> Option<TableSources> {
table: "points1".to_owned(),
id_column: None,
geometry_column: "geom".to_owned(),
srid: 3857,
bounds: Some(vec![-180.0, -90.0, 180.0, 90.0]),
srid: 4326,
extent: Some(4096),
buffer: Some(64),
clip_geom: Some(true),
Expand All @@ -47,6 +49,22 @@ pub fn mock_table_sources() -> Option<TableSources> {
table: "points2".to_owned(),
id_column: None,
geometry_column: "geom".to_owned(),
bounds: Some(vec![-180.0, -90.0, 180.0, 90.0]),
srid: 4326,
extent: Some(4096),
buffer: Some(64),
clip_geom: Some(true),
geometry_type: None,
properties: HashMap::new(),
};

let table_source3857 = TableSource {
id: "public.points3857".to_owned(),
schema: "public".to_owned(),
table: "points3857".to_owned(),
id_column: None,
geometry_column: "geom".to_owned(),
bounds: Some(vec![-180.0, -90.0, 180.0, 90.0]),
srid: 3857,
extent: Some(4096),
buffer: Some(64),
Expand All @@ -59,6 +77,7 @@ pub fn mock_table_sources() -> Option<TableSources> {
table_sources.insert("public.table_source".to_owned(), Box::new(source));
table_sources.insert("public.points1".to_owned(), Box::new(table_source1));
table_sources.insert("public.points2".to_owned(), Box::new(table_source2));
table_sources.insert("public.points3857".to_owned(), Box::new(table_source3857));
Some(table_sources)
}

Expand Down
4 changes: 4 additions & 0 deletions src/scripts/get_bounds.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
SELECT
ST_Transform (ST_SetSRID (ST_Extent ({geometry_column}), {srid}), 4326) AS bounds
FROM
{id}
29 changes: 22 additions & 7 deletions src/table_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use tilejson::{TileJSON, TileJSONBuilder};

use crate::db::Connection;
use crate::source::{Query, Source, Tile, Xyz};
use crate::utils::{get_bounds_cte, get_srid_bounds, json_to_hashmap, prettify_error, tilebbox};
use crate::utils;

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TableSource {
Expand All @@ -16,6 +16,7 @@ pub struct TableSource {
pub id_column: Option<String>,
pub geometry_column: String,
pub srid: u32,
pub bounds: Option<Vec<f32>>,
pub extent: Option<u32>,
pub buffer: Option<u32>,
pub clip_geom: Option<bool>,
Expand All @@ -27,7 +28,7 @@ pub type TableSources = HashMap<String, Box<TableSource>>;

impl TableSource {
pub fn get_geom_query(&self, xyz: &Xyz) -> String {
let mercator_bounds = tilebbox(xyz);
let mercator_bounds = utils::tilebbox(xyz);

let properties = if self.properties.is_empty() {
"".to_string()
Expand Down Expand Up @@ -73,8 +74,8 @@ impl TableSource {
}

pub fn build_tile_query(&self, xyz: &Xyz) -> String {
let srid_bounds = get_srid_bounds(self.srid, xyz);
let bounds_cte = get_bounds_cte(srid_bounds);
let srid_bounds = utils::get_srid_bounds(self.srid, xyz);
let bounds_cte = utils::get_bounds_cte(srid_bounds);
let tile_query = self.get_tile_query(xyz);

format!("{} {}", bounds_cte, tile_query)
Expand All @@ -92,6 +93,10 @@ impl Source for TableSource {
tilejson_builder.scheme("xyz");
tilejson_builder.name(&self.id);

if let Some(bounds) = &self.bounds {
tilejson_builder.bounds(bounds.to_vec());
};

Ok(tilejson_builder.finalize())
}

Expand All @@ -106,7 +111,7 @@ impl Source for TableSource {
let tile: Tile = conn
.query_one(tile_query.as_str(), &[])
.map(|row| row.get("st_asmvt"))
.map_err(prettify_error("Can't get table source tile"))?;
.map_err(utils::prettify_error("Can't get table source tile"))?;

Ok(tile)
}
Expand All @@ -121,7 +126,7 @@ pub fn get_table_sources(conn: &mut Connection) -> Result<TableSources, io::Erro

let rows = conn
.query(include_str!("scripts/get_table_sources.sql"), &[])
.map_err(prettify_error("Can't get table sources"))?;
.map_err(utils::prettify_error("Can't get table sources"))?;

for row in &rows {
let schema: String = row.get("f_table_schema");
Expand All @@ -142,14 +147,24 @@ pub fn get_table_sources(conn: &mut Connection) -> Result<TableSources, io::Erro
continue;
}

let properties = json_to_hashmap(&row.get("properties"));
let bounds_query = utils::get_source_bounds(&id, srid as u32, &geometry_column);

let bounds: Option<Vec<f32>> = conn
.query_one(bounds_query.as_str(), &[])
.map(|row| row.get("bounds"))
.ok()
.flatten()
.and_then(utils::polygon_to_bbox);

let properties = utils::json_to_hashmap(&row.get("properties"));

let source = TableSource {
id: id.to_string(),
schema,
table,
id_column: None,
geometry_column,
bounds,
srid: srid as u32,
extent: Some(DEFAULT_EXTENT),
buffer: Some(DEFAULT_BUFFER),
Expand Down
26 changes: 26 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::collections::HashMap;

use crate::source::{Query, Xyz};
use postgis::{ewkb, LineString, Point, Polygon};
use postgres::types::Json;
use serde_json::Value;

Expand Down Expand Up @@ -66,3 +67,28 @@ pub fn get_srid_bounds(srid: u32, xyz: &Xyz) -> String {
mercator_bounds = tilebbox(xyz),
)
}

pub fn get_source_bounds(id: &str, srid: u32, geometry_column: &str) -> String {
format!(
include_str!("scripts/get_bounds.sql"),
id = id,
srid = srid,
geometry_column = geometry_column,
)
}

pub fn polygon_to_bbox(polygon: ewkb::Polygon) -> Option<Vec<f32>> {
polygon.rings().next().and_then(|linestring| {
let mut points = linestring.points();
if let (Some(bottom_left), Some(top_right)) = (points.next(), points.nth(1)) {
Some(vec![
bottom_left.x() as f32,
bottom_left.y() as f32,
top_right.x() as f32,
top_right.y() as f32,
])
} else {
None
}
})
}
19 changes: 19 additions & 0 deletions tests/fixtures/points3857_source.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
CREATE TABLE points3857(gid SERIAL PRIMARY KEY, geom GEOMETRY(POINT, 3857));

INSERT INTO points3857
SELECT
generate_series(1, 10000) as id,
(
ST_DUMP(
ST_GENERATEPOINTS(
ST_TRANSFORM(
ST_GEOMFROMTEXT('POLYGON ((-179 89, 179 89, 179 -89, -179 -89, -179 89))', 4326),
3857
),
10000
)
)
).geom;

CREATE INDEX ON points3857 USING GIST(geom);
CLUSTER points3857_geom_idx ON points3857;
1 change: 1 addition & 0 deletions tests/initdb-martin.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ psql --dbname="$POSTGRES_DB" -f /fixtures/function_source_query_params.sql

psql --dbname="$POSTGRES_DB" -f /fixtures/points1_source.sql
psql --dbname="$POSTGRES_DB" -f /fixtures/points2_source.sql
psql --dbname="$POSTGRES_DB" -f /fixtures/points3857_source.sql
4 changes: 2 additions & 2 deletions tests/server_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ async fn test_get_composite_source_ok() {
assert_eq!(response.status(), http::StatusCode::NOT_FOUND);

let req = test::TestRequest::get()
.uri("/public.points1,public.points2.json")
.uri("/public.points1,public.points2,public.points3857.json")
.to_request();

let response = test::call_service(&mut app, req).await;
Expand All @@ -124,7 +124,7 @@ async fn test_get_composite_source_tile_ok() {
assert_eq!(response.status(), http::StatusCode::NOT_FOUND);

let req = test::TestRequest::get()
.uri("/public.points1,public.points2/0/0/0.pbf")
.uri("/public.points1,public.points2,public.points3857/0/0/0.pbf")
.to_request();

let response = test::call_service(&mut app, req).await;
Expand Down

0 comments on commit 40b0a0c

Please sign in to comment.