adding tests for map conversion determinism

This commit is contained in:
Dustin Carlino 2018-07-22 16:43:33 -07:00
parent 1c85188a64
commit 5b018fdf06
8 changed files with 138 additions and 108 deletions

97
convert_osm/src/lib.rs Normal file
View File

@ -0,0 +1,97 @@
extern crate abstutil;
extern crate byteorder;
extern crate geom;
extern crate map_model;
extern crate ordered_float;
extern crate osm_xml;
extern crate shp;
#[macro_use]
extern crate structopt;
mod osm;
mod remove_disconnected;
mod split_ways;
mod srtm;
mod traffic_signals;
use geom::LonLat;
use map_model::raw_data;
use ordered_float::NotNaN;
use srtm::Elevation;
const MAX_METERS_BTWN_INTERSECTION_AND_SIGNAL: f64 = 50.0;
#[derive(StructOpt, Debug)]
#[structopt(name = "convert_osm")]
pub struct Flags {
/// OSM XML file to read
#[structopt(long = "osm")]
pub osm: String,
/// HGT with elevation data
#[structopt(long = "elevation")]
pub elevation: String,
/// SHP with traffic signals
#[structopt(long = "traffic_signals")]
pub traffic_signals: String,
/// .abst with parcels, produced using the kml crate
#[structopt(long = "parcels")]
pub parcels: String,
/// Output .abst path
#[structopt(long = "output")]
pub output: String,
}
pub fn convert(flags: &Flags) -> raw_data::Map {
let elevation = Elevation::new(&flags.elevation).expect("loading .hgt failed");
let (map, bounds) = osm::osm_to_raw_roads(&flags.osm);
let mut map = split_ways::split_up_roads(&map, &elevation);
// TODO get bounds here instead
remove_disconnected::remove_disconnected_roads(&mut map);
println!("Loading parcels from {}", flags.parcels);
let parcels_map: raw_data::Map =
abstutil::read_binary(&flags.parcels).expect("loading parcels failed");
println!(
"Finding matching parcels from {} candidates",
parcels_map.parcels.len()
);
for p in parcels_map.parcels {
if p.points
.iter()
.find(|pt| !bounds.contains(pt.longitude, pt.latitude))
.is_none()
{
map.parcels.push(p);
}
}
for coord in
&traffic_signals::extract(&flags.traffic_signals).expect("loading traffic signals failed")
{
if bounds.contains(coord.longitude, coord.latitude) {
let distance = |i: &raw_data::Intersection| {
coord.gps_dist_meters(LonLat::new(i.point.longitude, i.point.latitude))
};
// TODO use a quadtree or some better way to match signals to the closest
// intersection
let closest_intersection = map.intersections
.iter_mut()
.min_by_key(|i| NotNaN::new(distance(i)).unwrap())
.unwrap();
let dist = distance(closest_intersection);
if dist <= MAX_METERS_BTWN_INTERSECTION_AND_SIGNAL {
if closest_intersection.has_traffic_signal {
println!("WARNING: {:?} already has a traffic signal, but there's another one that's {} from it", closest_intersection, dist);
}
closest_intersection.has_traffic_signal = true;
}
}
}
map
}

View File

@ -1,103 +1,13 @@
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
extern crate abstutil;
extern crate byteorder;
extern crate geom;
extern crate map_model;
extern crate ordered_float;
extern crate osm_xml;
extern crate shp;
#[macro_use]
extern crate convert_osm;
extern crate structopt;
mod osm;
mod remove_disconnected;
mod split_ways;
mod srtm;
mod traffic_signals;
use geom::LonLat;
use map_model::raw_data;
use ordered_float::NotNaN;
use srtm::Elevation;
use convert_osm::{convert, Flags};
use structopt::StructOpt;
const MAX_METERS_BTWN_INTERSECTION_AND_SIGNAL: f64 = 50.0;
#[derive(StructOpt, Debug)]
#[structopt(name = "convert_osm")]
struct Flags {
/// OSM XML file to read
#[structopt(long = "osm")]
osm: String,
/// HGT with elevation data
#[structopt(long = "elevation")]
elevation: String,
/// SHP with traffic signals
#[structopt(long = "traffic_signals")]
traffic_signals: String,
/// .abst with parcels, produced using the kml crate
#[structopt(long = "parcels")]
parcels: String,
/// Output .abst path
#[structopt(long = "output")]
output: String,
}
fn main() {
let flags = Flags::from_args();
let elevation = Elevation::new(&flags.elevation).expect("loading .hgt failed");
let (map, bounds) = osm::osm_to_raw_roads(&flags.osm);
let mut map = split_ways::split_up_roads(&map, &elevation);
// TODO get bounds here instead
remove_disconnected::remove_disconnected_roads(&mut map);
println!("Loading parcels from {}", flags.parcels);
let parcels_map: raw_data::Map =
abstutil::read_binary(&flags.parcels).expect("loading parcels failed");
println!(
"Finding matching parcels from {} candidates",
parcels_map.parcels.len()
);
for p in parcels_map.parcels {
if p.points
.iter()
.find(|pt| !bounds.contains(pt.longitude, pt.latitude))
.is_none()
{
map.parcels.push(p);
}
}
for coord in
&traffic_signals::extract(&flags.traffic_signals).expect("loading traffic signals failed")
{
if bounds.contains(coord.longitude, coord.latitude) {
let distance = |i: &raw_data::Intersection| {
coord.gps_dist_meters(LonLat::new(i.point.longitude, i.point.latitude))
};
// TODO use a quadtree or some better way to match signals to the closest
// intersection
let closest_intersection = map.intersections
.iter_mut()
.min_by_key(|i| NotNaN::new(distance(i)).unwrap())
.unwrap();
let dist = distance(closest_intersection);
if dist <= MAX_METERS_BTWN_INTERSECTION_AND_SIGNAL {
if closest_intersection.has_traffic_signal {
println!("WARNING: {:?} already has a traffic signal, but there's another one that's {} from it", closest_intersection, dist);
}
closest_intersection.has_traffic_signal = true;
}
}
}
let map = convert(&flags);
println!("writing to {}", flags.output);
abstutil::write_binary(&flags.output, &map).expect("serializing map failed");
}

View File

@ -0,0 +1,23 @@
extern crate abstutil;
extern crate convert_osm;
#[test]
fn convert_twice() {
let flags = convert_osm::Flags {
osm: "../data/input/tiny_montlake.osm".to_string(),
elevation: "../data/input/N47W122.hgt".to_string(),
traffic_signals: "../data/input/TrafficSignals.shp".to_string(),
parcels: "../data/seattle_parcels.abst".to_string(),
output: "".to_string(),
};
let map1 = convert_osm::convert(&flags);
let map2 = convert_osm::convert(&flags);
if map1 != map2 {
// TODO tmp files
abstutil::write_json("map1.json", &map1).unwrap();
abstutil::write_json("map2.json", &map2).unwrap();
panic!("map1.json and map2.json differ");
}
}

View File

@ -1,7 +1,7 @@
use pt::HashablePt2D;
// longitude is x, latitude is y
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct LonLat {
pub longitude: f64,
pub latitude: f64,

View File

@ -1,7 +1,7 @@
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
use geom::{Line, Pt2D};
use std::collections::HashMap;
use std::collections::BTreeMap;
use std::fmt;
// TODO reconsider pub usize. maybe outside world shouldnt know.
@ -18,7 +18,7 @@ impl fmt::Display for BuildingID {
pub struct Building {
pub id: BuildingID,
pub points: Vec<Pt2D>,
pub osm_tags: HashMap<String, String>,
pub osm_tags: BTreeMap<String, String>,
pub osm_way_id: i64,
pub front_path: Option<Line>,

View File

@ -3,7 +3,7 @@ use geom::{Bounds, Line, Pt2D};
use geometry;
use ordered_float::NotNaN;
use raw_data;
use std::collections::HashMap;
use std::collections::BTreeMap;
use {Building, BuildingID, LaneType, Road, RoadID};
pub(crate) fn make_building(
@ -30,7 +30,7 @@ pub(crate) fn make_building(
fn find_front_path(
bldg_points: &Vec<Pt2D>,
bldg_osm_tags: &HashMap<String, String>,
bldg_osm_tags: &BTreeMap<String, String>,
roads: &Vec<Road>,
) -> Option<Line> {
use geo::prelude::{ClosestPoint, EuclideanDistance};

View File

@ -1,7 +1,7 @@
use geom::{Bounds, HashablePt2D, LonLat};
use std::collections::HashMap;
use std::collections::BTreeMap;
#[derive(Debug, Serialize, Deserialize)]
#[derive(PartialEq, Debug, Serialize, Deserialize)]
pub struct Map {
pub roads: Vec<Road>,
pub intersections: Vec<Intersection>,
@ -45,10 +45,10 @@ impl Map {
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct Road {
pub points: Vec<LonLat>,
pub osm_tags: HashMap<String, String>,
pub osm_tags: BTreeMap<String, String>,
pub osm_way_id: i64,
}
@ -62,22 +62,22 @@ impl Road {
}
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(PartialEq, Debug, Serialize, Deserialize)]
pub struct Intersection {
pub point: LonLat,
pub elevation_meters: f64,
pub has_traffic_signal: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct Building {
// last point never the first?
pub points: Vec<LonLat>,
pub osm_tags: HashMap<String, String>,
pub osm_tags: BTreeMap<String, String>,
pub osm_way_id: i64,
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(PartialEq, Debug, Serialize, Deserialize)]
pub struct Parcel {
// last point never the first?
pub points: Vec<LonLat>,

View File

@ -3,7 +3,7 @@
use dimensioned::si;
use geom::{Angle, Line, PolyLine, Pt2D};
use std;
use std::collections::HashMap;
use std::collections::BTreeMap;
use std::f64;
use std::fmt;
use IntersectionID;
@ -34,7 +34,7 @@ pub enum LaneType {
#[derive(Debug)]
pub struct Road {
pub id: RoadID,
pub osm_tags: HashMap<String, String>,
pub osm_tags: BTreeMap<String, String>,
pub osm_way_id: i64,
pub lane_type: LaneType,