use std::collections::BTreeMap;
use anyhow::Result;
use serde::Deserialize;
use serde_json::Value;
use abstio::{CityName, MapName};
use geom::Speed;
use crate::raw::OriginalRoad;
use crate::{
osm, AccessRestrictions, Direction, EditCmd, EditRoad, LaneType, Map, PermanentMapEdits, RoadID,
};
pub fn upgrade(mut value: Value, map: &Map) -> Result<PermanentMapEdits> {
if value.get("version").is_none() {
fix_offset(&mut value);
fix_intersection_ids(&mut value);
value
.as_object_mut()
.unwrap()
.insert("version".to_string(), Value::Number(0.into()));
}
if value["version"] == Value::Number(0.into()) {
fix_road_direction(&mut value);
value
.as_object_mut()
.unwrap()
.insert("version".to_string(), Value::Number(1.into()));
}
if value["version"] == Value::Number(1.into()) {
fix_old_lane_cmds(&mut value, map)?;
value
.as_object_mut()
.unwrap()
.insert("version".to_string(), Value::Number(2.into()));
}
if value["version"] == Value::Number(2.into()) {
fix_merge_zones(&mut value);
value
.as_object_mut()
.unwrap()
.insert("version".to_string(), Value::Number(3.into()));
}
if value["version"] == Value::Number(3.into()) {
fix_map_name(&mut value);
value
.as_object_mut()
.unwrap()
.insert("version".to_string(), Value::Number(4.into()));
}
if value["version"] == Value::Number(4.into()) {
fix_phase_to_stage(&mut value);
value
.as_object_mut()
.unwrap()
.insert("version".to_string(), Value::Number(5.into()));
}
if value["version"] == Value::Number(5.into()) {
fix_adaptive_stages(&mut value);
value
.as_object_mut()
.unwrap()
.insert("version".to_string(), Value::Number(6.into()));
}
if value["version"] == Value::Number(6.into()) {
fix_plans(&mut value);
value
.as_object_mut()
.unwrap()
.insert("version".to_string(), Value::Number(7.into()));
}
if value["version"] == Value::Number(7.into()) {
fix_city_name(&mut value);
value
.as_object_mut()
.unwrap()
.insert("version".to_string(), Value::Number(8.into()));
}
abstutil::from_json(&value.to_string().into_bytes())
}
fn walk<F: Fn(&mut serde_json::Map<String, Value>) -> bool>(value: &mut Value, transform: &F) {
match value {
Value::Array(list) => {
for x in list {
walk(x, transform);
}
}
Value::Object(map) => {
if !(transform)(map) {
for x in map.values_mut() {
walk(x, transform);
}
}
}
_ => {}
}
}
fn fix_offset(value: &mut Value) {
walk(value, &|map| {
if map.len() == 1 && map.contains_key("TrafficSignal") {
let ts = map
.get_mut("TrafficSignal")
.unwrap()
.as_object_mut()
.unwrap();
if ts.get("offset_seconds").is_none() {
ts.insert("offset_seconds".to_string(), Value::Number(0.into()));
}
true
} else {
false
}
})
}
fn fix_intersection_ids(value: &mut Value) {
match value {
Value::Array(list) => {
for x in list {
fix_intersection_ids(x);
}
}
Value::Object(map) => {
if map.len() == 1 && map.contains_key("osm_node_id") {
*value = Value::Number(map["osm_node_id"].as_i64().unwrap().into());
} else {
for x in map.values_mut() {
fix_intersection_ids(x);
}
}
}
_ => {}
}
}
fn fix_road_direction(value: &mut Value) {
walk(value, &|map| {
if map.contains_key("num_fwd") {
map.insert(
"dir".to_string(),
if map["fwd"].as_bool().unwrap() {
"Fwd".into()
} else {
"Back".into()
},
);
true
} else {
false
}
});
}
fn fix_old_lane_cmds(value: &mut Value, map: &Map) -> Result<()> {
let mut modified: BTreeMap<RoadID, EditRoad> = BTreeMap::new();
let mut commands = Vec::new();
for mut orig in value.as_object_mut().unwrap()["commands"]
.as_array_mut()
.unwrap()
.drain(..)
{
let cmd = orig.as_object_mut().unwrap();
if let Some(obj) = cmd.remove("ChangeLaneType") {
let obj: ChangeLaneType = serde_json::from_value(obj).unwrap();
let (r, idx) = obj.id.lookup(map)?;
let road = modified.entry(r).or_insert_with(|| map.get_r_edit(r));
if road.lanes_ltr[idx].0 != obj.orig_lt {
bail!("{:?} lane type has changed", obj);
}
road.lanes_ltr[idx].0 = obj.lt;
} else if let Some(obj) = cmd.remove("ReverseLane") {
let obj: ReverseLane = serde_json::from_value(obj).unwrap();
let (r, idx) = obj.l.lookup(map)?;
let dst_i = map.find_i_by_osm_id(obj.dst_i)?;
let road = modified.entry(r).or_insert_with(|| map.get_r_edit(r));
let edits_dir = if dst_i == map.get_r(r).dst_i {
Direction::Fwd
} else if dst_i == map.get_r(r).src_i {
Direction::Back
} else {
bail!("{:?}'s road doesn't point to dst_i at all", obj);
};
if road.lanes_ltr[idx].1 == edits_dir {
bail!("{:?}'s road already points to dst_i", obj);
}
road.lanes_ltr[idx].1 = edits_dir;
} else if let Some(obj) = cmd.remove("ChangeSpeedLimit") {
let obj: ChangeSpeedLimit = serde_json::from_value(obj).unwrap();
let r = map.find_r_by_osm_id(obj.id)?;
let road = modified.entry(r).or_insert_with(|| map.get_r_edit(r));
if road.speed_limit != obj.old {
bail!("{:?} speed limit has changed", obj);
}
road.speed_limit = obj.new;
} else if let Some(obj) = cmd.remove("ChangeAccessRestrictions") {
let obj: ChangeAccessRestrictions = serde_json::from_value(obj).unwrap();
let r = map.find_r_by_osm_id(obj.id)?;
let road = modified.entry(r).or_insert_with(|| map.get_r_edit(r));
if road.access_restrictions != obj.old {
bail!("{:?} access restrictions have changed", obj);
}
road.access_restrictions = obj.new.clone();
} else {
commands.push(orig);
}
}
for (r, new) in modified {
let old = map.get_r_edit(r);
commands
.push(serde_json::to_value(EditCmd::ChangeRoad { r, old, new }.to_perma(map)).unwrap());
}
value.as_object_mut().unwrap()["commands"] = Value::Array(commands);
Ok(())
}
fn fix_merge_zones(value: &mut Value) {
let obj = value.as_object_mut().unwrap();
if !obj.contains_key("merge_zones") {
obj.insert("merge_zones".to_string(), Value::Bool(true.into()));
}
}
fn fix_map_name(value: &mut Value) {
let root = value.as_object_mut().unwrap();
if let Value::String(ref name) = root["map_name"].clone() {
root.insert(
"map_name".to_string(),
serde_json::to_value(MapName::seattle(name)).unwrap(),
);
}
}
fn fix_phase_to_stage(value: &mut Value) {
walk(value, &|map| {
if let Some(list) = map.remove("phases") {
map.insert("stages".to_string(), list);
}
if let Some(obj) = map.remove("phase_type") {
map.insert("stage_type".to_string(), obj);
}
false
});
}
fn fix_adaptive_stages(value: &mut Value) {
walk(value, &|map| {
if let Some(seconds) = map.remove("Adaptive") {
let minimum = seconds.clone();
let delay = Value::Number(1.into());
let additional = seconds;
map.insert(
"Variable".to_string(),
Value::Array(vec![minimum, delay, additional]),
);
}
false
});
}
fn fix_plans(value: &mut Value) {
walk(value, &|map| {
if map.len() == 1 && map.contains_key("TrafficSignal") {
let ts = map
.get_mut("TrafficSignal")
.unwrap()
.as_object_mut()
.unwrap();
let mut plan = serde_json::Map::new();
plan.insert("start_time_seconds".to_string(), Value::Number(0.into()));
plan.insert("stages".to_string(), ts.remove("stages").unwrap());
plan.insert(
"offset_seconds".to_string(),
ts.remove("offset_seconds").unwrap(),
);
ts.insert("plans".to_string(), Value::Array(vec![Value::Object(plan)]));
true
} else {
false
}
})
}
fn fix_city_name(value: &mut Value) {
let root = value.as_object_mut().unwrap();
let map_name = root["map_name"].as_object_mut().unwrap();
if let Value::String(ref name) = map_name["city"].clone() {
let country = match name.as_ref() {
"salzburg" => "at",
"montreal" => "ca",
"berlin" => "de",
"paris" => "fr",
"leeds" | "london" => "gb",
"tel_aviv" => "il",
"krakow" | "warsaw" => "pl",
_ => "us",
};
map_name.insert(
"city".to_string(),
serde_json::to_value(CityName::new(&country, &name)).unwrap(),
);
}
}
#[derive(Debug, Deserialize)]
struct OriginalLane {
parent: OriginalRoad,
num_fwd: usize,
num_back: usize,
dir: Direction,
idx: usize,
}
#[derive(Debug, Deserialize)]
struct ChangeLaneType {
id: OriginalLane,
lt: LaneType,
orig_lt: LaneType,
}
#[derive(Debug, Deserialize)]
struct ReverseLane {
l: OriginalLane,
dst_i: osm::NodeID,
}
#[derive(Debug, Deserialize)]
struct ChangeSpeedLimit {
id: OriginalRoad,
new: Speed,
old: Speed,
}
#[derive(Debug, Deserialize)]
struct ChangeAccessRestrictions {
id: OriginalRoad,
new: AccessRestrictions,
old: AccessRestrictions,
}
impl OriginalLane {
fn lookup(&self, map: &Map) -> Result<(RoadID, usize)> {
let r = map.get_r(map.find_r_by_osm_id(self.parent)?);
let current_fwd = r.children_forwards();
let current_back = r.children_backwards();
if current_fwd.len() != self.num_fwd || current_back.len() != self.num_back {
bail!(
"number of lanes in {} is ({} fwd, {} back) now, but ({}, {}) in the edits",
r.orig_id,
current_fwd.len(),
current_back.len(),
self.num_fwd,
self.num_back
);
}
let l = if self.dir == Direction::Fwd {
current_fwd[self.idx].0
} else {
current_back[self.idx].0
};
Ok((r.id, r.offset(l)))
}
}