Skip to content

Commit

Permalink
PG TileJSON changes, add vector_layers (#584)
Browse files Browse the repository at this point in the history
* make tilejson's `name` be the same as the ID of the source (even if
aliased)
* `/catalog` will always show ID, but now it will hide the `name` if it
is the same as the `id`
* make `description` be the longer version, e.g. `public.table.column`
format - not guaranteed to be stable
* make `vector_layers` have the fields auto-discovered in the PG table
* preserve the order of the serialized json fields

Fixes #583
  • Loading branch information
nyurik authored Feb 22, 2023
1 parent 6a55956 commit e927227
Show file tree
Hide file tree
Showing 25 changed files with 267 additions and 183 deletions.
7 changes: 4 additions & 3 deletions Cargo.lock

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

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ opt-level = 3

[package]
name = "martin"
version = "0.7.1"
version = "0.7.2"
edition = "2021"
authors = ["Stepan Kuzmin <[email protected]>", "Yuri Astrakhan <[email protected]>", "MapLibre contributors"]
description = "Blazing fast and lightweight tile server with PostGIS, MBTiles, and PMTiles support"
Expand Down Expand Up @@ -56,7 +56,7 @@ postgres-protocol = "0.6"
regex = "1"
semver = "1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_json = { version = "1", features = ["preserve_order"] }
serde_yaml = "0.9"
subst = { version = "0.2", features = ["yaml"] }
thiserror = "1"
Expand Down
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,15 @@ Martin provides [TileJSON](https://github.com/mapbox/tilejson-spec) endpoint for

When started, Martin will go through all spatial tables and functions with an appropriate signature in the database. These tables and functions will be available as the HTTP endpoints, which you can use to query Mapbox vector tiles.

| Method | URL | Description |
|--------|------------------------------------|---------------------------------------------------------|
| `GET` | `/` | Status text, that will eventually show web UI |
| `GET` | `/catalog` | [List of all sources](#source-list) |
| `GET` | `/{name}` | [Source TileJSON](#table-source-tilejson) |
| `GET` | `/{name}/{z}/{x}/{y}` | [Source Tiles](#table-source-tiles) |
| `GET` | `/{name1},...,{nameN}` | [Composite Source TileJSON](#composite-source-tilejson) |
| `GET` | `/{name1},...,{nameN}/{z}/{x}/{y}` | [Composite Source Tiles](#composite-source-tiles) |
| `GET` | `/health` | Martin server health check: returns 200 `OK` |
| Method | URL | Description |
|--------|----------------------------------------|---------------------------------------------------------|
| `GET` | `/` | Status text, that will eventually show web UI |
| `GET` | `/catalog` | [List of all sources](#source-list) |
| `GET` | `/{sourceID}` | [Source TileJSON](#table-source-tilejson) |
| `GET` | `/{sourceID}/{z}/{x}/{y}` | [Source Tiles](#table-source-tiles) |
| `GET` | `/{sourceID1},...,{nameN}` | [Composite Source TileJSON](#composite-source-tilejson) |
| `GET` | `/{sourceID1},...,{nameN}/{z}/{x}/{y}` | [Composite Source Tiles](#composite-source-tiles) |
| `GET` | `/health` | Martin server health check: returns 200 `OK` |

# Using with MapLibre
[MapLibre](https://maplibre.org/projects/maplibre-gl-js/) is an Open-source JavaScript library for showing maps on a website. MapLibre can accept [MVT vector tiles](https://github.com/mapbox/vector-tile-spec) generated by Martin, and applies [a style](https://maplibre.org/maplibre-gl-js-docs/style-spec/) to them to draw a map using Web GL.
Expand Down
2 changes: 1 addition & 1 deletion src/pg/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::utils::{sorted_opt_map, BoolOrObject, OneOrMany};

pub trait PgInfo {
fn format_id(&self) -> String;
fn to_tilejson(&self) -> TileJSON;
fn to_tilejson(&self, source_id: String) -> TileJSON;
}

#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
Expand Down
5 changes: 3 additions & 2 deletions src/pg/config_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,11 @@ impl PgInfo for FunctionInfo {
format!("{}.{}", self.schema, self.function)
}

fn to_tilejson(&self) -> TileJSON {
fn to_tilejson(&self, source_id: String) -> TileJSON {
let mut tilejson = tilejson::tilejson! {
tiles: vec![], // tile source is required, but not yet known
name: self.format_id(),
name: source_id,
description: self.format_id(),
};
tilejson.minzoom = self.minzoom;
tilejson.maxzoom = self.maxzoom;
Expand Down
16 changes: 13 additions & 3 deletions src/pg/config_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::collections::HashMap;

use serde::{Deserialize, Serialize};
use serde_yaml::Value;
use tilejson::{Bounds, TileJSON};
use tilejson::{Bounds, TileJSON, VectorLayer};

use crate::pg::config::PgInfo;
use crate::utils::{sorted_opt_map, InfoMap};
Expand Down Expand Up @@ -83,14 +83,24 @@ impl PgInfo for TableInfo {
format!("{}.{}.{}", self.schema, self.table, self.geometry_column)
}

fn to_tilejson(&self) -> TileJSON {
fn to_tilejson(&self, source_id: String) -> TileJSON {
let mut tilejson = tilejson::tilejson! {
tiles: vec![], // tile source is required, but not yet known
name: self.format_id(),
name: source_id.clone(),
description: self.format_id(),
};
tilejson.minzoom = self.minzoom;
tilejson.maxzoom = self.maxzoom;
tilejson.bounds = self.bounds;
let layer = VectorLayer {
id: source_id,
fields: self.properties.clone().unwrap_or_default(),
description: None,
maxzoom: None,
minzoom: None,
other: HashMap::default(),
};
tilejson.vector_layers = Some(vec![layer]);
tilejson
}
}
7 changes: 6 additions & 1 deletion src/pg/configurator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,12 @@ impl PgBuilder {
}

fn add_func_src(&self, sources: &mut Sources, id: String, info: &impl PgInfo, sql: PgSqlInfo) {
let source = PgSource::new(id.clone(), sql, info.to_tilejson(), self.pool.clone());
let source = PgSource::new(
id.clone(),
sql,
info.to_tilejson(id.clone()),
self.pool.clone(),
);
sources.insert(id, Box::new(source));
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/srv/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ async fn get_catalog(state: Data<AppState>) -> impl Responder {
id: id.clone(),
content_type: info.format.content_type().to_string(),
content_encoding: info.encoding.content_encoding().map(ToString::to_string),
name: tilejson.name,
name: tilejson.name.filter(|v| v != id),
description: tilejson.description,
attribution: tilejson.attribution,
}
Expand Down
128 changes: 64 additions & 64 deletions tests/expected/auto/catalog_auto.json
Original file line number Diff line number Diff line change
@@ -1,162 +1,162 @@
[
{
"content_type": "application/x-protobuf",
"id": "MixPoints",
"name": "MixedCase.MixPoints.Geom"
"content_type": "application/x-protobuf",
"description": "MixedCase.MixPoints.Geom"
},
{
"content_type": "application/x-protobuf",
"id": "function_Mixed_Name",
"name": "MixedCase.function_Mixed_Name"
"content_type": "application/x-protobuf",
"description": "MixedCase.function_Mixed_Name"
},
{
"content_type": "application/x-protobuf",
"id": "function_null",
"name": "public.function_null"
"content_type": "application/x-protobuf",
"description": "public.function_null"
},
{
"content_type": "application/x-protobuf",
"id": "function_null_row",
"name": "public.function_null_row"
"content_type": "application/x-protobuf",
"description": "public.function_null_row"
},
{
"content_type": "application/x-protobuf",
"id": "function_null_row2",
"name": "public.function_null_row2"
"content_type": "application/x-protobuf",
"description": "public.function_null_row2"
},
{
"content_type": "application/x-protobuf",
"id": "function_zoom_xy",
"name": "public.function_zoom_xy"
"content_type": "application/x-protobuf",
"description": "public.function_zoom_xy"
},
{
"content_type": "application/x-protobuf",
"id": "function_zxy",
"name": "public.function_zxy"
"content_type": "application/x-protobuf",
"description": "public.function_zxy"
},
{
"content_type": "application/x-protobuf",
"id": "function_zxy2",
"name": "public.function_zxy2"
"content_type": "application/x-protobuf",
"description": "public.function_zxy2"
},
{
"content_type": "application/x-protobuf",
"id": "function_zxy_query",
"name": "public.function_zxy_query"
"content_type": "application/x-protobuf",
"description": "public.function_zxy_query"
},
{
"content_type": "application/x-protobuf",
"id": "function_zxy_query_jsonb",
"name": "public.function_zxy_query_jsonb"
"content_type": "application/x-protobuf",
"description": "public.function_zxy_query_jsonb"
},
{
"content_type": "application/x-protobuf",
"id": "function_zxy_query_test",
"name": "public.function_zxy_query_test"
"content_type": "application/x-protobuf",
"description": "public.function_zxy_query_test"
},
{
"content_type": "application/x-protobuf",
"id": "function_zxy_row",
"name": "public.function_zxy_row"
"content_type": "application/x-protobuf",
"description": "public.function_zxy_row"
},
{
"content_type": "application/x-protobuf",
"id": "function_zxy_row_key",
"name": "public.function_zxy_row_key"
"content_type": "application/x-protobuf",
"description": "public.function_zxy_row_key"
},
{
"content_type": "image/jpeg",
"description": "One of the example maps that comes with TileMill - a bright & colorful world map that blends retro and high-tech with its folded paper texture and interactive flag tooltips. ",
"id": "geography-class-jpg",
"name": "Geography Class"
"content_type": "image/jpeg",
"name": "Geography Class",
"description": "One of the example maps that comes with TileMill - a bright & colorful world map that blends retro and high-tech with its folded paper texture and interactive flag tooltips. "
},
{
"content_type": "image/png",
"description": "One of the example maps that comes with TileMill - a bright & colorful world map that blends retro and high-tech with its folded paper texture and interactive flag tooltips. ",
"id": "geography-class-png",
"name": "Geography Class"
"content_type": "image/png",
"name": "Geography Class",
"description": "One of the example maps that comes with TileMill - a bright & colorful world map that blends retro and high-tech with its folded paper texture and interactive flag tooltips. "
},
{
"content_type": "image/png",
"description": "One of the example maps that comes with TileMill - a bright & colorful world map that blends retro and high-tech with its folded paper texture and interactive flag tooltips. ",
"id": "geography-class-png-no-bounds",
"name": "Geography Class"
"content_type": "image/png",
"name": "Geography Class",
"description": "One of the example maps that comes with TileMill - a bright & colorful world map that blends retro and high-tech with its folded paper texture and interactive flag tooltips. "
},
{
"content_type": "application/json",
"id": "json",
"content_type": "application/json",
"name": "Dummy json data"
},
{
"content_type": "image/png",
"id": "png",
"content_type": "image/png",
"name": "ne2sr"
},
{
"content_type": "application/x-protobuf",
"id": "points1",
"name": "public.points1.geom"
"content_type": "application/x-protobuf",
"description": "public.points1.geom"
},
{
"content_type": "application/x-protobuf",
"id": "points1_vw",
"name": "public.points1_vw.geom"
"content_type": "application/x-protobuf",
"description": "public.points1_vw.geom"
},
{
"content_type": "application/x-protobuf",
"id": "points2",
"name": "public.points2.geom"
"content_type": "application/x-protobuf",
"description": "public.points2.geom"
},
{
"content_type": "application/x-protobuf",
"id": "points3857",
"name": "public.points3857.geom"
"content_type": "application/x-protobuf",
"description": "public.points3857.geom"
},
{
"content_type": "application/x-protobuf",
"id": "points_empty_srid",
"name": "public.points_empty_srid.geom"
"content_type": "application/x-protobuf",
"description": "public.points_empty_srid.geom"
},
{
"content_type": "image/png",
"id": "stamen_toner__raster_CC-BY-ODbL_z3"
"id": "stamen_toner__raster_CC-BY-ODbL_z3",
"content_type": "image/png"
},
{
"content_type": "application/x-protobuf",
"id": "table_source",
"name": "public.table_source.geom"
"content_type": "application/x-protobuf",
"description": "public.table_source.geom"
},
{
"content_type": "application/x-protobuf",
"id": "table_source_multiple_geom",
"name": "public.table_source_multiple_geom.geom1"
"content_type": "application/x-protobuf",
"description": "public.table_source_multiple_geom.geom1"
},
{
"content_type": "application/x-protobuf",
"id": "table_source_multiple_geom.1",
"name": "public.table_source_multiple_geom.geom2"
"content_type": "application/x-protobuf",
"description": "public.table_source_multiple_geom.geom2"
},
{
"content_type": "application/x-protobuf",
"description": "Major cities from Natural Earth data",
"id": "uncompressed_mvt",
"name": "Major cities from Natural Earth data"
"content_type": "application/x-protobuf",
"name": "Major cities from Natural Earth data",
"description": "Major cities from Natural Earth data"
},
{
"content_type": "image/webp",
"id": "webp",
"content_type": "image/webp",
"name": "ne2sr"
},
{
"content_type": "image/webp",
"id": "webp.1",
"content_type": "image/webp",
"name": "ne2sr"
},
{
"content_encoding": "gzip",
"content_type": "application/x-protobuf",
"description": "Major cities from Natural Earth data",
"id": "world_cities",
"name": "Major cities from Natural Earth data"
"content_type": "application/x-protobuf",
"content_encoding": "gzip",
"name": "Major cities from Natural Earth data",
"description": "Major cities from Natural Earth data"
}
]
13 changes: 11 additions & 2 deletions tests/expected/auto/cmp.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
{
"name": "public.table_source.geom",
"tilejson": "3.0.0",
"tiles": [
"http://localhost:3111/table_source,points1,points2/{z}/{x}/{y}"
]
],
"vector_layers": [
{
"id": "table_source",
"fields": {
"gid": "int4"
}
}
],
"description": "public.table_source.geom",
"name": "table_source"
}
Loading

0 comments on commit e927227

Please sign in to comment.