From cd375ff1b5b555e0d5ccbde48555d1e3dfb3ca53 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 17 Nov 2023 02:40:20 -0500 Subject: [PATCH] Transparent errors, reusable get_composite_tile, minor opt (#1010) --- martin/src/srv/mod.rs | 6 ++-- martin/src/srv/server.rs | 65 +++++++++++++++++++++++++++------------ martin/src/utils/error.rs | 8 ++--- 3 files changed, 52 insertions(+), 27 deletions(-) diff --git a/martin/src/srv/mod.rs b/martin/src/srv/mod.rs index 6b8e739e..754642d9 100644 --- a/martin/src/srv/mod.rs +++ b/martin/src/srv/mod.rs @@ -1,7 +1,5 @@ mod config; -mod server; - pub use config::{SrvConfig, KEEP_ALIVE_DEFAULT, LISTEN_ADDRESSES_DEFAULT}; -pub use server::{new_server, router, Catalog, RESERVED_KEYWORDS}; -pub use crate::source::CatalogSourceEntry; +mod server; +pub use server::{get_composite_tile, new_server, router, Catalog, RESERVED_KEYWORDS}; diff --git a/martin/src/srv/server.rs b/martin/src/srv/server.rs index df755853..d5a97989 100755 --- a/martin/src/srv/server.rs +++ b/martin/src/srv/server.rs @@ -335,29 +335,15 @@ async fn get_tile( // Optimization for a single-source request. let (tile, info) = if path.source_ids.contains(',') { let (sources, use_url_query, info) = sources.get_sources(&path.source_ids, Some(path.z))?; - if sources.is_empty() { - return Err(ErrorNotFound("No valid sources found")); - } let query = if use_url_query { - Some(Query::::from_query(req.query_string())?.into_inner()) + Some(req.query_string()) } else { None }; - let tiles = try_join_all(sources.into_iter().map(|s| s.get_tile(&xyz, &query))) - .await - .map_err(map_internal_error)?; - // Make sure tiles can be concatenated, or if not, that there is only one non-empty tile for each zoom level - // TODO: can zlib, brotli, or zstd be concatenated? - // TODO: implement decompression step for other concatenate-able formats - let can_join = info.format == Format::Mvt - && (info.encoding == Encoding::Uncompressed || info.encoding == Encoding::Gzip); - if !can_join && tiles.iter().filter(|v| !v.is_empty()).count() > 1 { - return Err(ErrorBadRequest(format!( - "Can't merge {info} tiles. Make sure there is only one non-empty tile source at zoom level {}", - xyz.z - )))?; - } - (tiles.concat(), info) + ( + get_composite_tile(sources.as_slice(), info, &xyz, query).await?, + info, + ) } else { let id = &path.source_ids; let zoom = xyz.z; @@ -393,6 +379,47 @@ async fn get_tile( }) } +pub async fn get_composite_tile( + sources: &[&dyn Source], + info: TileInfo, + xyz: &Xyz, + query: Option<&str>, +) -> Result> { + if sources.is_empty() { + return Err(ErrorNotFound("No valid sources found")); + } + let query = if let Some(v) = query { + Some(Query::::from_query(v)?.into_inner()) + } else { + None + }; + let mut tiles = try_join_all(sources.iter().map(|s| s.get_tile(xyz, &query))) + .await + .map_err(map_internal_error)?; + // Make sure tiles can be concatenated, or if not, that there is only one non-empty tile for each zoom level + // TODO: can zlib, brotli, or zstd be concatenated? + // TODO: implement decompression step for other concatenate-able formats + let can_join = info.format == Format::Mvt + && (info.encoding == Encoding::Uncompressed || info.encoding == Encoding::Gzip); + let layer_count = tiles.iter().filter(|v| !v.is_empty()).count(); + if !can_join && layer_count > 1 { + return Err(ErrorBadRequest(format!( + "Can't merge {info} tiles. Make sure there is only one non-empty tile source at zoom level {}", + xyz.z + )))?; + } + Ok( + // Minor optimization to prevent concatenation if there are less than 2 tiles + if layer_count == 1 { + tiles.swap_remove(0) + } else if layer_count == 0 { + Vec::new() + } else { + tiles.concat() + }, + ) +} + fn recompress( mut tile: Vec, mut info: TileInfo, diff --git a/martin/src/utils/error.rs b/martin/src/utils/error.rs index bc28e4ef..e421574b 100644 --- a/martin/src/utils/error.rs +++ b/martin/src/utils/error.rs @@ -52,15 +52,15 @@ pub enum Error { #[error("Unrecognizable connection strings: {0:?}")] UnrecognizableConnections(Vec), - #[error("{0}")] + #[error(transparent)] PostgresError(#[from] PgError), - #[error("{0}")] + #[error(transparent)] FileError(#[from] FileError), - #[error("{0}")] + #[error(transparent)] SpriteError(#[from] SpriteError), - #[error("{0}")] + #[error(transparent)] FontError(#[from] FontError), }