From 204b132a2699d8d1b20a7b1cabefb4e8ef749d87 Mon Sep 17 00:00:00 2001 From: Stepan Kuzmin Date: Thu, 16 Nov 2017 21:14:38 +0300 Subject: [PATCH] feat: add MVT handler --- Cargo.lock | 44 ++++++---------------------- Cargo.toml | 5 ++-- README.md | 2 +- src/main.rs | 83 +++++++++++++++++++---------------------------------- 4 files changed, 41 insertions(+), 93 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1b916238..66842594 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,14 +2,13 @@ name = "falcon" version = "0.1.0" dependencies = [ - "fallible-iterator 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "iron-cors 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "persistent 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "postgres 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", "r2d2 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "r2d2_postgres 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -98,11 +97,6 @@ dependencies = [ "generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "dtoa" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "error" version = "0.1.9" @@ -222,9 +216,13 @@ dependencies = [ ] [[package]] -name = "itoa" -version = "0.3.4" +name = "iron-cors" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "kernel32-sys" @@ -291,11 +289,6 @@ name = "nodrop" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "num-traits" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "num_cpus" version = "1.7.0" @@ -381,7 +374,6 @@ dependencies = [ "hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", "postgres-protocol 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -455,22 +447,6 @@ dependencies = [ "antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "serde" -version = "1.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde_json" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "sha2" version = "0.6.0" @@ -630,7 +606,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" "checksum crypto-mac 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "779015233ac67d65098614aec748ac1c756ab6677fa2e14cf8b37c08dfed1198" "checksum digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b29bf156f3f4b3c4f610a25ff69370616ae6e0657d416de22645483e72af0a" -"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" "checksum error 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "a6e606f14042bb87cc02ef6a14db6c90ab92ed6f62d87e69377bc759fd7987cc" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum fallible-iterator 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5d48ab1bc11a086628e8cc0cc2c2dc200b884ac05c4b48fb71d6036b6999ff1d" @@ -644,7 +619,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" "checksum iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6e8b9c2247fcf6c6a1151f1156932be5606c9fd6f55a2d7f9fc1cb29386b2f7" "checksum iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2440ae846e7a8c7f9b401db8f6e31b4ea5e7d3688b91761337da7e054520c75b" -"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c" +"checksum iron-cors 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8a271fc9d686887269d23bb2b7dea4dd628fb640578d4d5d951368882208b88" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c9e5e58fa1a4c3b915a561a78a22ee0cac6ab97dca2504428bc1cb074375f8d5" @@ -656,7 +631,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" "checksum modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41f5c9112cb662acd3b204077e0de5bc66305fa8df65c8019d5adb10e9ab6e58" "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" -"checksum num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "99843c856d68d8b4313b03a17e33c4bb42ae8f6610ea81b28abe076ac721b9b0" "checksum num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "514f0d73e64be53ff320680ca671b64fe3fb91da01e1ae2ddc99eb51d453b20d" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum persistent 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c9c94f2ef72dc272c6bcc8157ccf2bc7da14f4c58c69059ac2fc48492d6916" @@ -675,8 +649,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" "checksum scheduled-thread-pool 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d9fbe48ead32343b76f544c85953bf260ed39219a8bbbb62cd85f6a00f9644f" -"checksum serde 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "0c9cab69e16835717c9b8bd13c29f92b6aa34fe32ce2866b1ab481cf2da8442a" -"checksum serde_json 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e4586746d1974a030c48919731ecffd0ed28d0c40749d0d18d43b3a7d6c9b20e" "checksum sha2 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d963c78ce367df26d7ea8b8cc655c651b42e8a1e584e869c1e17dae3ccb116a" "checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537" "checksum stringprep 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" diff --git a/Cargo.toml b/Cargo.toml index c5097646..688cc521 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,10 +10,9 @@ path = "src/main.rs" [dependencies] url = "1.6.0" regex = "0.2.2" -fallible-iterator = "0.1.3" iron = "0.5.1" +iron-cors = "0.5.1" persistent = "0.3.0" -serde_json = "1.0.6" r2d2 = "0.7.4" r2d2_postgres = "0.13.0" -postgres = { version = "0.15.1", features = ["with-serde_json", "with-time", "with-uuid"] } +postgres = { version = "0.15.1", features = ["with-time", "with-uuid"] } diff --git a/README.md b/README.md index 93073454..bcd0fef6 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build Status](https://travis-ci.org/stepankuzmin/falcon.svg?branch=master)](https://travis-ci.org/stepankuzmin/falcon) -PostgreSQL RESTful API +PostGIS [Mapbox Vector Tiles](https://github.com/mapbox/vector-tile-spec) server. **Warning: this is experimental** diff --git a/src/main.rs b/src/main.rs index c6423259..2b18f6ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,8 @@ extern crate url; extern crate iron; extern crate regex; +extern crate iron_cors; extern crate persistent; -extern crate serde_json; -extern crate fallible_iterator; extern crate r2d2; extern crate r2d2_postgres; @@ -15,9 +14,8 @@ use iron::prelude::*; use iron::mime; use iron::status; use iron::typemap::Key; +use iron_cors::CorsMiddleware; use persistent::Read; -use serde_json::Value; -use fallible_iterator::FallibleIterator; use r2d2::{Pool, PooledConnection}; use r2d2_postgres::{TlsMode, PostgresConnectionManager}; @@ -36,7 +34,8 @@ fn setup_connection_pool(cn_str: &str, pool_size: u32) -> PostgresPool { fn handler(req: &mut Request) -> IronResult { let url: Url = req.url.clone().into(); - let re = Regex::new(r"^/(?P\w*)/(?P\w*).(?P\w*)$").unwrap(); + let tile_re = r"^/(?P\w*)/(?P
\w*)/(?P\d*)/(?P\d*)/(?P\d*).(?P\w*)$"; + let re = Regex::new(tile_re).unwrap(); match re.captures(&url.path()) { Some(caps) => { println!("{} {} {}", req.method, req.version, req.url); @@ -44,60 +43,35 @@ fn handler(req: &mut Request) -> IronResult { let pool = req.get::>().unwrap(); let conn = pool.get().unwrap(); - match &caps["format"] { - "json" => get_json(conn, &caps["schema"], &caps["table"]), - "geojson" => get_geojson(conn, &caps["schema"], &caps["table"]), - &_ => Ok(Response::with((status::NotFound))) + let query = format!( + "SELECT ST_AsMVT(q, '{1}', 4096, 'geom') FROM ( \ + SELECT ST_AsMVTGeom( \ + geom, \ + TileBBox({2}, {3}, {4}, 4326), \ + 4096, \ + 256, \ + true \ + ) AS geom FROM {0}.{1} \ + ) AS q;", + &caps["schema"], &caps["table"], &caps["z"], &caps["x"], &caps["y"] + ); + + match conn.query(&query, &[]) { + Ok(rows) => { + let content_type = "application/x-protobuf".parse::().unwrap(); + let tile: Vec = rows.get(0).get("st_asmvt"); + match tile.len() { + 0 => Ok(Response::with((content_type, status::NoContent))), + _ => Ok(Response::with((content_type, status::Ok, tile))) + } + }, + Err(e) => Ok(Response::with((status::InternalServerError, e.to_string()))) } }, None => Ok(Response::with((status::NotFound))) } } -fn get_json(conn: PostgresPooledConnection, schema: &str, table: &str) -> IronResult { - let query = format!("select json_agg({1}) from {0}.{1}", schema, table); - - let trans = conn.transaction().unwrap(); - let stmt = trans.prepare(&query).unwrap(); - let mut result = stmt.lazy_query(&trans, &[], 1000).unwrap(); - - let content_type = "application/json".parse::().unwrap(); - match result.next() { - Ok(Some(rows)) => { - let result: Value = rows.get("json_agg"); - let content = serde_json::to_string(&result).unwrap(); - Ok(Response::with((content_type, status::Ok, content))) - }, - Ok(None) => { - let content = "[]"; - Ok(Response::with((content_type, status::Ok, content))) - }, - Err(e) => Ok(Response::with((status::InternalServerError, e.to_string()))) - } -} - -fn get_geojson(conn: PostgresPooledConnection, schema: &str, table: &str) -> IronResult { - let query = format!("select json_agg({1}) from {0}.{1}", schema, table); - - let trans = conn.transaction().unwrap(); - let stmt = trans.prepare(&query).unwrap(); - let mut result = stmt.lazy_query(&trans, &[], 1000).unwrap(); - - let content_type = "application/json".parse::().unwrap(); - match result.next() { - Ok(Some(rows)) => { - let result: Value = rows.get("json_agg"); - let content = serde_json::to_string(&result).unwrap(); - Ok(Response::with((content_type, status::Ok, content))) - }, - Ok(None) => { - let content = "[]"; - Ok(Response::with((content_type, status::Ok, content))) - }, - Err(e) => Ok(Response::with((status::InternalServerError, e.to_string()))) - } -} - fn main() { let conn_string: String = env::var("DATABASE_URL") .expect("DATABASE_URL must be set"); @@ -108,6 +82,9 @@ fn main() { let mut middleware = Chain::new(handler); middleware.link(Read::::both(pool)); + let cors_middleware = CorsMiddleware::with_allow_any(false); + middleware.link_around(cors_middleware); + let port = 3000; let bind_addr = format!("0.0.0.0:{}", port); println!("server has been started on {}.", bind_addr);