Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add minzoom and maxzoom support #265

Merged
merged 2 commits into from
Oct 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
69 changes: 48 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,11 @@ martin --config config.yaml
You can find an example of a configuration file [here](https://github.com/urbica/martin/blob/master/tests/config.yaml).

```yaml
# The socket address to bind [default: 0.0.0.0:3000]
listen_addresses: '0.0.0.0:3000'

# Database connection string
connection_string: 'postgres://postgres@localhost/db'
connection_string: 'postgres://postgres@localhost:5432/db'

# Maximum connections pool size [default: 20]
pool_size: 20
Expand All @@ -402,60 +405,84 @@ keep_alive: 75
# Number of web server workers
worker_processes: 8

# The socket address to bind [default: 0.0.0.0:3000]
listen_addresses: '0.0.0.0:3000'

# Enable watch mode
watch: true
watch: false

# Trust invalid certificates. This introduces significant vulnerabilities, and should only be used as a last resort.
danger_accept_invalid_certs: false

# associative arrays of table sources
# Associative arrays of table sources
table_sources:
public.table_source:
# table source id
# Table source id (required)
id: public.table_source

# table schema
# Table schema (required)
schema: public

# table name
# Table name (required)
table: table_source

# geometry column name
# Geometry SRID (required)
srid: 4326

# Geometry column name (required)
geometry_column: geom

# geometry srid
srid: 4326
# Feature id column name
id_column: ~

# An integer specifying the minimum zoom level
minzoom: 0

# An integer specifying the maximum zoom level. MUST be >= minzoom
maxzoom: 30

# The maximum extent of available map tiles. Bounds MUST define an area
# covered by all zoom levels. The bounds are represented in WGS:84
# latitude and longitude values, in the order left, bottom, right, top.
# Values may be integers or floating point numbers.
bounds: [-180.0, -90.0, 180.0, 90.0]

# tile extent in tile coordinate space
# Tile extent in tile coordinate space
extent: 4096

# buffer distance in tile coordinate space to optionally clip geometries
# Buffer distance in tile coordinate space to optionally clip geometries
buffer: 64

# boolean to control if geometries should be clipped or encoded as is
# Boolean to control if geometries should be clipped or encoded as is
clip_geom: true

# geometry type
# Geometry type
geometry_type: GEOMETRY

# list of columns, that should be encoded as a tile properties
# List of columns, that should be encoded as tile properties (required)
properties:
gid: int4

# associative arrays of function sources
# Associative arrays of function sources
function_sources:
public.function_source:
# function source id
# Function source id (required)
id: public.function_source

# schema name
# Schema name (required)
schema: public

# function name
# Function name (required)
function: function_source

# An integer specifying the minimum zoom level
minzoom: 0

# An integer specifying the maximum zoom level. MUST be >= minzoom
maxzoom: 30

# The maximum extent of available map tiles. Bounds MUST define an area
# covered by all zoom levels. The bounds are represented in WGS:84
# latitude and longitude values, in the order left, bottom, right, top.
# Values may be integers or floating point numbers.
bounds: [-180.0, -90.0, 180.0, 90.0]
```

## Using with Docker
Expand Down
5 changes: 5 additions & 0 deletions benches/sources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ fn mock_table_source(schema: &str, table: &str) -> TableSource {
table: table.to_owned(),
id_column: None,
geometry_column: "geom".to_owned(),
minzoom: None,
maxzoom: None,
bounds: None,
srid: 3857,
extent: Some(4096),
Expand All @@ -31,6 +33,9 @@ fn mock_function_source(schema: &str, function: &str) -> FunctionSource {
id: format!("{}.{}", schema, function),
schema: schema.to_owned(),
function: function.to_owned(),
minzoom: None,
maxzoom: None,
bounds: None,
}
}

Expand Down
22 changes: 22 additions & 0 deletions src/composite_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,20 @@ impl CompositeSource {
format!("{} {}", bounds_cte, tile_query)
}

pub fn get_minzoom(&self) -> Option<u8> {
self.table_sources
.iter()
.filter_map(|table_source| table_source.minzoom)
.min()
}

pub fn get_maxzoom(&self) -> Option<u8> {
self.table_sources
.iter()
.filter_map(|table_source| table_source.maxzoom)
.max()
}

pub fn get_bounds(&self) -> Option<Vec<f32>> {
self.table_sources
.iter()
Expand Down Expand Up @@ -75,6 +89,14 @@ impl Source for CompositeSource {
tilejson_builder.scheme("xyz");
tilejson_builder.name(&self.id);

if let Some(minzoom) = self.get_minzoom() {
tilejson_builder.minzoom(minzoom);
};

if let Some(maxzoom) = self.get_maxzoom() {
tilejson_builder.maxzoom(maxzoom);
};

if let Some(bounds) = self.get_bounds() {
tilejson_builder.bounds(bounds);
};
Expand Down
73 changes: 46 additions & 27 deletions src/dev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,24 @@ use crate::function_source::{FunctionSource, FunctionSources};
use crate::server::AppState;
use crate::table_source::{TableSource, TableSources};

pub fn mock_table_sources() -> Option<TableSources> {
pub fn mock_table_sources(sources: Vec<TableSource>) -> TableSources {
let mut table_sources: TableSources = HashMap::new();
for source in sources {
table_sources.insert(source.id.to_owned(), Box::new(source));
}

table_sources
}

pub fn mock_default_table_sources() -> TableSources {
let source = TableSource {
id: "public.table_source".to_owned(),
schema: "public".to_owned(),
table: "table_source".to_owned(),
id_column: None,
geometry_column: "geom".to_owned(),
minzoom: Some(0),
maxzoom: Some(30),
bounds: Some(vec![-180.0, -90.0, 180.0, 90.0]),
srid: 4326,
extent: Some(4096),
Expand All @@ -34,6 +45,8 @@ pub fn mock_table_sources() -> Option<TableSources> {
table: "points1".to_owned(),
id_column: None,
geometry_column: "geom".to_owned(),
minzoom: Some(0),
maxzoom: Some(30),
bounds: Some(vec![-180.0, -90.0, 180.0, 90.0]),
srid: 4326,
extent: Some(4096),
Expand All @@ -49,6 +62,8 @@ pub fn mock_table_sources() -> Option<TableSources> {
table: "points2".to_owned(),
id_column: None,
geometry_column: "geom".to_owned(),
minzoom: Some(0),
maxzoom: Some(30),
bounds: Some(vec![-180.0, -90.0, 180.0, 90.0]),
srid: 4326,
extent: Some(4096),
Expand All @@ -64,6 +79,8 @@ pub fn mock_table_sources() -> Option<TableSources> {
table: "points3857".to_owned(),
id_column: None,
geometry_column: "geom".to_owned(),
minzoom: Some(0),
maxzoom: Some(30),
bounds: Some(vec![-180.0, -90.0, 180.0, 90.0]),
srid: 3857,
extent: Some(4096),
Expand All @@ -73,36 +90,38 @@ pub fn mock_table_sources() -> Option<TableSources> {
properties: HashMap::new(),
};

let mut table_sources: TableSources = HashMap::new();
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)
mock_table_sources(vec![source, table_source1, table_source2, table_source3857])
}

pub fn mock_function_sources() -> Option<FunctionSources> {
pub fn mock_function_sources(sources: Vec<FunctionSource>) -> FunctionSources {
let mut function_sources: FunctionSources = HashMap::new();
for source in sources {
function_sources.insert(source.id.to_owned(), Box::new(source));
}

function_sources
}

pub fn mock_default_function_sources() -> FunctionSources {
let function_source = FunctionSource {
id: "public.function_source".to_owned(),
schema: "public".to_owned(),
function: "function_source".to_owned(),
minzoom: Some(0),
maxzoom: Some(30),
bounds: Some(vec![-180.0, -90.0, 180.0, 90.0]),
};

let function_source_query_params = FunctionSource {
id: "public.function_source_query_params".to_owned(),
schema: "public".to_owned(),
function: "function_source_query_params".to_owned(),
minzoom: Some(0),
maxzoom: Some(30),
bounds: Some(vec![-180.0, -90.0, 180.0, 90.0]),
};

function_sources.insert(
"public.function_source".to_owned(),
Box::new(FunctionSource {
id: "public.function_source".to_owned(),
schema: "public".to_owned(),
function: "function_source".to_owned(),
}),
);

function_sources.insert(
"public.function_source_query_params".to_owned(),
Box::new(FunctionSource {
id: "public.function_source_query_params".to_owned(),
schema: "public".to_owned(),
function: "function_source_query_params".to_owned(),
}),
);

Some(function_sources)
mock_function_sources(vec![function_source, function_source_query_params])
}

pub fn make_pool() -> Pool {
Expand Down
34 changes: 34 additions & 0 deletions src/function_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,28 @@ use crate::utils::{prettify_error, query_to_json};

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FunctionSource {
// Function source id
pub id: String,
// Schema name
pub schema: String,

// Function name
pub function: String,

// An integer specifying the minimum zoom level
#[serde(skip_serializing_if = "Option::is_none")]
pub minzoom: Option<u8>,

// An integer specifying the maximum zoom level. MUST be >= minzoom
#[serde(skip_serializing_if = "Option::is_none")]
pub maxzoom: Option<u8>,

// The maximum extent of available map tiles. Bounds MUST define an area
// covered by all zoom levels. The bounds are represented in WGS:84
// latitude and longitude values, in the order left, bottom, right, top.
// Values may be integers or floating point numbers.
#[serde(skip_serializing_if = "Option::is_none")]
pub bounds: Option<Vec<f32>>,
}

pub type FunctionSources = HashMap<String, Box<FunctionSource>>;
Expand All @@ -30,6 +49,18 @@ impl Source for FunctionSource {
tilejson_builder.name(&self.id);
tilejson_builder.tiles(vec![]);

if let Some(minzoom) = &self.minzoom {
tilejson_builder.minzoom(*minzoom);
};

if let Some(maxzoom) = &self.maxzoom {
tilejson_builder.maxzoom(*maxzoom);
};

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

Ok(tilejson_builder.finalize())
}

Expand Down Expand Up @@ -100,6 +131,9 @@ pub fn get_function_sources(conn: &mut Connection) -> Result<FunctionSources, io
id: id.clone(),
schema,
function,
minzoom: None,
maxzoom: None,
bounds: None,
};

sources.insert(id, Box::new(source));
Expand Down
30 changes: 27 additions & 3 deletions src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,17 @@ async fn get_composite_source_tile(
.split(',')
.filter_map(|source_id| table_sources.get(source_id))
.map(|source| source.deref().clone())
.filter(|source| {
let gte_minzoom = source
.minzoom
.map_or(true, |minzoom| path.z >= minzoom.into());

let lte_maxzoom = source
.maxzoom
.map_or(true, |maxzoom| path.z <= maxzoom.into());

gte_minzoom && lte_maxzoom
})
.collect();

if sources.is_empty() {
Expand Down Expand Up @@ -295,9 +306,22 @@ async fn get_function_source_tile(
.clone()
.ok_or_else(|| error::ErrorNotFound("There is no function sources"))?;

let source = function_sources.get(&path.source_id).ok_or_else(|| {
error::ErrorNotFound(format!("Function source '{}' not found", path.source_id))
})?;
let source = function_sources
.get(&path.source_id)
.filter(|source| {
let gte_minzoom = source
.minzoom
.map_or(true, |minzoom| path.z >= minzoom.into());

let lte_maxzoom = source
.maxzoom
.map_or(true, |maxzoom| path.z <= maxzoom.into());

gte_minzoom && lte_maxzoom
})
.ok_or_else(|| {
error::ErrorNotFound(format!("Function source '{}' not found", path.source_id))
})?;

let xyz = Xyz {
z: path.z,
Expand Down
Loading