render individual points from extra KML, and refactor some circle helpers

This commit is contained in:
Dustin Carlino 2018-08-23 14:37:13 -07:00
parent f141329e85
commit f36c94c730
16 changed files with 112 additions and 65 deletions

View File

@ -594,3 +594,9 @@ CarParking
- rename to ParkedCar - rename to ParkedCar
SidewalkSpot SidewalkSpot
- this should cache lane and distance. :) - this should cache lane and distance. :)
## Notes on King County GIS datasets
- TODO: https://data-seattlecitygis.opendata.arcgis.com/datasets/channelization
- https://data-seattlecitygis.opendata.arcgis.com/datasets/street-signs

5
docs/references.md Normal file
View File

@ -0,0 +1,5 @@
# References
## Groups that may be eventually interested
- Seattle Times Traffic Lab

View File

@ -175,9 +175,9 @@
1.0 1.0
], ],
"ExtraShape": [ "ExtraShape": [
0.0,
1.0,
1.0, 1.0,
0.0,
0.0,
1.0 1.0
], ],
"MatchClassification": [ "MatchClassification": [

View File

@ -282,7 +282,7 @@ impl UI {
let hit = vertical_pl.intersection(&horiz_pl).unwrap(); let hit = vertical_pl.intersection(&horiz_pl).unwrap();
if false { if false {
g.draw_ellipse(BLUE, geometry::circle(hit.x(), hit.y(), 1.0)); g.draw_ellipse(BLUE, geometry::make_circle(hit, 1.0));
} else { } else {
vertical_pl.trim_to_pt(hit); vertical_pl.trim_to_pt(hit);
horiz_pl.trim_to_pt(hit); horiz_pl.trim_to_pt(hit);
@ -325,7 +325,7 @@ fn draw_polyline(g: &mut GfxCtx, pl: &PolyLine, thickness: f64, color: Color) {
} }
let radius = 0.5; let radius = 0.5;
for pt in pts { for pt in pts {
g.draw_ellipse(BLUE, geometry::circle(pt.x(), pt.y(), radius)); g.draw_ellipse(BLUE, geometry::make_circle(*pt, radius));
} }
} }

View File

@ -1,7 +1,7 @@
use geom::{Bounds, LonLat, PolyLine, Pt2D}; use geom::{Bounds, LonLat, PolyLine, Pt2D};
use quick_xml::events::Event; use quick_xml::events::Event;
use quick_xml::reader::Reader; use quick_xml::reader::Reader;
use std::collections::HashMap; use std::collections::BTreeMap;
use std::fs::File; use std::fs::File;
use std::{f64, fmt, io}; use std::{f64, fmt, io};
@ -17,8 +17,14 @@ impl fmt::Display for ExtraShapeID {
#[derive(Debug)] #[derive(Debug)]
pub struct ExtraShape { pub struct ExtraShape {
pub id: ExtraShapeID, pub id: ExtraShapeID,
pub pts: PolyLine, pub geom: ExtraShapeGeom,
pub attributes: HashMap<String, String>, pub attributes: BTreeMap<String, String>,
}
#[derive(Debug)]
pub enum ExtraShapeGeom {
Point(Pt2D),
Points(PolyLine),
} }
pub fn load(path: &String, gps_bounds: &Bounds) -> Result<Vec<ExtraShape>, io::Error> { pub fn load(path: &String, gps_bounds: &Bounds) -> Result<Vec<ExtraShape>, io::Error> {
@ -33,7 +39,7 @@ pub fn load(path: &String, gps_bounds: &Bounds) -> Result<Vec<ExtraShape>, io::E
// TODO uncomfortably stateful // TODO uncomfortably stateful
let mut shapes = Vec::new(); let mut shapes = Vec::new();
let mut scanned_schema = false; let mut scanned_schema = false;
let mut attributes: HashMap<String, String> = HashMap::new(); let mut attributes: BTreeMap<String, String> = BTreeMap::new();
let mut attrib_key: Option<String> = None; let mut attrib_key: Option<String> = None;
let mut skipped_count = 0; let mut skipped_count = 0;
@ -75,11 +81,15 @@ pub fn load(path: &String, gps_bounds: &Bounds) -> Result<Vec<ExtraShape>, io::E
break; break;
} }
} }
if ok { if ok && is_interesting_sign(&attributes) {
let id = ExtraShapeID(shapes.len()); let id = ExtraShapeID(shapes.len());
shapes.push(ExtraShape { shapes.push(ExtraShape {
id, id,
pts: PolyLine::new(pts), geom: if pts.len() == 1 {
ExtraShapeGeom::Point(pts[0])
} else {
ExtraShapeGeom::Points(PolyLine::new(pts))
},
attributes: attributes.clone(), attributes: attributes.clone(),
}); });
} else { } else {
@ -126,3 +136,8 @@ fn parse_pt(input: &str, gps_bounds: &Bounds) -> Option<Pt2D> {
_ => None, _ => None,
}; };
} }
// TODO only for Street_Signs.kml; this is temporary to explore stuff
fn is_interesting_sign(attributes: &BTreeMap<String, String>) -> bool {
attributes.get("CATEGORY") == Some(&"REGMIS".to_string())
}

View File

@ -2,42 +2,70 @@ use aabb_quadtree::geom::Rect;
use ezgui::GfxCtx; use ezgui::GfxCtx;
use geom::{Polygon, Pt2D}; use geom::{Polygon, Pt2D};
use graphics::types::Color; use graphics::types::Color;
use kml::{ExtraShape, ExtraShapeID}; use kml::{ExtraShape, ExtraShapeGeom, ExtraShapeID};
use render::{get_bbox, EXTRA_SHAPE_THICKNESS}; use map_model::geometry;
use std::collections::HashMap; use render::{get_bbox, EXTRA_SHAPE_POINT_RADIUS, EXTRA_SHAPE_THICKNESS};
use std::collections::BTreeMap;
#[derive(Debug)]
enum Shape {
Polygon(Polygon),
Circle([f64; 4]),
}
#[derive(Debug)] #[derive(Debug)]
pub struct DrawExtraShape { pub struct DrawExtraShape {
pub id: ExtraShapeID, pub id: ExtraShapeID,
polygon: Polygon, shape: Shape,
attributes: HashMap<String, String>, attributes: BTreeMap<String, String>,
} }
impl DrawExtraShape { impl DrawExtraShape {
pub fn new(s: ExtraShape) -> DrawExtraShape { pub fn new(s: ExtraShape) -> DrawExtraShape {
DrawExtraShape { DrawExtraShape {
id: s.id, id: s.id,
polygon: s.pts.make_polygons(EXTRA_SHAPE_THICKNESS).unwrap(), shape: match s.geom {
ExtraShapeGeom::Point(pt) => {
Shape::Circle(geometry::make_circle(pt, EXTRA_SHAPE_POINT_RADIUS))
}
ExtraShapeGeom::Points(pl) => {
Shape::Polygon(pl.make_polygons(EXTRA_SHAPE_THICKNESS).unwrap())
}
},
attributes: s.attributes, attributes: s.attributes,
} }
} }
pub fn draw(&self, g: &mut GfxCtx, color: Color) { pub fn draw(&self, g: &mut GfxCtx, color: Color) {
g.draw_polygon(color, &self.polygon); match self.shape {
Shape::Polygon(ref p) => g.draw_polygon(color, &p),
Shape::Circle(c) => g.draw_ellipse(color, c),
}
} }
pub fn contains_pt(&self, pt: Pt2D) -> bool { pub fn contains_pt(&self, pt: Pt2D) -> bool {
self.polygon.contains_pt(pt) match self.shape {
Shape::Polygon(ref p) => p.contains_pt(pt),
Shape::Circle(c) => geometry::point_in_circle(&c, pt),
}
} }
pub fn get_bbox(&self) -> Rect { pub fn get_bbox(&self) -> Rect {
get_bbox(&self.polygon.get_bounds()) match self.shape {
Shape::Polygon(ref p) => get_bbox(&p.get_bounds()),
Shape::Circle(c) => geometry::circle_to_bbox(&c),
}
} }
pub fn tooltip_lines(&self) -> Vec<String> { pub fn tooltip_lines(&self) -> Vec<String> {
let mut lines = Vec::new(); let mut lines = Vec::new();
for (k, v) in &self.attributes { for (k, v) in &self.attributes {
lines.push(format!("{} = {}", k, v)); // Make interesting atributes easier to spot
if k == "TEXT" {
lines.push(format!("*** {} = {}", k, v));
} else {
lines.push(format!("{} = {}", k, v));
}
} }
lines lines
} }

View File

@ -111,17 +111,17 @@ impl DrawIntersection {
g.draw_ellipse( g.draw_ellipse(
cs.get(Colors::TrafficSignalYellow), cs.get(Colors::TrafficSignalYellow),
geometry::circle(self.center.x(), self.center.y(), radius), geometry::make_circle(self.center, radius),
); );
g.draw_ellipse( g.draw_ellipse(
cs.get(Colors::TrafficSignalGreen), cs.get(Colors::TrafficSignalGreen),
geometry::circle(self.center.x(), self.center.y() + (radius * 2.0), radius), geometry::make_circle(self.center.offset(0.0, radius * 2.0), radius),
); );
g.draw_ellipse( g.draw_ellipse(
cs.get(Colors::TrafficSignalRed), cs.get(Colors::TrafficSignalRed),
geometry::circle(self.center.x(), self.center.y() - (radius * 2.0), radius), geometry::make_circle(self.center.offset(0.0, radius * -2.0), radius),
); );
} }
} }

View File

@ -108,8 +108,8 @@ impl DrawLane {
for pair in l.lane_center_pts.points().windows(2) { for pair in l.lane_center_pts.points().windows(2) {
let (pt1, pt2) = (pair[0], pair[1]); let (pt1, pt2) = (pair[0], pair[1]);
g.draw_line(&line, [pt1.x(), pt1.y(), pt2.x(), pt2.y()]); g.draw_line(&line, [pt1.x(), pt1.y(), pt2.x(), pt2.y()]);
g.draw_ellipse(circle_color, geometry::circle(pt1.x(), pt1.y(), 0.4)); g.draw_ellipse(circle_color, geometry::make_circle(pt1, 0.4));
g.draw_ellipse(circle_color, geometry::circle(pt2.x(), pt2.y(), 0.8)); g.draw_ellipse(circle_color, geometry::make_circle(pt2, 0.8));
} }
} }

View File

@ -20,6 +20,7 @@ use std::f64;
const PARCEL_BOUNDARY_THICKNESS: f64 = 0.5; const PARCEL_BOUNDARY_THICKNESS: f64 = 0.5;
const BUILDING_BOUNDARY_THICKNESS: f64 = 1.5; const BUILDING_BOUNDARY_THICKNESS: f64 = 1.5;
const EXTRA_SHAPE_THICKNESS: f64 = 1.0; const EXTRA_SHAPE_THICKNESS: f64 = 1.0;
const EXTRA_SHAPE_POINT_RADIUS: f64 = 1.0;
const TURN_ICON_ARROW_THICKNESS: f64 = geometry::BIG_ARROW_THICKNESS / 3.0; const TURN_ICON_ARROW_THICKNESS: f64 = geometry::BIG_ARROW_THICKNESS / 3.0;
const BIG_ARROW_TIP_LENGTH: f64 = 1.0; const BIG_ARROW_TIP_LENGTH: f64 = 1.0;

View File

@ -45,11 +45,7 @@ impl DrawTurn {
.project_away(TURN_ICON_ARROW_LENGTH / 2.0, angle) .project_away(TURN_ICON_ARROW_LENGTH / 2.0, angle)
.to_vec(); .to_vec();
let icon_circle = geometry::circle( let icon_circle = geometry::make_circle(icon_center, TURN_ICON_ARROW_LENGTH / 2.0);
icon_center.x(),
icon_center.y(),
TURN_ICON_ARROW_LENGTH / 2.0,
);
let icon_arrow = [icon_src[0], icon_src[1], icon_dst[0], icon_dst[1]]; let icon_arrow = [icon_src[0], icon_src[1], icon_dst[0], icon_dst[1]];
@ -87,12 +83,6 @@ impl DrawTurn {
// for the icon // for the icon
pub fn contains_pt(&self, pt: Pt2D) -> bool { pub fn contains_pt(&self, pt: Pt2D) -> bool {
let radius = self.icon_circle[2] / 2.0; geometry::point_in_circle(&self.icon_circle, pt)
geometry::point_in_circle(
pt.x(),
pt.y(),
[self.icon_circle[0] + radius, self.icon_circle[1] + radius],
radius,
)
} }
} }

View File

@ -183,6 +183,16 @@ impl UI {
let screen_bbox = self.canvas.get_screen_bbox(); let screen_bbox = self.canvas.get_screen_bbox();
if self.show_extra_shapes.is_enabled() {
for s in &self.draw_map
.get_extra_shapes_onscreen(screen_bbox, &self.hider)
{
if s.contains_pt(pt) {
return Some(ID::ExtraShape(s.id));
}
}
}
let lanes_onscreen = if self.show_lanes.is_enabled() { let lanes_onscreen = if self.show_lanes.is_enabled() {
self.draw_map.get_loads_onscreen(screen_bbox, &self.hider) self.draw_map.get_loads_onscreen(screen_bbox, &self.hider)
} else { } else {
@ -230,16 +240,6 @@ impl UI {
} }
} }
if self.show_extra_shapes.is_enabled() {
for s in &self.draw_map
.get_extra_shapes_onscreen(screen_bbox, &self.hider)
{
if s.contains_pt(pt) {
return Some(ID::ExtraShape(s.id));
}
}
}
if self.show_lanes.is_enabled() { if self.show_lanes.is_enabled() {
for l in &lanes_onscreen { for l in &lanes_onscreen {
if l.contains_pt(pt) { if l.contains_pt(pt) {

View File

@ -73,6 +73,10 @@ impl Pt2D {
// DON'T invert y here // DON'T invert y here
Angle::new((to.y() - self.y()).atan2(to.x() - self.x())) Angle::new((to.y() - self.y()).atan2(to.x() - self.x()))
} }
pub fn offset(&self, dx: f64, dy: f64) -> Pt2D {
Pt2D::new(self.x() + dx, self.y() + dy)
}
} }
impl fmt::Display for Pt2D { impl fmt::Display for Pt2D {

View File

@ -10,7 +10,6 @@ dimensioned = { git = "https://github.com/paholg/dimensioned", rev = "0e1076ebfa
geo = "0.9.1" geo = "0.9.1"
geom = { path = "../geom" } geom = { path = "../geom" }
ordered-float = "0.5.0" ordered-float = "0.5.0"
piston2d-graphics = "*"
pretty_assertions = "0.5.1" pretty_assertions = "0.5.1"
serde = "1.0" serde = "1.0"
serde_derive = "1.0" serde_derive = "1.0"

View File

@ -2,7 +2,6 @@
use aabb_quadtree::geom::{Point, Rect}; use aabb_quadtree::geom::{Point, Rect};
use geom::{Angle, PolyLine, Polygon, Pt2D}; use geom::{Angle, PolyLine, Polygon, Pt2D};
use graphics::math::Vec2d;
use std::f64; use std::f64;
pub const LANE_THICKNESS: f64 = 2.5; pub const LANE_THICKNESS: f64 = 2.5;
@ -14,15 +13,22 @@ pub fn thick_line_from_angle(thickness: f64, line_length: f64, pt: Pt2D, angle:
PolyLine::new(vec![pt, pt2]).make_polygons_blindly(thickness) PolyLine::new(vec![pt, pt2]).make_polygons_blindly(thickness)
} }
pub fn point_in_circle(x: f64, y: f64, center: Vec2d, radius: f64) -> bool { pub fn center_of_circle(c: &[f64; 4]) -> Pt2D {
// avoid sqrt by squaring radius instead let radius = c[2] / 2.0;
(x - center[0]).powi(2) + (y - center[1]).powi(2) < radius.powi(2) Pt2D::new(c[0] + radius, c[1] + radius)
} }
pub fn circle(center_x: f64, center_y: f64, radius: f64) -> [f64; 4] { pub fn point_in_circle(c: &[f64; 4], pt: Pt2D) -> bool {
let radius = c[2] / 2.0;
let center = center_of_circle(c);
// avoid sqrt by squaring radius instead
(pt.x() - center.x()).powi(2) + (pt.y() - center.y()).powi(2) < radius.powi(2)
}
pub fn make_circle(center: Pt2D, radius: f64) -> [f64; 4] {
[ [
center_x - radius, center.x() - radius,
center_y - radius, center.y() - radius,
2.0 * radius, 2.0 * radius,
2.0 * radius, 2.0 * radius,
] ]

View File

@ -3,7 +3,6 @@ extern crate abstutil;
extern crate dimensioned; extern crate dimensioned;
extern crate geo; extern crate geo;
extern crate geom; extern crate geom;
extern crate graphics;
extern crate ordered_float; extern crate ordered_float;
#[macro_use] #[macro_use]
extern crate pretty_assertions; extern crate pretty_assertions;

View File

@ -26,7 +26,7 @@ impl DrawPedestrian {
DrawPedestrian { DrawPedestrian {
id, id,
circle: geometry::circle(pos.x(), pos.y(), RADIUS), circle: geometry::make_circle(pos, RADIUS),
turn_arrow, turn_arrow,
} }
} }
@ -45,16 +45,10 @@ impl DrawPedestrian {
} }
pub fn contains_pt(&self, pt: Pt2D) -> bool { pub fn contains_pt(&self, pt: Pt2D) -> bool {
geometry::point_in_circle( geometry::point_in_circle(&self.circle, pt)
pt.x(),
pt.y(),
[self.circle[0] + RADIUS, self.circle[1] + RADIUS],
RADIUS,
)
} }
pub fn focus_pt(&self) -> Pt2D { pub fn focus_pt(&self) -> Pt2D {
let radius = self.circle[2] / 2.0; geometry::center_of_circle(&self.circle)
Pt2D::new(self.circle[0] + radius, self.circle[1] + radius)
} }
} }