mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-27 06:52:10 +03:00
Store lane_specs_ltr on RawRoad, instead of constantly recalculating. https://github.com/a-b-street/osm2streets/issues/2
This commit is contained in:
parent
2c3157e889
commit
f72dd62e56
@ -28,7 +28,7 @@ impl EditRoad {
|
||||
for (k, v) in road.osm_tags.inner() {
|
||||
txt.add_line(Line(format!("{} = {}", k, v)).secondary());
|
||||
}
|
||||
if let Ok((pl, _)) = app.model.map.untrimmed_road_geometry(r) {
|
||||
if let Ok((pl, _)) = road.untrimmed_road_geometry() {
|
||||
txt.add_line(Line(format!("Length before trimming: {}", pl.length())));
|
||||
}
|
||||
if let Ok(pl) = app.model.map.trimmed_road_geometry(r) {
|
||||
|
@ -262,9 +262,9 @@ impl Model {
|
||||
// Roads
|
||||
impl Model {
|
||||
pub fn road_added(&mut self, ctx: &EventCtx, id: OriginalRoad) {
|
||||
let (center, total_width) = self.map.untrimmed_road_geometry(id).unwrap();
|
||||
let hitbox = center.make_polygons(total_width);
|
||||
let road = &self.map.roads[&id];
|
||||
let (center, total_width) = road.untrimmed_road_geometry().unwrap();
|
||||
let hitbox = center.make_polygons(total_width);
|
||||
let mut draw = GeomBatch::new();
|
||||
draw.push(
|
||||
if road.osm_tags.is("junction", "intersection") {
|
||||
@ -338,6 +338,7 @@ impl Model {
|
||||
self.map.intersections[&i2].point,
|
||||
],
|
||||
osm_tags,
|
||||
&self.map.config,
|
||||
),
|
||||
);
|
||||
self.road_added(ctx, id);
|
||||
|
@ -109,8 +109,10 @@ pub fn extract_osm(
|
||||
way.tags.insert(osm::SIDEWALK, "right");
|
||||
}
|
||||
|
||||
out.roads
|
||||
.push((id, RawRoad::new(way.pts.clone(), way.tags.clone())));
|
||||
out.roads.push((
|
||||
id,
|
||||
RawRoad::new(way.pts.clone(), way.tags.clone(), &opts.map_config),
|
||||
));
|
||||
continue;
|
||||
} else if way.tags.is(osm::HIGHWAY, "service") {
|
||||
// If we got here, is_road didn't interpret it as a normal road
|
||||
|
@ -31,6 +31,8 @@ pub fn apply_parking(map: &mut RawMap, opts: &Options, timer: &mut Timer) {
|
||||
} else {
|
||||
r.osm_tags.insert(osm::PARKING_BOTH, "parallel");
|
||||
}
|
||||
|
||||
r.lane_specs_ltr = raw_map::get_lane_specs_ltr(&r.osm_tags, &opts.map_config);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -138,6 +140,9 @@ fn use_parking_hints(map: &mut RawMap, path: String, timer: &mut Timer) {
|
||||
tags.remove(osm::PARKING_RIGHT).unwrap();
|
||||
tags.insert(osm::PARKING_BOTH, value);
|
||||
}
|
||||
|
||||
let lane_specs_ltr = raw_map::get_lane_specs_ltr(tags, &map.config);
|
||||
map.roads.get_mut(&r).unwrap().lane_specs_ltr = lane_specs_ltr;
|
||||
}
|
||||
}
|
||||
timer.stop("apply parking hints");
|
||||
|
2310
data/MANIFEST.json
2310
data/MANIFEST.json
File diff suppressed because it is too large
Load Diff
@ -142,7 +142,7 @@ impl Map {
|
||||
orig_id: r.id,
|
||||
lanes: Vec::new(),
|
||||
center_pts: r.trimmed_center_pts,
|
||||
untrimmed_center_pts: raw.untrimmed_road_geometry(r.id).unwrap().0,
|
||||
untrimmed_center_pts: raw_road.untrimmed_road_geometry().unwrap().0,
|
||||
src_i: i1,
|
||||
dst_i: i2,
|
||||
speed_limit: Speed::ZERO,
|
||||
@ -156,7 +156,7 @@ impl Map {
|
||||
road.speed_limit = road.speed_limit_from_osm();
|
||||
road.access_restrictions = road.access_restrictions_from_osm();
|
||||
|
||||
road.recreate_lanes(r.lane_specs_ltr);
|
||||
road.recreate_lanes(raw_road.lane_specs_ltr.clone());
|
||||
for lane in &road.lanes {
|
||||
map.intersections[lane.src_i.0].outgoing_lanes.push(lane.id);
|
||||
map.intersections[lane.dst_i.0].incoming_lanes.push(lane.id);
|
||||
|
@ -11,7 +11,7 @@ impl RawMap {
|
||||
pub fn save_osm2polygon_input(&self, output_path: String, i: osm::NodeID) -> Result<()> {
|
||||
let mut features = Vec::new();
|
||||
for id in self.roads_per_intersection(i) {
|
||||
let (untrimmed_center_pts, total_width) = self.untrimmed_road_geometry(id)?;
|
||||
let (untrimmed_center_pts, total_width) = self.roads[&id].untrimmed_road_geometry()?;
|
||||
|
||||
let mut properties = serde_json::Map::new();
|
||||
properties.insert("osm_way_id".to_string(), id.osm_way_id.0.into());
|
||||
|
@ -8,7 +8,7 @@ use anyhow::Result;
|
||||
use abstutil::{Tags, Timer};
|
||||
use geom::{Bounds, Circle, Distance, PolyLine, Polygon, Pt2D};
|
||||
|
||||
use crate::{osm, InputRoad, IntersectionType, LaneSpec, OriginalRoad, RawMap};
|
||||
use crate::{osm, InputRoad, IntersectionType, OriginalRoad, RawMap};
|
||||
|
||||
pub struct InitialMap {
|
||||
pub roads: BTreeMap<OriginalRoad, Road>,
|
||||
@ -26,18 +26,13 @@ pub struct Road {
|
||||
// The true center of the road, including sidewalks
|
||||
pub trimmed_center_pts: PolyLine,
|
||||
pub half_width: Distance,
|
||||
pub lane_specs_ltr: Vec<LaneSpec>,
|
||||
pub osm_tags: Tags,
|
||||
}
|
||||
|
||||
impl Road {
|
||||
pub fn new(map: &RawMap, id: OriginalRoad) -> Result<Road> {
|
||||
let road = &map.roads[&id];
|
||||
let mut lane_specs_ltr = crate::lane_specs::get_lane_specs_ltr(&road.osm_tags, &map.config);
|
||||
for l in &mut lane_specs_ltr {
|
||||
l.width *= road.scale_width;
|
||||
}
|
||||
let (trimmed_center_pts, total_width) = map.untrimmed_road_geometry(id)?;
|
||||
let (trimmed_center_pts, total_width) = road.untrimmed_road_geometry()?;
|
||||
|
||||
Ok(Road {
|
||||
id,
|
||||
@ -45,7 +40,6 @@ impl Road {
|
||||
dst_i: id.i2,
|
||||
trimmed_center_pts,
|
||||
half_width: total_width / 2.0,
|
||||
lane_specs_ltr,
|
||||
osm_tags: road.osm_tags.clone(),
|
||||
})
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ extern crate log;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use anyhow::Result;
|
||||
use petgraph::graphmap::DiGraphMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -268,40 +268,6 @@ impl RawMap {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the corrected (but untrimmed) center and total width for a road
|
||||
pub fn untrimmed_road_geometry(&self, id: OriginalRoad) -> Result<(PolyLine, Distance)> {
|
||||
let road = &self.roads[&id];
|
||||
let lane_specs = get_lane_specs_ltr(&road.osm_tags, &self.config);
|
||||
let mut total_width = Distance::ZERO;
|
||||
let mut sidewalk_right = None;
|
||||
let mut sidewalk_left = None;
|
||||
for l in &lane_specs {
|
||||
total_width += l.width;
|
||||
if l.lt.is_walkable() {
|
||||
if l.dir == Direction::Back {
|
||||
sidewalk_left = Some(l.width);
|
||||
} else {
|
||||
sidewalk_right = Some(l.width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there's a sidewalk on only one side, adjust the true center of the road.
|
||||
let mut true_center =
|
||||
PolyLine::new(road.center_points.clone()).with_context(|| id.to_string())?;
|
||||
match (sidewalk_right, sidewalk_left) {
|
||||
(Some(w), None) => {
|
||||
true_center = true_center.must_shift_right(w / 2.0);
|
||||
}
|
||||
(None, Some(w)) => {
|
||||
true_center = true_center.must_shift_right(w / 2.0);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok((true_center, road.scale_width * total_width))
|
||||
}
|
||||
|
||||
pub fn save(&self) {
|
||||
abstio::write_binary(abstio::path_raw_map(&self.name), self)
|
||||
}
|
||||
@ -377,8 +343,6 @@ pub struct RawRoad {
|
||||
/// cul-de-sac roads for roundabout handling. No transformation of these points whatsoever has
|
||||
/// happened.
|
||||
pub center_points: Vec<Pt2D>,
|
||||
/// Multiply the width of each lane by this ratio, to prevent overlapping roads.
|
||||
pub scale_width: f64,
|
||||
pub osm_tags: Tags,
|
||||
pub turn_restrictions: Vec<(RestrictionType, OriginalRoad)>,
|
||||
/// (via, to). For turn restrictions where 'via' is an entire road. Only BanTurns.
|
||||
@ -387,13 +351,17 @@ pub struct RawRoad {
|
||||
/// Is there a tagged crosswalk near each end of the road?
|
||||
pub crosswalk_forward: bool,
|
||||
pub crosswalk_backward: bool,
|
||||
|
||||
/// Derived from osm_tags. Not automatically updated.
|
||||
pub lane_specs_ltr: Vec<LaneSpec>,
|
||||
}
|
||||
|
||||
impl RawRoad {
|
||||
pub fn new(osm_center_points: Vec<Pt2D>, osm_tags: Tags) -> Self {
|
||||
pub fn new(osm_center_points: Vec<Pt2D>, osm_tags: Tags, config: &MapConfig) -> Self {
|
||||
let lane_specs_ltr = get_lane_specs_ltr(&osm_tags, config);
|
||||
|
||||
Self {
|
||||
center_points: osm_center_points,
|
||||
scale_width: 1.0,
|
||||
osm_tags,
|
||||
turn_restrictions: Vec::new(),
|
||||
complicated_turn_restrictions: Vec::new(),
|
||||
@ -402,6 +370,8 @@ impl RawRoad {
|
||||
// later
|
||||
crosswalk_forward: true,
|
||||
crosswalk_backward: true,
|
||||
|
||||
lane_specs_ltr,
|
||||
}
|
||||
}
|
||||
|
||||
@ -428,10 +398,10 @@ impl RawRoad {
|
||||
self.osm_tags.is(osm::HIGHWAY, "service")
|
||||
}
|
||||
|
||||
pub fn is_cycleway(&self, cfg: &MapConfig) -> bool {
|
||||
pub fn is_cycleway(&self) -> bool {
|
||||
// Don't repeat the logic looking at the tags, just see what lanes we'll create
|
||||
let mut bike = false;
|
||||
for spec in get_lane_specs_ltr(&self.osm_tags, cfg) {
|
||||
for spec in &self.lane_specs_ltr {
|
||||
if spec.lt == LaneType::Biking {
|
||||
bike = true;
|
||||
} else if spec.lt != LaneType::Shoulder {
|
||||
@ -441,9 +411,9 @@ impl RawRoad {
|
||||
bike
|
||||
}
|
||||
|
||||
pub fn is_driveable(&self, cfg: &MapConfig) -> bool {
|
||||
get_lane_specs_ltr(&self.osm_tags, cfg)
|
||||
.into_iter()
|
||||
pub fn is_driveable(&self) -> bool {
|
||||
self.lane_specs_ltr
|
||||
.iter()
|
||||
.any(|spec| spec.lt == LaneType::Driving)
|
||||
}
|
||||
|
||||
@ -478,6 +448,39 @@ impl RawRoad {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the corrected (but untrimmed) center and total width for a road
|
||||
pub fn untrimmed_road_geometry(&self) -> Result<(PolyLine, Distance)> {
|
||||
let mut total_width = Distance::ZERO;
|
||||
let mut sidewalk_right = None;
|
||||
let mut sidewalk_left = None;
|
||||
for l in &self.lane_specs_ltr {
|
||||
total_width += l.width;
|
||||
if l.lt.is_walkable() {
|
||||
if l.dir == Direction::Back {
|
||||
sidewalk_left = Some(l.width);
|
||||
} else {
|
||||
sidewalk_right = Some(l.width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there's a sidewalk on only one side, adjust the true center of the road.
|
||||
// TODO I don't remember the rationale for doing this in the first place. What if there's a
|
||||
// shoulder and a sidewalk of different widths? We don't do anything then
|
||||
let mut true_center = PolyLine::new(self.center_points.clone())?;
|
||||
match (sidewalk_right, sidewalk_left) {
|
||||
(Some(w), None) => {
|
||||
true_center = true_center.must_shift_right(w / 2.0);
|
||||
}
|
||||
(None, Some(w)) => {
|
||||
true_center = true_center.must_shift_right(w / 2.0);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok((true_center, total_width))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
|
@ -4,9 +4,8 @@ use anyhow::Result;
|
||||
|
||||
use geom::{Distance, Pt2D};
|
||||
|
||||
use crate::lane_specs::get_lane_specs_ltr;
|
||||
use crate::osm::NodeID;
|
||||
use crate::{osm, IntersectionType, LaneSpec, LaneType, OriginalRoad, RawMap};
|
||||
use crate::{osm, IntersectionType, OriginalRoad, RawMap};
|
||||
|
||||
/// Collapse degenerate intersections:
|
||||
/// - between two cycleways
|
||||
@ -54,9 +53,7 @@ fn should_collapse(r1: OriginalRoad, r2: OriginalRoad, raw: &RawMap) -> Result<(
|
||||
bail!("oneway roads point at each other");
|
||||
}
|
||||
|
||||
let lanes1 = get_lane_specs_ltr(&road1.osm_tags, &raw.config);
|
||||
let lanes2 = get_lane_specs_ltr(&road2.osm_tags, &raw.config);
|
||||
if lanes1 != lanes2 {
|
||||
if road1.lane_specs_ltr != road2.lane_specs_ltr {
|
||||
bail!("lane specs don't match");
|
||||
}
|
||||
|
||||
@ -64,7 +61,7 @@ fn should_collapse(r1: OriginalRoad, r2: OriginalRoad, raw: &RawMap) -> Result<(
|
||||
bail!("zorders don't match");
|
||||
}
|
||||
|
||||
if is_cycleway(&lanes1) && is_cycleway(&lanes2) {
|
||||
if road1.is_cycleway() && road2.is_cycleway() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@ -135,20 +132,6 @@ fn should_collapse(r1: OriginalRoad, r2: OriginalRoad, raw: &RawMap) -> Result<(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Rather bruteforce way of figuring this out... is_cycleway logic lifted from Road, unfortunately.
|
||||
// Better than repeating the OSM tag log from get_lane_specs_ltr.
|
||||
fn is_cycleway(lanes: &[LaneSpec]) -> bool {
|
||||
let mut bike = false;
|
||||
for spec in lanes {
|
||||
if spec.lt == LaneType::Biking {
|
||||
bike = true;
|
||||
} else if spec.lt != LaneType::Shoulder {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bike
|
||||
}
|
||||
|
||||
pub fn collapse_intersection(raw: &mut RawMap, i: NodeID) {
|
||||
let roads = raw.roads_per_intersection(i);
|
||||
assert_eq!(roads.len(), 2);
|
||||
@ -250,8 +233,7 @@ pub fn trim_deadends(raw: &mut RawMap) {
|
||||
}
|
||||
let road = &raw.roads[&roads[0]];
|
||||
if road.length() < SHORT_THRESHOLD
|
||||
&& (is_cycleway(&get_lane_specs_ltr(&road.osm_tags, &raw.config))
|
||||
|| road.osm_tags.is(osm::HIGHWAY, "service"))
|
||||
&& (road.is_cycleway() || road.osm_tags.is(osm::HIGHWAY, "service"))
|
||||
{
|
||||
remove_roads.insert(roads[0]);
|
||||
remove_intersections.insert(*id);
|
||||
|
@ -105,7 +105,7 @@ impl RawMap {
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if let Ok((pl, _)) = self.untrimmed_road_geometry(*id) {
|
||||
if let Ok((pl, _)) = road.untrimmed_road_geometry() {
|
||||
if pl.length() <= threshold {
|
||||
results.push(*id);
|
||||
}
|
||||
@ -149,7 +149,7 @@ impl RawMap {
|
||||
for r in &connections {
|
||||
// Are both intersections 3-ways of driveable roads? (Don't even attempt
|
||||
// cycleways yet...)
|
||||
if !self.roads[r].is_driveable(&self.config) {
|
||||
if !self.roads[r].is_driveable() {
|
||||
continue 'ROAD;
|
||||
}
|
||||
// Don't do anything near border intersections
|
||||
|
@ -1,10 +1,12 @@
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashSet, HashMap};
|
||||
|
||||
use aabb_quadtree::QuadTree;
|
||||
use abstutil::Timer;
|
||||
|
||||
use crate::{osm, RawMap};
|
||||
|
||||
/// Look for roads that physically overlap, but aren't connected by an intersection. Shrink their
|
||||
/// width.
|
||||
pub fn shrink(raw: &mut RawMap, timer: &mut Timer) {
|
||||
let mut road_centers = HashMap::new();
|
||||
let mut road_polygons = HashMap::new();
|
||||
@ -21,7 +23,7 @@ pub fn shrink(raw: &mut RawMap, timer: &mut Timer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (center, total_width) = match raw.untrimmed_road_geometry(*id) {
|
||||
let (center, total_width) = match road.untrimmed_road_geometry() {
|
||||
Ok((center, total_width)) => (center, total_width),
|
||||
Err(err) => {
|
||||
// Crashing in Lisbon because of https://www.openstreetmap.org/node/5754625281 and
|
||||
@ -52,12 +54,24 @@ pub fn shrink(raw: &mut RawMap, timer: &mut Timer) {
|
||||
}
|
||||
|
||||
timer.start_iter("shrink overlapping roads", overlapping.len());
|
||||
let mut shrunk = HashSet::new();
|
||||
for (r1, r2) in overlapping {
|
||||
timer.next();
|
||||
// TODO It'd be better to gradually shrink each road until they stop touching. I got that
|
||||
// working in some maps, but it crashes in others (downstream in intersection polygon code)
|
||||
// for unknown reasons. Just do the simple thing for now.
|
||||
raw.roads.get_mut(&r1).unwrap().scale_width = 0.5;
|
||||
raw.roads.get_mut(&r2).unwrap().scale_width = 0.5;
|
||||
for id in [r1, r2] {
|
||||
// Don't shrink any road twice!
|
||||
if shrunk.contains(&id) {
|
||||
continue;
|
||||
}
|
||||
shrunk.insert(id);
|
||||
// Anything derived from lane_specs_ltr will need to be changed. When we store
|
||||
// untrimmed and trimmed center points instead of calculating them dynamically, that'll
|
||||
// have to happen here.
|
||||
for spec in &mut raw.roads.get_mut(&id).unwrap().lane_specs_ltr {
|
||||
spec.width *= 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,11 +19,11 @@ pub fn snap_cycleways(map: &mut RawMap) {
|
||||
// Because there are so many false positives with snapping, only start with cycleways
|
||||
// explicitly tagged with
|
||||
// https://wiki.openstreetmap.org/wiki/Proposed_features/cycleway:separation
|
||||
if road.is_cycleway(&map.config)
|
||||
if road.is_cycleway()
|
||||
&& (road.osm_tags.contains_key("separation:left")
|
||||
|| road.osm_tags.contains_key("separation:right"))
|
||||
{
|
||||
let (center, total_width) = map.untrimmed_road_geometry(*id).unwrap();
|
||||
let (center, total_width) = road.untrimmed_road_geometry().unwrap();
|
||||
cycleways.push(Cycleway {
|
||||
id: *id,
|
||||
center,
|
||||
@ -35,10 +35,10 @@ pub fn snap_cycleways(map: &mut RawMap) {
|
||||
|
||||
let mut road_edges: HashMap<(OriginalRoad, Direction), PolyLine> = HashMap::new();
|
||||
for (id, r) in &map.roads {
|
||||
if r.is_light_rail() || r.is_footway() || r.is_service() || r.is_cycleway(&map.config) {
|
||||
if r.is_light_rail() || r.is_footway() || r.is_service() || r.is_cycleway() {
|
||||
continue;
|
||||
}
|
||||
let (pl, total_width) = map.untrimmed_road_geometry(*id).unwrap();
|
||||
let (pl, total_width) = r.untrimmed_road_geometry().unwrap();
|
||||
road_edges.insert(
|
||||
(*id, Direction::Fwd),
|
||||
pl.must_shift_right(total_width / 2.0),
|
||||
|
@ -7,16 +7,16 @@ data/system/us/seattle/maps/lakeslice.bin
|
||||
data/system/us/phoenix/maps/tempe.bin
|
||||
425 single blocks (1 failures to blockify), 0 partial merges, 0 failures to blockify partitions
|
||||
data/system/gb/bristol/maps/east.bin
|
||||
1059 single blocks (3 failures to blockify), 5 partial merges, 0 failures to blockify partitions
|
||||
1059 single blocks (3 failures to blockify), 6 partial merges, 0 failures to blockify partitions
|
||||
data/system/gb/leeds/maps/north.bin
|
||||
2597 single blocks (5 failures to blockify), 17 partial merges, 1 failures to blockify partitions
|
||||
data/system/gb/london/maps/camden.bin
|
||||
1561 single blocks (1 failures to blockify), 6 partial merges, 0 failures to blockify partitions
|
||||
1565 single blocks (1 failures to blockify), 5 partial merges, 0 failures to blockify partitions
|
||||
data/system/gb/london/maps/southwark.bin
|
||||
2195 single blocks (3 failures to blockify), 10 partial merges, 0 failures to blockify partitions
|
||||
2195 single blocks (3 failures to blockify), 11 partial merges, 1 failures to blockify partitions
|
||||
data/system/gb/manchester/maps/levenshulme.bin
|
||||
1351 single blocks (1 failures to blockify), 1 partial merges, 0 failures to blockify partitions
|
||||
1353 single blocks (1 failures to blockify), 0 partial merges, 0 failures to blockify partitions
|
||||
data/system/fr/lyon/maps/center.bin
|
||||
5220 single blocks (5 failures to blockify), 26 partial merges, 1 failures to blockify partitions
|
||||
5216 single blocks (5 failures to blockify), 26 partial merges, 1 failures to blockify partitions
|
||||
data/system/us/seattle/maps/north_seattle.bin
|
||||
3376 single blocks (1 failures to blockify), 6 partial merges, 1 failures to blockify partitions
|
||||
|
@ -4,14 +4,14 @@
|
||||
"scenario": "weekday",
|
||||
"finished_trips": 76640,
|
||||
"cancelled_trips": 0,
|
||||
"total_trip_duration_seconds": 43931256.87820016
|
||||
"total_trip_duration_seconds": 43622111.81940041
|
||||
},
|
||||
{
|
||||
"map": "montlake (in seattle (us))",
|
||||
"scenario": "weekday",
|
||||
"finished_trips": 36710,
|
||||
"cancelled_trips": 86,
|
||||
"total_trip_duration_seconds": 18533435.094100088
|
||||
"finished_trips": 36706,
|
||||
"cancelled_trips": 90,
|
||||
"total_trip_duration_seconds": 18616755.940000143
|
||||
},
|
||||
{
|
||||
"map": "sao_miguel_paulista (in sao_paulo (br))",
|
||||
|
Loading…
Reference in New Issue
Block a user