mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-28 12:12:00 +03:00
adding tests for map conversion determinism
This commit is contained in:
parent
1c85188a64
commit
5b018fdf06
97
convert_osm/src/lib.rs
Normal file
97
convert_osm/src/lib.rs
Normal 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
|
||||
}
|
@ -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");
|
||||
}
|
||||
|
23
convert_osm/tests/determinism.rs
Normal file
23
convert_osm/tests/determinism.rs
Normal 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");
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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>,
|
||||
|
@ -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};
|
||||
|
@ -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>,
|
||||
|
@ -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,
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user