tiles: add osm_multipolygons support

master
Astro 1 year ago
parent 5ed6a336df
commit 3fb1083a7b
  1. 3
      db.sql
  2. 81
      server/src/tiles.rs

@ -36,4 +36,5 @@ CREATE TABLE osm_multipolygon_members (
m_role text,
m_id bigint
);
CREATE INDEX osm_ways_box_coords ON osm_ways USING GIST (box(m_geo));
CREATE INDEX osm_multipolygons_id ON osm_multipolygons USING BTREE (id);
CREATE INDEX osm_multipolygon_members_geo ON osm_multipolygon_members USING GIST (box(m_geo));

@ -30,6 +30,7 @@ impl Grow for Rect<f64> {
struct Shape {
style: &'static tile_style::Style,
path: LineString<f64>,
exclude: Vec<LineString<f64>>,
}
pub fn get_tile(state: State) -> (State, Response<Body>) {
@ -38,11 +39,6 @@ pub fn get_tile(state: State) -> (State, Response<Body>) {
let bounds = te.to_rect();
println!("get_tile@{} {:?}", zoom, bounds);
let app_state = AppState::borrow_from(&state);
let result = app_state.with_db(|db|
db.query("SELECT geo, attrs FROM osm_ways WHERE box(polygon(pclose(geo))) && $1::box", &[
&bounds.grow(1.2)
]).unwrap()
);
const TILE_SIZE: i32 = 256;
let (w, h) = (TILE_SIZE, TILE_SIZE);
@ -61,8 +57,13 @@ pub fn get_tile(state: State) -> (State, Response<Body>) {
h: h as f64,
bounds,
};
let mut shapes: BTreeMap<i32, Vec<Shape>> = BTreeMap::new();
let result = app_state.with_db(|db|
db.query("SELECT geo, attrs FROM osm_ways WHERE box(polygon(pclose(geo))) && $1::box", &[
&bounds.grow(1.2)
]).unwrap()
);
for row in result {
let tags =
if let serde_json::Value::Object(tags) = row.get(1) {
@ -80,24 +81,86 @@ pub fn get_tile(state: State) -> (State, Response<Body>) {
tile_style::find(&zoom, &tags)
.map(|style| {
let path: LineString<f64> = row.get(0);
let shape = Shape { style, path };
let shape = Shape { style, path, exclude: vec![] };
shapes.entry(style.z_index)
.or_default()
.push(shape);
});
}
let result = app_state.with_db(|db| {
let multi_members = db.query("SELECT id, m_role, path(m_geo) FROM osm_multipolygon_members WHERE box(m_geo) && $1::box", &[
&bounds.grow(1.2)
]).unwrap();
let mut members_by_id: HashMap<i64, Vec<(String, LineString<f64>)>> = HashMap::new();
for row in multi_members {
members_by_id.entry(row.get(0))
.or_default()
.push((row.get(1), row.get(2)));
}
let ids: Vec<i64> = members_by_id.keys()
.cloned()
.collect();
let multis = db.query("SELECT id, attrs FROM osm_multipolygons WHERE id=ANY($1)", &[&ids])
.unwrap();
multis.into_iter().map(move |row| {
let id = row.get(0);
let attrs = row.get(1);
let members = members_by_id.remove(&id).unwrap();
(id, attrs, members)
})
});
for (id, attrs, members) in result {
let tags =
if let serde_json::Value::Object(tags) = attrs {
tags.into_iter()
.filter_map(|(k, v)| if let serde_json::Value::String(s) = v {
Some((k, s))
} else {
None
})
.collect::<HashMap<String, String>>()
} else {
continue;
};
tile_style::find(&zoom, &tags)
.map(|style| {
let filter_members = |target_role| members.iter().filter_map(move |(m_role, m_geo)| {
if m_role == target_role {
Some(m_geo)
} else {
None
}
}).cloned();
let inners: Vec<_> = filter_members("inner").collect();
for path in filter_members("outer") {
// TODO: expensive?
let inners = inners.clone();
let shape = Shape { style, path, exclude: inners };
shapes.entry(style.z_index)
.or_default()
.push(shape);
}
});
}
for shapes in shapes.values() {
for Shape { style, path } in shapes {
for Shape { style, path, exclude } in shapes {
pctx.ctx.save().unwrap();
// TODO: multipolygon relations
if path.is_closed() {
style.fill.map(|(r, g, b, a)| {
pctx.ctx.set_source_rgba(r, g, b, a);
pctx.create_path(path);
pctx.ctx.close_path();
if ! exclude.is_empty() {
pctx.ctx.clip_preserve();
for path in exclude {
pctx.create_path(path);
pctx.ctx.close_path();
}
}
pctx.ctx.fill().unwrap();
});
}

Loading…
Cancel
Save