diff --git a/Cargo.lock b/Cargo.lock index 6f491c3..afa9561 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -274,7 +274,7 @@ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "micro_traffic_sim_core" -version = "0.1.9" +version = "0.1.10" dependencies = [ "criterion", "indexmap", diff --git a/Cargo.toml b/Cargo.toml index 0b65348..9e6b516 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "micro_traffic_sim_core" -version = "0.1.9" +version = "0.1.10" edition = "2024" description = "Core library for microscopic traffic simulation via cellular automata." license = "Apache-2.0" diff --git a/examples/merge-blocked/README.md b/examples/merge-blocked/README.md new file mode 100644 index 0000000..7f7faf3 --- /dev/null +++ b/examples/merge-blocked/README.md @@ -0,0 +1,27 @@ +# merge-blocked + +Demonstrates the "keep rolling along a jammed lane" fallback in +`find_alternate_intention` and the confusion A*-skip, on the real stage network +(`network.json` - 676 cells exported from my local toy client application: forward/left/right +links, WGS84 coordinates, zones, speed limits). + +Vehicle 1 drives 666 -> 667 -> 668 (no right neighbor) toward destination 621, +reachable only via the left merges 666->605, 667->606, 668->619. Past 668 lies +the point of no return (681 -> 683 death zone). The left lane is jammed with +parked vehicles. + +```bash +cargo run --example merge-blocked +``` + +| Scenario | Parked | Expected outcome | +|---|---|---| +| A: gap at the last merge | 605, 606 | rolls along the jam, merges at 619 -> COMPLETED | +| B: fully jammed | 605, 606, 619 | rolls past the last merge, confusion drives it to the 683 death zone -> LOST, the lane stays free (accepted risk) | + +A DEADLOCK verdict (standing next to the jam forever) is a regression: the +forward fallback in `find_alternate_intention` stopped working. + +Once a vehicle is confused, its destination is unreachable from every cell it +can ever reach (reachability is monotone along directed edges), so per-tick A* +is skipped for it entirely - a failed full A* is the most expensive kind. diff --git a/examples/merge-blocked/main.rs b/examples/merge-blocked/main.rs new file mode 100644 index 0000000..0023a08 --- /dev/null +++ b/examples/merge-blocked/main.rs @@ -0,0 +1,152 @@ +//! Demonstration of the "keep rolling along a jammed lane" fallback in +//! find_alternate_intention, on the real "Pisareva" stage network. +//! +//! Setup: vehicle 1 drives in lane 666 -> 667 -> 668 (no right neighbor) with +//! destination 621, which is reachable ONLY through the left merges +//! 666->605, 667->606, 668->619 (after 668 comes the point of no return: +//! 681 -> 683 dead end). The left lane is jammed with parked vehicles. +//! +//! Scenario A - gap at the last merge (605, 606 parked, 619 free): +//! the vehicle rolls along the jam and merges into the gap -> COMPLETED. +//! +//! Scenario B - fully jammed (605, 606, 619 parked): +//! the vehicle rolls past the last merge, per-tick A* returns NoPathFound, +//! confusion drives it forward into the 683 death zone -> LOST. +//! The trip fails, but the road stays free - an accepted risk. +//! +//! A DEADLOCK verdict (standing next to the jam forever) is a regression: +//! it means the forward fallback in find_alternate_intention stopped working. +//! +//! Run: cargo run --example merge-blocked + +use micro_traffic_sim_core::agents::Vehicle; +use micro_traffic_sim_core::behaviour::BehaviourType; +use micro_traffic_sim_core::geom::{SRID, new_point}; +use micro_traffic_sim_core::grid::{cell::Cell, road_network::GridRoads, zones::ZoneType}; +use micro_traffic_sim_core::simulation::grids_storage::GridsStorage; +use micro_traffic_sim_core::simulation::session::Session; +use micro_traffic_sim_core::verbose::VerboseLevel; + +const NETWORK_JSON: &str = include_str!("network.json"); + +const START_CELL: i64 = 666; +const DESTINATION: i64 = 621; +const STEPS: usize = 80; +const DEADLOCK_THRESHOLD: usize = 15; + +fn load_grid() -> GridRoads { + let cells: serde_json::Value = + serde_json::from_str(NETWORK_JSON).expect("network.json must be valid JSON"); + let mut grid = GridRoads::new(); + for c in cells.as_array().expect("array of cells") { + let zone = match c["zone"].as_i64().unwrap() { + 1 => ZoneType::Birth, + 2 => ZoneType::Death, + 3 => ZoneType::Coordination, + 4 => ZoneType::Common, + _ => ZoneType::Undefined, + }; + let cell = Cell::new(c["id"].as_i64().unwrap()) + .with_point(new_point( + c["lon"].as_f64().unwrap(), + c["lat"].as_f64().unwrap(), + Some(SRID::WGS84), + )) + .with_forward_node(c["fwd"].as_i64().unwrap()) + .with_left_node(c["left"].as_i64().unwrap()) + .with_right_node(c["right"].as_i64().unwrap()) + .with_zone_type(zone) + .with_speed_limit(c["sl"].as_i64().unwrap() as i32) + .build(); + grid.add_cell(cell); + } + grid +} + +fn run_scenario(name: &str, parked_cells: &[i64]) { + println!("Scenario {name}: parked at {parked_cells:?}"); + + let traveller = Vehicle::new(1) + .with_speed(1) + .with_speed_limit(4) + .with_cell(START_CELL) + .with_destination(DESTINATION) + .with_slowdown(0.0) + .build(); + let mut vehicles = vec![traveller]; + for (i, &cell) in parked_cells.iter().enumerate() { + vehicles.push( + Vehicle::new(100 + i as u64) + .with_cell(cell) + .with_destination(-1) + .with_behaviour(BehaviourType::Block) + .build(), + ); + } + + let grids_storage = GridsStorage::new().with_vehicles_net(load_grid()).build(); + let mut session = Session::new(grids_storage, None); + session.set_verbose_level(VerboseLevel::None); + session.add_vehicles(vehicles); + + let mut trajectory: Vec = vec![START_CELL]; + let mut stuck_ticks = 0usize; + let mut completed = 0; + let mut lost = 0; + + for _ in 0..STEPS { + let state = session.step().expect("simulation step failed"); + completed = state.vehicles_completed; + lost = state.vehicles_lost; + + let traveller = session + .get_vehicles() + .into_iter() + .find(|(_, v)| v.id == 1) + .map(|(_, v)| v); + match traveller { + Some(v) => { + if *trajectory.last().unwrap() == v.cell_id { + if v.speed == 0 { + stuck_ticks += 1; + } + } else { + trajectory.push(v.cell_id); + stuck_ticks = 0; + } + } + None => break, // removed: either completed or lost + } + if stuck_ticks >= DEADLOCK_THRESHOLD { + break; + } + } + + println!("trajectory: {trajectory:?}"); + if completed > 0 { + println!("VERDICT: COMPLETED - merged into the gap and reached {DESTINATION}\n"); + } else if lost > 0 { + println!( + "VERDICT: LOST - rolled past the last merge, wandered to the death zone; \ + the lane stays free (accepted risk)\n" + ); + } else if stuck_ticks >= DEADLOCK_THRESHOLD { + println!( + "VERDICT: DEADLOCK - stood {stuck_ticks} ticks at cell {} waiting for \ + the jammed lane; REGRESSION - the forward fallback did not kick in\n", + trajectory.last().unwrap() + ); + } else { + println!("VERDICT: INCONCLUSIVE after {STEPS} steps\n"); + } +} + +fn main() { + let grid = load_grid(); + println!( + "Loaded stage network: {} cells; route to {DESTINATION} exists only via left merges 605/606/619\n", + grid.get_cells_num() + ); + run_scenario("A (gap at the last merge)", &[605, 606]); + run_scenario("B (fully jammed)", &[605, 606, 619]); +} diff --git a/examples/merge-blocked/network.json b/examples/merge-blocked/network.json new file mode 100644 index 0000000..1c78cd5 --- /dev/null +++ b/examples/merge-blocked/network.json @@ -0,0 +1,678 @@ +[ +{"id":1,"fwd":2,"left":-1,"right":-1,"lon":82.91228279471397,"lat":55.045628514938585,"zone":1,"sl":4}, +{"id":2,"fwd":3,"left":-1,"right":-1,"lon":82.91227251183587,"lat":55.04566855324775,"zone":4,"sl":4}, +{"id":3,"fwd":4,"left":-1,"right":-1,"lon":82.91226222893721,"lat":55.045708591556064,"zone":4,"sl":4}, +{"id":4,"fwd":5,"left":-1,"right":-1,"lon":82.91225194601797,"lat":55.04574862986349,"zone":4,"sl":4}, +{"id":5,"fwd":6,"left":-1,"right":-1,"lon":82.91224166307819,"lat":55.045788668170076,"zone":4,"sl":4}, +{"id":6,"fwd":7,"left":-1,"right":-1,"lon":82.91223138011786,"lat":55.045828706475774,"zone":4,"sl":4}, +{"id":7,"fwd":8,"left":-1,"right":-1,"lon":82.91222109713696,"lat":55.04586874478059,"zone":4,"sl":4}, +{"id":8,"fwd":9,"left":-1,"right":-1,"lon":82.91221081413549,"lat":55.04590878308458,"zone":4,"sl":4}, +{"id":9,"fwd":10,"left":-1,"right":-1,"lon":82.91220053111347,"lat":55.045948821387654,"zone":4,"sl":4}, +{"id":10,"fwd":11,"left":-1,"right":-1,"lon":82.9121902480709,"lat":55.04598885968991,"zone":4,"sl":4}, +{"id":11,"fwd":12,"left":-1,"right":-1,"lon":82.91217996500777,"lat":55.04602889799128,"zone":4,"sl":4}, +{"id":12,"fwd":13,"left":-1,"right":-1,"lon":82.91216968192404,"lat":55.046068936291775,"zone":4,"sl":4}, +{"id":13,"fwd":14,"left":-1,"right":-1,"lon":82.91215939881978,"lat":55.04610897459141,"zone":4,"sl":4}, +{"id":14,"fwd":15,"left":-1,"right":-1,"lon":82.91214911569497,"lat":55.04614901289019,"zone":4,"sl":4}, +{"id":15,"fwd":16,"left":-1,"right":-1,"lon":82.91213883254959,"lat":55.04618905118809,"zone":4,"sl":4}, +{"id":16,"fwd":17,"left":-1,"right":-1,"lon":82.91212854938364,"lat":55.04622908948513,"zone":4,"sl":4}, +{"id":17,"fwd":18,"left":-1,"right":-1,"lon":82.91211826619715,"lat":55.046269127781294,"zone":4,"sl":4}, +{"id":18,"fwd":19,"left":-1,"right":-1,"lon":82.91210798299008,"lat":55.0463091660766,"zone":4,"sl":4}, +{"id":19,"fwd":20,"left":-1,"right":-1,"lon":82.91209769976246,"lat":55.04634920437102,"zone":4,"sl":4}, +{"id":20,"fwd":21,"left":-1,"right":-1,"lon":82.91208741651427,"lat":55.0463892426646,"zone":4,"sl":4}, +{"id":21,"fwd":22,"left":-1,"right":-1,"lon":82.91207713324553,"lat":55.04642928095729,"zone":4,"sl":4}, +{"id":22,"fwd":23,"left":-1,"right":-1,"lon":82.91206684995623,"lat":55.04646931924914,"zone":4,"sl":4}, +{"id":23,"fwd":24,"left":-1,"right":-1,"lon":82.91205656664636,"lat":55.0465093575401,"zone":4,"sl":4}, +{"id":24,"fwd":25,"left":-1,"right":-1,"lon":82.91204628331595,"lat":55.04654939583021,"zone":4,"sl":4}, +{"id":25,"fwd":26,"left":-1,"right":-1,"lon":82.91203599996496,"lat":55.04658943411944,"zone":4,"sl":4}, +{"id":26,"fwd":27,"left":-1,"right":-1,"lon":82.91202571659342,"lat":55.04662947240782,"zone":4,"sl":4}, +{"id":27,"fwd":28,"left":-1,"right":-1,"lon":82.91201543320129,"lat":55.0466695106953,"zone":4,"sl":4}, +{"id":28,"fwd":29,"left":-1,"right":-1,"lon":82.91200514978864,"lat":55.046709548981944,"zone":4,"sl":4}, +{"id":29,"fwd":30,"left":-1,"right":-1,"lon":82.9119948663554,"lat":55.046749587267726,"zone":4,"sl":4}, +{"id":30,"fwd":31,"left":-1,"right":-1,"lon":82.9119845829016,"lat":55.04678962555262,"zone":4,"sl":4}, +{"id":31,"fwd":32,"left":-1,"right":-1,"lon":82.91197429942726,"lat":55.046829663836675,"zone":4,"sl":4}, +{"id":32,"fwd":33,"left":-1,"right":-1,"lon":82.91196401593234,"lat":55.046869702119835,"zone":4,"sl":4}, +{"id":33,"fwd":34,"left":-1,"right":-1,"lon":82.91195373241685,"lat":55.04690974040214,"zone":4,"sl":4}, +{"id":34,"fwd":35,"left":-1,"right":-1,"lon":82.91194344888082,"lat":55.04694977868358,"zone":4,"sl":4}, +{"id":35,"fwd":36,"left":-1,"right":-1,"lon":82.91193316532421,"lat":55.04698981696414,"zone":4,"sl":4}, +{"id":36,"fwd":37,"left":-1,"right":-1,"lon":82.91192288174706,"lat":55.04702985524384,"zone":4,"sl":4}, +{"id":37,"fwd":38,"left":-1,"right":-1,"lon":82.91191259814933,"lat":55.04706989352268,"zone":4,"sl":4}, +{"id":38,"fwd":39,"left":-1,"right":-1,"lon":82.91190231453106,"lat":55.04710993180065,"zone":4,"sl":4}, +{"id":39,"fwd":40,"left":-1,"right":-1,"lon":82.9118920308922,"lat":55.04714997007775,"zone":4,"sl":4}, +{"id":40,"fwd":41,"left":-1,"right":-1,"lon":82.91188174723281,"lat":55.047190008353994,"zone":4,"sl":4}, +{"id":41,"fwd":42,"left":-1,"right":-1,"lon":82.91187146355284,"lat":55.04723004662935,"zone":4,"sl":4}, +{"id":42,"fwd":43,"left":-1,"right":-1,"lon":82.91186117985228,"lat":55.04727008490386,"zone":4,"sl":4}, +{"id":43,"fwd":44,"left":-1,"right":-1,"lon":82.9118508961312,"lat":55.047310123177496,"zone":4,"sl":4}, +{"id":44,"fwd":45,"left":-1,"right":-1,"lon":82.91184061238954,"lat":55.04735016145028,"zone":4,"sl":4}, +{"id":45,"fwd":46,"left":-1,"right":-1,"lon":82.91183032862732,"lat":55.04739019972217,"zone":4,"sl":4}, +{"id":46,"fwd":47,"left":-1,"right":-1,"lon":82.91182004484453,"lat":55.04743023799321,"zone":4,"sl":4}, +{"id":47,"fwd":48,"left":-1,"right":-1,"lon":82.91180976104121,"lat":55.04747027626337,"zone":4,"sl":4}, +{"id":48,"fwd":49,"left":-1,"right":-1,"lon":82.91179947721729,"lat":55.04751031453267,"zone":4,"sl":4}, +{"id":49,"fwd":50,"left":-1,"right":-1,"lon":82.91178919337283,"lat":55.04755035280112,"zone":4,"sl":4}, +{"id":50,"fwd":51,"left":-1,"right":-1,"lon":82.9117789095078,"lat":55.04759039106869,"zone":4,"sl":4}, +{"id":51,"fwd":52,"left":-1,"right":-1,"lon":82.91176862562219,"lat":55.04763042933538,"zone":4,"sl":4}, +{"id":52,"fwd":53,"left":-1,"right":-1,"lon":82.91175834171604,"lat":55.047670467601215,"zone":4,"sl":4}, +{"id":53,"fwd":54,"left":-1,"right":-1,"lon":82.91174805778932,"lat":55.04771050586619,"zone":4,"sl":4}, +{"id":54,"fwd":55,"left":-1,"right":-1,"lon":82.91173777384203,"lat":55.04775054413029,"zone":4,"sl":4}, +{"id":55,"fwd":56,"left":-1,"right":-1,"lon":82.9117274898742,"lat":55.04779058239352,"zone":4,"sl":4}, +{"id":56,"fwd":57,"left":-1,"right":-1,"lon":82.91171720588578,"lat":55.04783062065588,"zone":4,"sl":4}, +{"id":57,"fwd":58,"left":-1,"right":-1,"lon":82.91170692187681,"lat":55.04787065891738,"zone":4,"sl":4}, +{"id":58,"fwd":59,"left":-1,"right":-1,"lon":82.91169663784727,"lat":55.047910697178025,"zone":4,"sl":4}, +{"id":59,"fwd":60,"left":-1,"right":-1,"lon":82.91168635379718,"lat":55.047950735437794,"zone":4,"sl":4}, +{"id":60,"fwd":61,"left":-1,"right":-1,"lon":82.91167606972651,"lat":55.0479907736967,"zone":4,"sl":4}, +{"id":61,"fwd":62,"left":-1,"right":-1,"lon":82.9116657856353,"lat":55.04803081195473,"zone":4,"sl":4}, +{"id":62,"fwd":63,"left":-1,"right":-1,"lon":82.9116555015235,"lat":55.04807085021189,"zone":4,"sl":4}, +{"id":63,"fwd":-1,"left":967,"right":940,"lon":82.91165180504322,"lat":55.0480852413842,"zone":4,"sl":4}, +{"id":64,"fwd":65,"left":-1,"right":-1,"lon":82.91146002709866,"lat":55.048076021738694,"zone":4,"sl":4}, +{"id":65,"fwd":66,"left":-1,"right":-1,"lon":82.9114701924614,"lat":55.04803597352977,"zone":4,"sl":4}, +{"id":66,"fwd":67,"left":-1,"right":-1,"lon":82.91148035780382,"lat":55.04799592532001,"zone":4,"sl":4}, +{"id":67,"fwd":68,"left":-1,"right":-1,"lon":82.91149052312592,"lat":55.047955877109395,"zone":4,"sl":4}, +{"id":68,"fwd":69,"left":-1,"right":-1,"lon":82.91150068842768,"lat":55.047915828897956,"zone":4,"sl":4}, +{"id":69,"fwd":70,"left":-1,"right":-1,"lon":82.91151085370912,"lat":55.047875780685644,"zone":4,"sl":4}, +{"id":70,"fwd":71,"left":-1,"right":-1,"lon":82.9115210189702,"lat":55.04783573247251,"zone":4,"sl":4}, +{"id":71,"fwd":72,"left":-1,"right":-1,"lon":82.91153118421099,"lat":55.0477956842585,"zone":4,"sl":4}, +{"id":72,"fwd":73,"left":-1,"right":-1,"lon":82.9115413494314,"lat":55.047755636043675,"zone":4,"sl":4}, +{"id":73,"fwd":74,"left":-1,"right":-1,"lon":82.91155151463153,"lat":55.04771558782799,"zone":4,"sl":4}, +{"id":74,"fwd":75,"left":-1,"right":-1,"lon":82.9115616798113,"lat":55.04767553961146,"zone":4,"sl":4}, +{"id":75,"fwd":76,"left":-1,"right":-1,"lon":82.91157184497072,"lat":55.04763549139406,"zone":4,"sl":4}, +{"id":76,"fwd":77,"left":-1,"right":-1,"lon":82.91158201010985,"lat":55.04759544317584,"zone":4,"sl":4}, +{"id":77,"fwd":78,"left":-1,"right":-1,"lon":82.91159217522863,"lat":55.04755539495676,"zone":4,"sl":4}, +{"id":78,"fwd":79,"left":-1,"right":-1,"lon":82.91160234032708,"lat":55.047515346736844,"zone":4,"sl":4}, +{"id":79,"fwd":80,"left":-1,"right":-1,"lon":82.91161250540522,"lat":55.047475298516076,"zone":4,"sl":4}, +{"id":80,"fwd":81,"left":-1,"right":-1,"lon":82.911622670463,"lat":55.04743525029445,"zone":4,"sl":4}, +{"id":81,"fwd":82,"left":-1,"right":-1,"lon":82.91163283550046,"lat":55.047395202071996,"zone":4,"sl":4}, +{"id":82,"fwd":83,"left":-1,"right":-1,"lon":82.91164300051761,"lat":55.04735515384867,"zone":4,"sl":4}, +{"id":83,"fwd":84,"left":-1,"right":-1,"lon":82.91165316551441,"lat":55.04731510562453,"zone":4,"sl":4}, +{"id":84,"fwd":85,"left":-1,"right":-1,"lon":82.9116633304909,"lat":55.04727505739952,"zone":4,"sl":4}, +{"id":85,"fwd":86,"left":-1,"right":-1,"lon":82.91167349544703,"lat":55.04723500917368,"zone":4,"sl":4}, +{"id":86,"fwd":87,"left":-1,"right":-1,"lon":82.91168366038283,"lat":55.04719496094698,"zone":4,"sl":4}, +{"id":87,"fwd":88,"left":-1,"right":-1,"lon":82.91169382529834,"lat":55.047154912719456,"zone":4,"sl":4}, +{"id":88,"fwd":89,"left":-1,"right":-1,"lon":82.91170399019349,"lat":55.04711486449103,"zone":4,"sl":4}, +{"id":89,"fwd":90,"left":-1,"right":-1,"lon":82.91171415506831,"lat":55.04707481626181,"zone":4,"sl":4}, +{"id":90,"fwd":91,"left":-1,"right":-1,"lon":82.9117243199228,"lat":55.047034768031715,"zone":4,"sl":4}, +{"id":91,"fwd":92,"left":-1,"right":-1,"lon":82.91173448475698,"lat":55.04699471980081,"zone":4,"sl":4}, +{"id":92,"fwd":93,"left":-1,"right":-1,"lon":82.9117446495708,"lat":55.04695467156901,"zone":4,"sl":4}, +{"id":93,"fwd":94,"left":-1,"right":-1,"lon":82.91175481436431,"lat":55.04691462333639,"zone":4,"sl":4}, +{"id":94,"fwd":95,"left":-1,"right":-1,"lon":82.9117649791375,"lat":55.046874575102926,"zone":4,"sl":4}, +{"id":95,"fwd":96,"left":-1,"right":-1,"lon":82.91177514389035,"lat":55.04683452686861,"zone":4,"sl":4}, +{"id":96,"fwd":97,"left":-1,"right":-1,"lon":82.91178530862287,"lat":55.04679447863344,"zone":4,"sl":4}, +{"id":97,"fwd":98,"left":-1,"right":-1,"lon":82.91179547333508,"lat":55.04675443039742,"zone":4,"sl":4}, +{"id":98,"fwd":99,"left":-1,"right":-1,"lon":82.91180563802692,"lat":55.046714382160566,"zone":4,"sl":4}, +{"id":99,"fwd":100,"left":-1,"right":-1,"lon":82.91181580269846,"lat":55.046674333922866,"zone":4,"sl":4}, +{"id":100,"fwd":101,"left":-1,"right":-1,"lon":82.91182596734969,"lat":55.04663428568432,"zone":4,"sl":4}, +{"id":101,"fwd":102,"left":-1,"right":-1,"lon":82.91183613198055,"lat":55.046594237444914,"zone":4,"sl":4}, +{"id":102,"fwd":103,"left":-1,"right":-1,"lon":82.9118462965911,"lat":55.04655418920467,"zone":4,"sl":4}, +{"id":103,"fwd":104,"left":-1,"right":-1,"lon":82.91185646118132,"lat":55.04651414096359,"zone":4,"sl":4}, +{"id":104,"fwd":105,"left":-1,"right":-1,"lon":82.9118666257512,"lat":55.046474092721645,"zone":4,"sl":4}, +{"id":105,"fwd":106,"left":-1,"right":-1,"lon":82.91187679030078,"lat":55.04643404447886,"zone":4,"sl":4}, +{"id":106,"fwd":107,"left":-1,"right":-1,"lon":82.91188695483001,"lat":55.04639399623522,"zone":4,"sl":4}, +{"id":107,"fwd":108,"left":-1,"right":-1,"lon":82.9118971193389,"lat":55.046353947990745,"zone":4,"sl":4}, +{"id":108,"fwd":109,"left":-1,"right":-1,"lon":82.91190728382749,"lat":55.04631389974543,"zone":4,"sl":4}, +{"id":109,"fwd":110,"left":-1,"right":-1,"lon":82.91191744829575,"lat":55.04627385149925,"zone":4,"sl":4}, +{"id":110,"fwd":111,"left":-1,"right":-1,"lon":82.91192761274367,"lat":55.04623380325223,"zone":4,"sl":4}, +{"id":111,"fwd":112,"left":-1,"right":-1,"lon":82.91193777717125,"lat":55.046193755004374,"zone":4,"sl":4}, +{"id":112,"fwd":113,"left":-1,"right":-1,"lon":82.91194794157853,"lat":55.04615370675566,"zone":4,"sl":4}, +{"id":113,"fwd":114,"left":-1,"right":-1,"lon":82.91195810596547,"lat":55.046113658506094,"zone":4,"sl":4}, +{"id":114,"fwd":115,"left":-1,"right":-1,"lon":82.91196827033207,"lat":55.04607361025569,"zone":4,"sl":4}, +{"id":115,"fwd":116,"left":-1,"right":-1,"lon":82.91197843467836,"lat":55.04603356200444,"zone":4,"sl":4}, +{"id":116,"fwd":117,"left":-1,"right":-1,"lon":82.91198859900432,"lat":55.04599351375232,"zone":4,"sl":4}, +{"id":117,"fwd":118,"left":-1,"right":-1,"lon":82.91199876330994,"lat":55.045953465499394,"zone":4,"sl":4}, +{"id":118,"fwd":119,"left":-1,"right":-1,"lon":82.91200892759524,"lat":55.04591341724559,"zone":4,"sl":4}, +{"id":119,"fwd":120,"left":-1,"right":-1,"lon":82.91201909186023,"lat":55.04587336899096,"zone":4,"sl":4}, +{"id":120,"fwd":121,"left":-1,"right":-1,"lon":82.91202925610487,"lat":55.04583332073548,"zone":4,"sl":4}, +{"id":121,"fwd":122,"left":-1,"right":-1,"lon":82.91203942032918,"lat":55.04579327247915,"zone":4,"sl":4}, +{"id":122,"fwd":123,"left":-1,"right":-1,"lon":82.91204958453316,"lat":55.045753224221954,"zone":4,"sl":4}, +{"id":123,"fwd":124,"left":-1,"right":-1,"lon":82.91205974871684,"lat":55.045713175963925,"zone":4,"sl":4}, +{"id":124,"fwd":125,"left":-1,"right":-1,"lon":82.91206991288018,"lat":55.045673127705065,"zone":4,"sl":4}, +{"id":125,"fwd":-1,"left":-1,"right":-1,"lon":82.9120709002018,"lat":55.045669237512186,"zone":2,"sl":4}, +{"id":127,"fwd":128,"left":-1,"right":190,"lon":82.91501283700077,"lat":55.048621159659554,"zone":1,"sl":4}, +{"id":128,"fwd":129,"left":-1,"right":191,"lon":82.91494322517356,"lat":55.04861505428803,"zone":4,"sl":4}, +{"id":129,"fwd":130,"left":-1,"right":192,"lon":82.91487361336665,"lat":55.04860894887652,"zone":4,"sl":4}, +{"id":130,"fwd":131,"left":-1,"right":193,"lon":82.91480400158,"lat":55.04860284342502,"zone":4,"sl":4}, +{"id":131,"fwd":132,"left":-1,"right":194,"lon":82.91473438981363,"lat":55.04859673793354,"zone":4,"sl":4}, +{"id":132,"fwd":133,"left":-1,"right":195,"lon":82.91466477806755,"lat":55.04859063240208,"zone":4,"sl":4}, +{"id":133,"fwd":134,"left":-1,"right":196,"lon":82.91459516634173,"lat":55.04858452683064,"zone":4,"sl":4}, +{"id":134,"fwd":135,"left":-1,"right":197,"lon":82.9145255546362,"lat":55.0485784212192,"zone":4,"sl":4}, +{"id":135,"fwd":136,"left":-1,"right":198,"lon":82.91445594295095,"lat":55.048572315567796,"zone":4,"sl":4}, +{"id":136,"fwd":137,"left":-1,"right":199,"lon":82.91438633128597,"lat":55.048566209876384,"zone":4,"sl":4}, +{"id":137,"fwd":138,"left":-1,"right":200,"lon":82.91431671964128,"lat":55.048560104145004,"zone":4,"sl":4}, +{"id":138,"fwd":139,"left":-1,"right":201,"lon":82.91424710801688,"lat":55.048553998373634,"zone":4,"sl":4}, +{"id":139,"fwd":140,"left":-1,"right":202,"lon":82.91417749641275,"lat":55.0485478925623,"zone":4,"sl":4}, +{"id":140,"fwd":141,"left":-1,"right":203,"lon":82.91410788482891,"lat":55.048541786710956,"zone":4,"sl":4}, +{"id":141,"fwd":142,"left":-1,"right":204,"lon":82.91403827326533,"lat":55.048535680819626,"zone":4,"sl":4}, +{"id":142,"fwd":143,"left":-1,"right":205,"lon":82.91396866172204,"lat":55.04852957488833,"zone":4,"sl":4}, +{"id":143,"fwd":144,"left":-1,"right":206,"lon":82.91389905019905,"lat":55.048523468917054,"zone":4,"sl":4}, +{"id":144,"fwd":145,"left":-1,"right":207,"lon":82.91382943869634,"lat":55.04851736290577,"zone":4,"sl":4}, +{"id":145,"fwd":146,"left":-1,"right":208,"lon":82.9137598272139,"lat":55.04851125685452,"zone":4,"sl":4}, +{"id":146,"fwd":147,"left":-1,"right":209,"lon":82.91369021575171,"lat":55.04850515076327,"zone":4,"sl":4}, +{"id":147,"fwd":148,"left":-1,"right":210,"lon":82.91362060430984,"lat":55.04849904463205,"zone":4,"sl":4}, +{"id":148,"fwd":149,"left":-1,"right":211,"lon":82.91355099288823,"lat":55.04849293846086,"zone":4,"sl":4}, +{"id":149,"fwd":150,"left":-1,"right":212,"lon":82.91348138148693,"lat":55.04848683224967,"zone":4,"sl":4}, +{"id":150,"fwd":151,"left":-1,"right":213,"lon":82.91341177010588,"lat":55.0484807259985,"zone":4,"sl":4}, +{"id":151,"fwd":152,"left":-1,"right":214,"lon":82.91334215874514,"lat":55.048474619707335,"zone":4,"sl":4}, +{"id":152,"fwd":153,"left":-1,"right":215,"lon":82.91327254740467,"lat":55.0484685133762,"zone":4,"sl":4}, +{"id":153,"fwd":154,"left":-1,"right":216,"lon":82.91320293608449,"lat":55.04846240700508,"zone":4,"sl":4}, +{"id":154,"fwd":155,"left":-1,"right":217,"lon":82.9131333247846,"lat":55.04845630059399,"zone":4,"sl":4}, +{"id":155,"fwd":156,"left":-1,"right":218,"lon":82.91306371350497,"lat":55.04845019414289,"zone":4,"sl":4}, +{"id":156,"fwd":157,"left":-1,"right":219,"lon":82.91299410224563,"lat":55.04844408765182,"zone":4,"sl":4}, +{"id":157,"fwd":158,"left":-1,"right":220,"lon":82.91292449100659,"lat":55.048437981120756,"zone":4,"sl":4}, +{"id":158,"fwd":159,"left":-1,"right":221,"lon":82.9128548797878,"lat":55.04843187454972,"zone":4,"sl":4}, +{"id":159,"fwd":160,"left":-1,"right":222,"lon":82.91278526858932,"lat":55.0484257679387,"zone":4,"sl":4}, +{"id":160,"fwd":161,"left":-1,"right":223,"lon":82.91271565741114,"lat":55.048419661287696,"zone":4,"sl":4}, +{"id":161,"fwd":162,"left":-1,"right":224,"lon":82.91264604625322,"lat":55.0484135545967,"zone":4,"sl":4}, +{"id":162,"fwd":163,"left":-1,"right":225,"lon":82.91257643511558,"lat":55.04840744786572,"zone":4,"sl":4}, +{"id":163,"fwd":164,"left":-1,"right":226,"lon":82.91250682399824,"lat":55.04840134109476,"zone":4,"sl":4}, +{"id":164,"fwd":165,"left":-1,"right":227,"lon":82.91243721290118,"lat":55.04839523428384,"zone":4,"sl":4}, +{"id":165,"fwd":166,"left":-1,"right":228,"lon":82.9123676018244,"lat":55.04838912743291,"zone":4,"sl":4}, +{"id":166,"fwd":167,"left":-1,"right":229,"lon":82.9122979907679,"lat":55.04838302054201,"zone":4,"sl":4}, +{"id":167,"fwd":168,"left":-1,"right":230,"lon":82.91222837973169,"lat":55.04837691361111,"zone":4,"sl":4}, +{"id":168,"fwd":169,"left":-1,"right":231,"lon":82.91215876871578,"lat":55.04837080664024,"zone":4,"sl":4}, +{"id":169,"fwd":170,"left":-1,"right":232,"lon":82.91208915772015,"lat":55.04836469962939,"zone":4,"sl":4}, +{"id":170,"fwd":171,"left":-1,"right":233,"lon":82.91201954674479,"lat":55.04835859257855,"zone":4,"sl":4}, +{"id":171,"fwd":172,"left":-1,"right":234,"lon":82.91194993578972,"lat":55.04835248548773,"zone":4,"sl":4}, +{"id":172,"fwd":173,"left":-1,"right":247,"lon":82.91188032485493,"lat":55.04834637835691,"zone":4,"sl":4}, +{"id":173,"fwd":174,"left":-1,"right":-1,"lon":82.91181071394045,"lat":55.048340271186134,"zone":4,"sl":4}, +{"id":174,"fwd":-1,"left":920,"right":-1,"lon":82.91174590891862,"lat":55.04833458561293,"zone":4,"sl":4}, +{"id":189,"fwd":190,"left":128,"right":265,"lon":82.91500456629639,"lat":55.0486522455027,"zone":1,"sl":4}, +{"id":190,"fwd":191,"left":129,"right":266,"lon":82.91493495441627,"lat":55.04864614012688,"zone":4,"sl":4}, +{"id":191,"fwd":192,"left":130,"right":267,"lon":82.91486534255645,"lat":55.04864003471108,"zone":4,"sl":4}, +{"id":192,"fwd":193,"left":131,"right":268,"lon":82.9147957307169,"lat":55.04863392925528,"zone":4,"sl":4}, +{"id":193,"fwd":194,"left":132,"right":269,"lon":82.91472611889765,"lat":55.0486278237595,"zone":4,"sl":4}, +{"id":194,"fwd":195,"left":133,"right":270,"lon":82.91465650709864,"lat":55.04862171822374,"zone":4,"sl":4}, +{"id":195,"fwd":196,"left":134,"right":271,"lon":82.91458689531993,"lat":55.04861561264799,"zone":4,"sl":4}, +{"id":196,"fwd":197,"left":135,"right":272,"lon":82.91451728356152,"lat":55.048609507032275,"zone":4,"sl":4}, +{"id":197,"fwd":198,"left":136,"right":273,"lon":82.91444767182335,"lat":55.04860340137655,"zone":4,"sl":4}, +{"id":198,"fwd":199,"left":137,"right":274,"lon":82.91437806010549,"lat":55.04859729568087,"zone":4,"sl":4}, +{"id":199,"fwd":200,"left":138,"right":275,"lon":82.91430844840791,"lat":55.04859118994519,"zone":4,"sl":4}, +{"id":200,"fwd":201,"left":139,"right":276,"lon":82.91423883673059,"lat":55.04858508416954,"zone":4,"sl":4}, +{"id":201,"fwd":202,"left":140,"right":277,"lon":82.91416922507358,"lat":55.04857897835387,"zone":4,"sl":4}, +{"id":202,"fwd":203,"left":141,"right":278,"lon":82.91409961343682,"lat":55.04857287249824,"zone":4,"sl":4}, +{"id":203,"fwd":204,"left":142,"right":279,"lon":82.91403000182038,"lat":55.048566766602626,"zone":4,"sl":4}, +{"id":204,"fwd":205,"left":143,"right":280,"lon":82.91396039022418,"lat":55.04856066066704,"zone":4,"sl":4}, +{"id":205,"fwd":206,"left":144,"right":281,"lon":82.91389077864828,"lat":55.04855455469145,"zone":4,"sl":4}, +{"id":206,"fwd":207,"left":145,"right":282,"lon":82.91382116709265,"lat":55.04854844867589,"zone":4,"sl":4}, +{"id":207,"fwd":208,"left":146,"right":283,"lon":82.91375155555731,"lat":55.048542342620344,"zone":4,"sl":4}, +{"id":208,"fwd":209,"left":147,"right":284,"lon":82.91368194404227,"lat":55.048536236524804,"zone":4,"sl":4}, +{"id":209,"fwd":210,"left":148,"right":285,"lon":82.91361233254747,"lat":55.04853013038929,"zone":4,"sl":4}, +{"id":210,"fwd":211,"left":149,"right":286,"lon":82.91354272107299,"lat":55.048524024213776,"zone":4,"sl":4}, +{"id":211,"fwd":212,"left":150,"right":287,"lon":82.91347310961878,"lat":55.048517917998296,"zone":4,"sl":4}, +{"id":212,"fwd":213,"left":151,"right":288,"lon":82.91340349818485,"lat":55.048511811742834,"zone":4,"sl":4}, +{"id":213,"fwd":214,"left":152,"right":289,"lon":82.9133338867712,"lat":55.04850570544739,"zone":4,"sl":4}, +{"id":214,"fwd":215,"left":153,"right":290,"lon":82.91326427537783,"lat":55.04849959911194,"zone":4,"sl":4}, +{"id":215,"fwd":216,"left":154,"right":291,"lon":82.91319466400475,"lat":55.048493492736526,"zone":4,"sl":4}, +{"id":216,"fwd":217,"left":155,"right":292,"lon":82.91312505265195,"lat":55.04848738632113,"zone":4,"sl":4}, +{"id":217,"fwd":218,"left":156,"right":293,"lon":82.91305544131943,"lat":55.04848127986574,"zone":4,"sl":4}, +{"id":218,"fwd":219,"left":157,"right":294,"lon":82.91298583000722,"lat":55.04847517337037,"zone":4,"sl":4}, +{"id":219,"fwd":220,"left":158,"right":295,"lon":82.91291621871525,"lat":55.04846906683502,"zone":4,"sl":4}, +{"id":220,"fwd":221,"left":159,"right":296,"lon":82.91284660744361,"lat":55.04846296025967,"zone":4,"sl":4}, +{"id":221,"fwd":222,"left":160,"right":297,"lon":82.9127769961922,"lat":55.04845685364435,"zone":4,"sl":4}, +{"id":222,"fwd":223,"left":161,"right":298,"lon":82.91270738496111,"lat":55.04845074698904,"zone":4,"sl":4}, +{"id":223,"fwd":224,"left":162,"right":299,"lon":82.9126377737503,"lat":55.048444640293766,"zone":4,"sl":4}, +{"id":224,"fwd":225,"left":163,"right":300,"lon":82.91256816255978,"lat":55.04843853355849,"zone":4,"sl":4}, +{"id":225,"fwd":226,"left":164,"right":301,"lon":82.91249855138953,"lat":55.048432426783236,"zone":4,"sl":4}, +{"id":226,"fwd":227,"left":165,"right":302,"lon":82.91242894023958,"lat":55.04842631996801,"zone":4,"sl":4}, +{"id":227,"fwd":228,"left":166,"right":303,"lon":82.91235932910989,"lat":55.048420213112784,"zone":4,"sl":4}, +{"id":228,"fwd":229,"left":167,"right":304,"lon":82.91228971800051,"lat":55.04841410621758,"zone":4,"sl":4}, +{"id":229,"fwd":230,"left":168,"right":305,"lon":82.9122201069114,"lat":55.0484079992824,"zone":4,"sl":4}, +{"id":230,"fwd":231,"left":169,"right":306,"lon":82.91215049584258,"lat":55.048401892307226,"zone":4,"sl":4}, +{"id":231,"fwd":232,"left":170,"right":307,"lon":82.91208088479404,"lat":55.04839578529208,"zone":4,"sl":4}, +{"id":232,"fwd":233,"left":171,"right":308,"lon":82.91201127376581,"lat":55.048389678236944,"zone":4,"sl":4}, +{"id":233,"fwd":234,"left":172,"right":309,"lon":82.91194166275784,"lat":55.04838357114182,"zone":4,"sl":4}, +{"id":234,"fwd":247,"left":173,"right":322,"lon":82.91187205177016,"lat":55.04837746400673,"zone":4,"sl":4}, +{"id":247,"fwd":249,"left":-1,"right":-1,"lon":82.9118024408028,"lat":55.04837135683164,"zone":4,"sl":4}, +{"id":249,"fwd":852,"left":-1,"right":-1,"lon":82.9117376357317,"lat":55.04836567125445,"zone":4,"sl":4}, +{"id":264,"fwd":265,"left":190,"right":-1,"lon":82.9149962955797,"lat":55.048683331345224,"zone":1,"sl":4}, +{"id":265,"fwd":266,"left":191,"right":-1,"lon":82.91492668364668,"lat":55.048677225965115,"zone":4,"sl":4}, +{"id":266,"fwd":267,"left":192,"right":-1,"lon":82.91485707173398,"lat":55.04867112054501,"zone":4,"sl":4}, +{"id":267,"fwd":268,"left":193,"right":-1,"lon":82.91478745984153,"lat":55.04866501508492,"zone":4,"sl":4}, +{"id":268,"fwd":269,"left":194,"right":-1,"lon":82.91471784796937,"lat":55.048658909584844,"zone":4,"sl":4}, +{"id":269,"fwd":270,"left":195,"right":-1,"lon":82.91464823611747,"lat":55.0486528040448,"zone":4,"sl":4}, +{"id":270,"fwd":271,"left":196,"right":-1,"lon":82.91457862428588,"lat":55.04864669846477,"zone":4,"sl":4}, +{"id":271,"fwd":272,"left":197,"right":-1,"lon":82.91450901247454,"lat":55.048640592844734,"zone":4,"sl":4}, +{"id":272,"fwd":273,"left":198,"right":-1,"lon":82.91443940068349,"lat":55.04863448718473,"zone":4,"sl":4}, +{"id":273,"fwd":274,"left":199,"right":-1,"lon":82.91436978891272,"lat":55.048628381484725,"zone":4,"sl":4}, +{"id":274,"fwd":275,"left":200,"right":-1,"lon":82.91430017716225,"lat":55.048622275744776,"zone":4,"sl":4}, +{"id":275,"fwd":276,"left":201,"right":-1,"lon":82.91423056543204,"lat":55.048616169964816,"zone":4,"sl":4}, +{"id":276,"fwd":277,"left":202,"right":-1,"lon":82.91416095372212,"lat":55.048610064144874,"zone":4,"sl":4}, +{"id":277,"fwd":278,"left":203,"right":-1,"lon":82.91409134203248,"lat":55.04860395828494,"zone":4,"sl":4}, +{"id":278,"fwd":279,"left":204,"right":-1,"lon":82.9140217303631,"lat":55.04859785238503,"zone":4,"sl":4}, +{"id":279,"fwd":280,"left":205,"right":-1,"lon":82.91395211871402,"lat":55.04859174644513,"zone":4,"sl":4}, +{"id":280,"fwd":281,"left":206,"right":-1,"lon":82.91388250708522,"lat":55.04858564046526,"zone":4,"sl":4}, +{"id":281,"fwd":282,"left":207,"right":-1,"lon":82.9138128954767,"lat":55.048579534445395,"zone":4,"sl":4}, +{"id":282,"fwd":283,"left":208,"right":-1,"lon":82.91374328388848,"lat":55.04857342838556,"zone":4,"sl":4}, +{"id":283,"fwd":284,"left":209,"right":-1,"lon":82.91367367232051,"lat":55.04856732228572,"zone":4,"sl":4}, +{"id":284,"fwd":285,"left":210,"right":-1,"lon":82.91360406077283,"lat":55.0485612161459,"zone":4,"sl":4}, +{"id":285,"fwd":286,"left":211,"right":-1,"lon":82.91353444924545,"lat":55.0485551099661,"zone":4,"sl":4}, +{"id":286,"fwd":287,"left":212,"right":-1,"lon":82.91346483773832,"lat":55.04854900374632,"zone":4,"sl":4}, +{"id":287,"fwd":288,"left":213,"right":-1,"lon":82.91339522625151,"lat":55.04854289748657,"zone":4,"sl":4}, +{"id":288,"fwd":289,"left":214,"right":-1,"lon":82.91332561478497,"lat":55.04853679118681,"zone":4,"sl":4}, +{"id":289,"fwd":290,"left":215,"right":-1,"lon":82.91325600333872,"lat":55.04853068484707,"zone":4,"sl":4}, +{"id":290,"fwd":291,"left":216,"right":-1,"lon":82.91318639191275,"lat":55.04852457846736,"zone":4,"sl":4}, +{"id":291,"fwd":292,"left":217,"right":-1,"lon":82.91311678050704,"lat":55.04851847204766,"zone":4,"sl":4}, +{"id":292,"fwd":293,"left":218,"right":-1,"lon":82.91304716912163,"lat":55.04851236558799,"zone":4,"sl":4}, +{"id":293,"fwd":294,"left":219,"right":-1,"lon":82.91297755775648,"lat":55.04850625908831,"zone":4,"sl":4}, +{"id":294,"fwd":295,"left":220,"right":-1,"lon":82.91290794641164,"lat":55.04850015254865,"zone":4,"sl":4}, +{"id":295,"fwd":296,"left":221,"right":-1,"lon":82.91283833508707,"lat":55.048494045969036,"zone":4,"sl":4}, +{"id":296,"fwd":297,"left":222,"right":-1,"lon":82.9127687237828,"lat":55.048487939349414,"zone":4,"sl":4}, +{"id":297,"fwd":298,"left":223,"right":-1,"lon":82.91269911249881,"lat":55.04848183268981,"zone":4,"sl":4}, +{"id":298,"fwd":299,"left":224,"right":-1,"lon":82.91262950123512,"lat":55.04847572599023,"zone":4,"sl":4}, +{"id":299,"fwd":300,"left":225,"right":-1,"lon":82.91255988999168,"lat":55.04846961925065,"zone":4,"sl":4}, +{"id":300,"fwd":301,"left":226,"right":-1,"lon":82.91249027876853,"lat":55.0484635124711,"zone":4,"sl":4}, +{"id":301,"fwd":302,"left":227,"right":-1,"lon":82.91242066756568,"lat":55.04845740565158,"zone":4,"sl":4}, +{"id":302,"fwd":303,"left":228,"right":-1,"lon":82.91235105638312,"lat":55.04845129879206,"zone":4,"sl":4}, +{"id":303,"fwd":304,"left":229,"right":-1,"lon":82.91228144522081,"lat":55.04844519189256,"zone":4,"sl":4}, +{"id":304,"fwd":305,"left":230,"right":-1,"lon":82.91221183407882,"lat":55.04843908495309,"zone":4,"sl":4}, +{"id":305,"fwd":306,"left":231,"right":-1,"lon":82.91214222295712,"lat":55.048432977973604,"zone":4,"sl":4}, +{"id":306,"fwd":307,"left":232,"right":-1,"lon":82.91207261185568,"lat":55.04842687095415,"zone":4,"sl":4}, +{"id":307,"fwd":308,"left":233,"right":-1,"lon":82.91200300077453,"lat":55.048420763894725,"zone":4,"sl":4}, +{"id":308,"fwd":309,"left":234,"right":-1,"lon":82.91193338971368,"lat":55.048414656795316,"zone":4,"sl":4}, +{"id":309,"fwd":322,"left":247,"right":-1,"lon":82.91186377867314,"lat":55.048408549655925,"zone":4,"sl":4}, +{"id":322,"fwd":324,"left":-1,"right":-1,"lon":82.91179416765284,"lat":55.048402442476515,"zone":4,"sl":4}, +{"id":324,"fwd":842,"left":-1,"right":-1,"lon":82.9117293625325,"lat":55.04839675689535,"zone":4,"sl":4}, +{"id":326,"fwd":327,"left":-1,"right":389,"lon":82.91177799000967,"lat":55.04822616541148,"zone":4,"sl":4}, +{"id":327,"fwd":328,"left":-1,"right":390,"lon":82.91184753601063,"lat":55.04823251098372,"zone":4,"sl":4}, +{"id":328,"fwd":329,"left":-1,"right":391,"lon":82.91191708203272,"lat":55.04823885651602,"zone":4,"sl":4}, +{"id":329,"fwd":330,"left":-1,"right":392,"lon":82.91198662807588,"lat":55.04824520200842,"zone":4,"sl":4}, +{"id":330,"fwd":331,"left":-1,"right":393,"lon":82.91205617414015,"lat":55.0482515474609,"zone":4,"sl":4}, +{"id":331,"fwd":332,"left":-1,"right":394,"lon":82.91212572022553,"lat":55.048257892873465,"zone":4,"sl":4}, +{"id":332,"fwd":333,"left":-1,"right":395,"lon":82.91219526633199,"lat":55.048264238246105,"zone":4,"sl":4}, +{"id":333,"fwd":334,"left":-1,"right":396,"lon":82.91226481245955,"lat":55.04827058357883,"zone":4,"sl":4}, +{"id":334,"fwd":335,"left":-1,"right":397,"lon":82.91233435860822,"lat":55.04827692887165,"zone":4,"sl":4}, +{"id":335,"fwd":336,"left":-1,"right":398,"lon":82.91240390477795,"lat":55.04828327412456,"zone":4,"sl":4}, +{"id":336,"fwd":337,"left":-1,"right":399,"lon":82.91247345096879,"lat":55.04828961933754,"zone":4,"sl":4}, +{"id":337,"fwd":338,"left":-1,"right":400,"lon":82.91254299718072,"lat":55.04829596451061,"zone":4,"sl":4}, +{"id":338,"fwd":339,"left":-1,"right":401,"lon":82.91261254341376,"lat":55.04830230964375,"zone":4,"sl":4}, +{"id":339,"fwd":340,"left":-1,"right":402,"lon":82.91268208966788,"lat":55.048308654737,"zone":4,"sl":4}, +{"id":340,"fwd":341,"left":-1,"right":403,"lon":82.9127516359431,"lat":55.048314999790314,"zone":4,"sl":4}, +{"id":341,"fwd":342,"left":-1,"right":404,"lon":82.9128211822394,"lat":55.048321344803725,"zone":4,"sl":4}, +{"id":342,"fwd":343,"left":-1,"right":405,"lon":82.91289072855682,"lat":55.04832768977721,"zone":4,"sl":4}, +{"id":343,"fwd":344,"left":-1,"right":406,"lon":82.91296027489531,"lat":55.048334034710784,"zone":4,"sl":4}, +{"id":344,"fwd":345,"left":-1,"right":407,"lon":82.91302982125492,"lat":55.04834037960444,"zone":4,"sl":4}, +{"id":345,"fwd":346,"left":-1,"right":408,"lon":82.91309936763561,"lat":55.048346724458185,"zone":4,"sl":4}, +{"id":346,"fwd":347,"left":-1,"right":409,"lon":82.91316891403739,"lat":55.048353069272004,"zone":4,"sl":4}, +{"id":347,"fwd":348,"left":-1,"right":410,"lon":82.91323846046026,"lat":55.04835941404592,"zone":4,"sl":4}, +{"id":348,"fwd":349,"left":-1,"right":411,"lon":82.91330800690424,"lat":55.04836575877992,"zone":4,"sl":4}, +{"id":349,"fwd":350,"left":-1,"right":412,"lon":82.91337755336929,"lat":55.04837210347399,"zone":4,"sl":4}, +{"id":350,"fwd":351,"left":-1,"right":413,"lon":82.91344709985546,"lat":55.04837844812815,"zone":4,"sl":4}, +{"id":351,"fwd":352,"left":-1,"right":414,"lon":82.91351664636271,"lat":55.04838479274239,"zone":4,"sl":4}, +{"id":352,"fwd":353,"left":-1,"right":415,"lon":82.91358619289103,"lat":55.04839113731673,"zone":4,"sl":4}, +{"id":353,"fwd":354,"left":-1,"right":416,"lon":82.91365573944044,"lat":55.048397481851126,"zone":4,"sl":4}, +{"id":354,"fwd":355,"left":-1,"right":417,"lon":82.91372528601099,"lat":55.04840382634564,"zone":4,"sl":4}, +{"id":355,"fwd":356,"left":-1,"right":418,"lon":82.9137948326026,"lat":55.04841017080022,"zone":4,"sl":4}, +{"id":356,"fwd":357,"left":-1,"right":419,"lon":82.91386437921531,"lat":55.048416515214875,"zone":4,"sl":4}, +{"id":357,"fwd":358,"left":-1,"right":420,"lon":82.91393392584911,"lat":55.04842285958964,"zone":4,"sl":4}, +{"id":358,"fwd":359,"left":-1,"right":421,"lon":82.91400347250398,"lat":55.04842920392446,"zone":4,"sl":4}, +{"id":359,"fwd":360,"left":-1,"right":422,"lon":82.91407301917997,"lat":55.048435548219366,"zone":4,"sl":4}, +{"id":360,"fwd":361,"left":-1,"right":423,"lon":82.91414256587704,"lat":55.04844189247437,"zone":4,"sl":4}, +{"id":361,"fwd":362,"left":-1,"right":424,"lon":82.9142121125952,"lat":55.04844823668945,"zone":4,"sl":4}, +{"id":362,"fwd":363,"left":-1,"right":425,"lon":82.91428165933446,"lat":55.04845458086462,"zone":4,"sl":4}, +{"id":363,"fwd":364,"left":-1,"right":426,"lon":82.9143512060948,"lat":55.04846092499987,"zone":4,"sl":4}, +{"id":364,"fwd":365,"left":-1,"right":427,"lon":82.91442075287624,"lat":55.048467269095205,"zone":4,"sl":4}, +{"id":365,"fwd":366,"left":-1,"right":428,"lon":82.91449029967876,"lat":55.04847361315062,"zone":4,"sl":4}, +{"id":366,"fwd":367,"left":-1,"right":429,"lon":82.91455984650239,"lat":55.04847995716612,"zone":4,"sl":4}, +{"id":367,"fwd":368,"left":-1,"right":430,"lon":82.91462939334707,"lat":55.0484863011417,"zone":4,"sl":4}, +{"id":368,"fwd":369,"left":-1,"right":431,"lon":82.91469894021287,"lat":55.04849264507737,"zone":4,"sl":4}, +{"id":369,"fwd":370,"left":-1,"right":432,"lon":82.91476848709975,"lat":55.04849898897314,"zone":4,"sl":4}, +{"id":370,"fwd":371,"left":-1,"right":433,"lon":82.91483803400773,"lat":55.04850533282895,"zone":4,"sl":4}, +{"id":371,"fwd":372,"left":-1,"right":446,"lon":82.91490758093678,"lat":55.048511676644864,"zone":4,"sl":4}, +{"id":372,"fwd":373,"left":-1,"right":-1,"lon":82.91497712788694,"lat":55.04851802042087,"zone":4,"sl":4}, +{"id":373,"fwd":-1,"left":-1,"right":-1,"lon":82.91504559360291,"lat":55.04852426553043,"zone":2,"sl":4}, +{"id":388,"fwd":389,"left":327,"right":464,"lon":82.91178658606168,"lat":55.04819510866461,"zone":4,"sl":4}, +{"id":389,"fwd":390,"left":328,"right":465,"lon":82.91185613200997,"lat":55.04820145423235,"zone":4,"sl":4}, +{"id":390,"fwd":391,"left":329,"right":466,"lon":82.91192567797935,"lat":55.04820779976019,"zone":4,"sl":4}, +{"id":391,"fwd":392,"left":330,"right":467,"lon":82.91199522396981,"lat":55.0482141452481,"zone":4,"sl":4}, +{"id":392,"fwd":393,"left":331,"right":468,"lon":82.91206476998138,"lat":55.048220490696096,"zone":4,"sl":4}, +{"id":393,"fwd":394,"left":332,"right":469,"lon":82.91213431601405,"lat":55.048226836104185,"zone":4,"sl":4}, +{"id":394,"fwd":395,"left":333,"right":470,"lon":82.91220386206781,"lat":55.04823318147234,"zone":4,"sl":4}, +{"id":395,"fwd":396,"left":334,"right":471,"lon":82.91227340814265,"lat":55.048239526800614,"zone":4,"sl":4}, +{"id":396,"fwd":397,"left":335,"right":472,"lon":82.91234295423861,"lat":55.048245872088955,"zone":4,"sl":4}, +{"id":397,"fwd":398,"left":336,"right":473,"lon":82.91241250035566,"lat":55.04825221733735,"zone":4,"sl":4}, +{"id":398,"fwd":399,"left":337,"right":474,"lon":82.91248204649378,"lat":55.048258562545875,"zone":4,"sl":4}, +{"id":399,"fwd":400,"left":338,"right":475,"lon":82.91255159265302,"lat":55.04826490771446,"zone":4,"sl":4}, +{"id":400,"fwd":401,"left":339,"right":476,"lon":82.91262113883336,"lat":55.048271252843136,"zone":4,"sl":4}, +{"id":401,"fwd":402,"left":340,"right":477,"lon":82.91269068503478,"lat":55.0482775979319,"zone":4,"sl":4}, +{"id":402,"fwd":403,"left":341,"right":478,"lon":82.91276023125727,"lat":55.048283942980746,"zone":4,"sl":4}, +{"id":403,"fwd":404,"left":342,"right":479,"lon":82.91282977750089,"lat":55.04829028798968,"zone":4,"sl":4}, +{"id":404,"fwd":405,"left":343,"right":480,"lon":82.9128993237656,"lat":55.04829663295867,"zone":4,"sl":4}, +{"id":405,"fwd":406,"left":344,"right":481,"lon":82.9129688700514,"lat":55.048302977887786,"zone":4,"sl":4}, +{"id":406,"fwd":407,"left":345,"right":482,"lon":82.91303841635828,"lat":55.048309322776966,"zone":4,"sl":4}, +{"id":407,"fwd":408,"left":346,"right":483,"lon":82.91310796268627,"lat":55.04831566762622,"zone":4,"sl":4}, +{"id":408,"fwd":409,"left":347,"right":484,"lon":82.91317750903534,"lat":55.04832201243557,"zone":4,"sl":4}, +{"id":409,"fwd":410,"left":348,"right":485,"lon":82.91324705540552,"lat":55.048328357205,"zone":4,"sl":4}, +{"id":410,"fwd":411,"left":349,"right":486,"lon":82.91331660179678,"lat":55.048334701934515,"zone":4,"sl":4}, +{"id":411,"fwd":412,"left":350,"right":487,"lon":82.91338614820913,"lat":55.04834104662411,"zone":4,"sl":4}, +{"id":412,"fwd":413,"left":351,"right":488,"lon":82.91345569464261,"lat":55.0483473912738,"zone":4,"sl":4}, +{"id":413,"fwd":414,"left":352,"right":489,"lon":82.91352524109712,"lat":55.048353735883566,"zone":4,"sl":4}, +{"id":414,"fwd":415,"left":353,"right":490,"lon":82.91359478757276,"lat":55.04836008045342,"zone":4,"sl":4}, +{"id":415,"fwd":416,"left":354,"right":491,"lon":82.91366433406948,"lat":55.04836642498335,"zone":4,"sl":4}, +{"id":416,"fwd":417,"left":355,"right":492,"lon":82.91373388058732,"lat":55.04837276947338,"zone":4,"sl":4}, +{"id":417,"fwd":418,"left":356,"right":493,"lon":82.9138034271262,"lat":55.04837911392349,"zone":4,"sl":4}, +{"id":418,"fwd":419,"left":357,"right":494,"lon":82.91387297368621,"lat":55.04838545833367,"zone":4,"sl":4}, +{"id":419,"fwd":420,"left":358,"right":495,"lon":82.91394252026731,"lat":55.04839180270393,"zone":4,"sl":4}, +{"id":420,"fwd":421,"left":359,"right":496,"lon":82.91401206686949,"lat":55.04839814703429,"zone":4,"sl":4}, +{"id":421,"fwd":422,"left":360,"right":497,"lon":82.91408161349277,"lat":55.04840449132473,"zone":4,"sl":4}, +{"id":422,"fwd":423,"left":361,"right":498,"lon":82.91415116013712,"lat":55.04841083557526,"zone":4,"sl":4}, +{"id":423,"fwd":424,"left":362,"right":499,"lon":82.91422070680258,"lat":55.04841717978587,"zone":4,"sl":4}, +{"id":424,"fwd":425,"left":363,"right":500,"lon":82.91429025348913,"lat":55.04842352395655,"zone":4,"sl":4}, +{"id":425,"fwd":426,"left":364,"right":501,"lon":82.91435980019676,"lat":55.04842986808732,"zone":4,"sl":4}, +{"id":426,"fwd":427,"left":365,"right":502,"lon":82.91442934692549,"lat":55.048436212178174,"zone":4,"sl":4}, +{"id":427,"fwd":428,"left":366,"right":503,"lon":82.9144988936753,"lat":55.04844255622912,"zone":4,"sl":4}, +{"id":428,"fwd":429,"left":367,"right":504,"lon":82.9145684404462,"lat":55.048448900240146,"zone":4,"sl":4}, +{"id":429,"fwd":430,"left":368,"right":505,"lon":82.91463798723822,"lat":55.04845524421126,"zone":4,"sl":4}, +{"id":430,"fwd":431,"left":369,"right":506,"lon":82.9147075340513,"lat":55.04846158814245,"zone":4,"sl":4}, +{"id":431,"fwd":432,"left":370,"right":507,"lon":82.91477708088549,"lat":55.04846793203371,"zone":4,"sl":4}, +{"id":432,"fwd":433,"left":371,"right":508,"lon":82.91484662774074,"lat":55.04847427588507,"zone":4,"sl":4}, +{"id":433,"fwd":446,"left":372,"right":521,"lon":82.9149161746171,"lat":55.048480619696505,"zone":4,"sl":4}, +{"id":446,"fwd":448,"left":-1,"right":-1,"lon":82.91498572151451,"lat":55.04848696346806,"zone":4,"sl":4}, +{"id":448,"fwd":-1,"left":-1,"right":-1,"lon":82.91505418717861,"lat":55.04849320857319,"zone":2,"sl":4}, +{"id":463,"fwd":464,"left":389,"right":-1,"lon":82.91179518210097,"lat":55.048164051917084,"zone":4,"sl":4}, +{"id":464,"fwd":465,"left":390,"right":-1,"lon":82.91186472799654,"lat":55.04817039748035,"zone":4,"sl":4}, +{"id":465,"fwd":466,"left":391,"right":-1,"lon":82.9119342739132,"lat":55.04817674300371,"zone":4,"sl":4}, +{"id":466,"fwd":467,"left":392,"right":-1,"lon":82.91200381985097,"lat":55.04818308848714,"zone":4,"sl":4}, +{"id":467,"fwd":468,"left":393,"right":-1,"lon":82.91207336580986,"lat":55.048189433930666,"zone":4,"sl":4}, +{"id":468,"fwd":469,"left":394,"right":-1,"lon":82.91214291178981,"lat":55.048195779334264,"zone":4,"sl":4}, +{"id":469,"fwd":470,"left":395,"right":-1,"lon":82.91221245779084,"lat":55.048202124697966,"zone":4,"sl":4}, +{"id":470,"fwd":471,"left":396,"right":-1,"lon":82.91228200381302,"lat":55.048208470021734,"zone":4,"sl":4}, +{"id":471,"fwd":472,"left":397,"right":-1,"lon":82.91235154985624,"lat":55.0482148153056,"zone":4,"sl":4}, +{"id":472,"fwd":473,"left":398,"right":-1,"lon":82.91242109592059,"lat":55.04822116054955,"zone":4,"sl":4}, +{"id":473,"fwd":474,"left":399,"right":-1,"lon":82.91249064200603,"lat":55.04822750575358,"zone":4,"sl":4}, +{"id":474,"fwd":475,"left":400,"right":-1,"lon":82.91256018811255,"lat":55.04823385091768,"zone":4,"sl":4}, +{"id":475,"fwd":476,"left":401,"right":-1,"lon":82.91262973424017,"lat":55.048240196041874,"zone":4,"sl":4}, +{"id":476,"fwd":477,"left":402,"right":-1,"lon":82.91269928038889,"lat":55.048246541126176,"zone":4,"sl":4}, +{"id":477,"fwd":478,"left":403,"right":-1,"lon":82.9127688265587,"lat":55.04825288617053,"zone":4,"sl":4}, +{"id":478,"fwd":479,"left":404,"right":-1,"lon":82.91283837274962,"lat":55.048259231174974,"zone":4,"sl":4}, +{"id":479,"fwd":480,"left":405,"right":-1,"lon":82.91290791896161,"lat":55.048265576139514,"zone":4,"sl":4}, +{"id":480,"fwd":481,"left":406,"right":-1,"lon":82.91297746519471,"lat":55.048271921064135,"zone":4,"sl":4}, +{"id":481,"fwd":482,"left":407,"right":-1,"lon":82.9130470114489,"lat":55.04827826594884,"zone":4,"sl":4}, +{"id":482,"fwd":483,"left":408,"right":-1,"lon":82.91311655772417,"lat":55.04828461079362,"zone":4,"sl":4}, +{"id":483,"fwd":484,"left":409,"right":-1,"lon":82.91318610402055,"lat":55.048290955598496,"zone":4,"sl":4}, +{"id":484,"fwd":485,"left":410,"right":-1,"lon":82.91325565033802,"lat":55.04829730036346,"zone":4,"sl":4}, +{"id":485,"fwd":486,"left":411,"right":-1,"lon":82.91332519667658,"lat":55.04830364508848,"zone":4,"sl":4}, +{"id":486,"fwd":487,"left":412,"right":-1,"lon":82.91339474303622,"lat":55.0483099897736,"zone":4,"sl":4}, +{"id":487,"fwd":488,"left":413,"right":-1,"lon":82.91346428941696,"lat":55.048316334418814,"zone":4,"sl":4}, +{"id":488,"fwd":489,"left":414,"right":-1,"lon":82.91353383581881,"lat":55.04832267902411,"zone":4,"sl":4}, +{"id":489,"fwd":490,"left":415,"right":-1,"lon":82.91360338224173,"lat":55.04832902358948,"zone":4,"sl":4}, +{"id":490,"fwd":491,"left":416,"right":-1,"lon":82.91367292868574,"lat":55.04833536811495,"zone":4,"sl":4}, +{"id":491,"fwd":492,"left":417,"right":-1,"lon":82.91374247515085,"lat":55.04834171260048,"zone":4,"sl":4}, +{"id":492,"fwd":493,"left":418,"right":-1,"lon":82.91381202163707,"lat":55.04834805704611,"zone":4,"sl":4}, +{"id":493,"fwd":494,"left":419,"right":-1,"lon":82.91388156814438,"lat":55.048354401451824,"zone":4,"sl":4}, +{"id":494,"fwd":495,"left":420,"right":-1,"lon":82.91395111467276,"lat":55.048360745817625,"zone":4,"sl":4}, +{"id":495,"fwd":496,"left":421,"right":-1,"lon":82.91402066122222,"lat":55.04836709014349,"zone":4,"sl":4}, +{"id":496,"fwd":497,"left":422,"right":-1,"lon":82.91409020779281,"lat":55.04837343442944,"zone":4,"sl":4}, +{"id":497,"fwd":498,"left":423,"right":-1,"lon":82.91415975438447,"lat":55.0483797786755,"zone":4,"sl":4}, +{"id":498,"fwd":499,"left":424,"right":-1,"lon":82.9142293009972,"lat":55.04838612288163,"zone":4,"sl":4}, +{"id":499,"fwd":500,"left":425,"right":-1,"lon":82.91429884763104,"lat":55.04839246704784,"zone":4,"sl":4}, +{"id":500,"fwd":501,"left":426,"right":-1,"lon":82.914368394286,"lat":55.048398811174145,"zone":4,"sl":4}, +{"id":501,"fwd":502,"left":427,"right":-1,"lon":82.91443794096202,"lat":55.048405155260525,"zone":4,"sl":4}, +{"id":502,"fwd":503,"left":428,"right":-1,"lon":82.91450748765911,"lat":55.048411499306994,"zone":4,"sl":4}, +{"id":503,"fwd":504,"left":429,"right":-1,"lon":82.91457703437732,"lat":55.04841784331353,"zone":4,"sl":4}, +{"id":504,"fwd":505,"left":430,"right":-1,"lon":82.91464658111663,"lat":55.048424187280176,"zone":4,"sl":4}, +{"id":505,"fwd":506,"left":431,"right":-1,"lon":82.91471612787699,"lat":55.04843053120688,"zone":4,"sl":4}, +{"id":506,"fwd":507,"left":432,"right":-1,"lon":82.91478567465848,"lat":55.048436875093685,"zone":4,"sl":4}, +{"id":507,"fwd":508,"left":433,"right":-1,"lon":82.91485522146104,"lat":55.048443218940555,"zone":4,"sl":4}, +{"id":508,"fwd":521,"left":446,"right":-1,"lon":82.91492476828465,"lat":55.048449562747535,"zone":4,"sl":4}, +{"id":521,"fwd":523,"left":-1,"right":-1,"lon":82.91499431512938,"lat":55.048455906514576,"zone":4,"sl":4}, +{"id":523,"fwd":-1,"left":-1,"right":-1,"lon":82.91506278074155,"lat":55.04846215161532,"zone":2,"sl":4}, +{"id":525,"fwd":526,"left":-1,"right":575,"lon":82.91116602984454,"lat":55.04825539157567,"zone":4,"sl":4}, +{"id":526,"fwd":527,"left":-1,"right":576,"lon":82.91110144789997,"lat":55.048239274601706,"zone":4,"sl":4}, +{"id":527,"fwd":528,"left":-1,"right":577,"lon":82.91103686600665,"lat":55.04822315759302,"zone":4,"sl":4}, +{"id":528,"fwd":529,"left":-1,"right":578,"lon":82.91097228416457,"lat":55.048207040549634,"zone":4,"sl":4}, +{"id":529,"fwd":530,"left":-1,"right":579,"lon":82.91090770237372,"lat":55.04819092347154,"zone":4,"sl":4}, +{"id":530,"fwd":531,"left":-1,"right":580,"lon":82.91084312063414,"lat":55.04817480635872,"zone":4,"sl":4}, +{"id":531,"fwd":532,"left":-1,"right":581,"lon":82.91077853894576,"lat":55.04815868921121,"zone":4,"sl":4}, +{"id":532,"fwd":533,"left":-1,"right":582,"lon":82.91071395730863,"lat":55.04814257202899,"zone":4,"sl":4}, +{"id":533,"fwd":534,"left":-1,"right":583,"lon":82.91064937572274,"lat":55.04812645481208,"zone":4,"sl":4}, +{"id":534,"fwd":535,"left":-1,"right":584,"lon":82.9105847941881,"lat":55.04811033756043,"zone":4,"sl":4}, +{"id":535,"fwd":536,"left":-1,"right":585,"lon":82.91052021270468,"lat":55.048094220274095,"zone":4,"sl":4}, +{"id":536,"fwd":537,"left":-1,"right":586,"lon":82.91045563127253,"lat":55.04807810295305,"zone":4,"sl":4}, +{"id":537,"fwd":538,"left":-1,"right":587,"lon":82.9103910498916,"lat":55.04806198559728,"zone":4,"sl":4}, +{"id":538,"fwd":539,"left":-1,"right":588,"lon":82.9103264685619,"lat":55.048045868206806,"zone":4,"sl":4}, +{"id":539,"fwd":540,"left":-1,"right":589,"lon":82.91026188728347,"lat":55.048029750781645,"zone":4,"sl":4}, +{"id":540,"fwd":541,"left":-1,"right":590,"lon":82.91019730605626,"lat":55.04801363332175,"zone":4,"sl":4}, +{"id":541,"fwd":542,"left":-1,"right":591,"lon":82.91013272488027,"lat":55.04799751582716,"zone":4,"sl":4}, +{"id":542,"fwd":543,"left":-1,"right":592,"lon":82.91006814375557,"lat":55.04798139829786,"zone":4,"sl":4}, +{"id":543,"fwd":544,"left":-1,"right":593,"lon":82.91000356268208,"lat":55.04796528073385,"zone":4,"sl":4}, +{"id":544,"fwd":545,"left":-1,"right":594,"lon":82.90993898165982,"lat":55.04794916313514,"zone":4,"sl":4}, +{"id":545,"fwd":546,"left":-1,"right":595,"lon":82.90987440068882,"lat":55.04793304550171,"zone":4,"sl":4}, +{"id":546,"fwd":547,"left":-1,"right":596,"lon":82.90980981976904,"lat":55.04791692783359,"zone":4,"sl":4}, +{"id":547,"fwd":548,"left":-1,"right":597,"lon":82.9097452389005,"lat":55.04790081013075,"zone":4,"sl":4}, +{"id":548,"fwd":549,"left":-1,"right":598,"lon":82.90968065808323,"lat":55.04788469239321,"zone":4,"sl":4}, +{"id":549,"fwd":550,"left":-1,"right":599,"lon":82.90961607731718,"lat":55.04786857462096,"zone":4,"sl":4}, +{"id":550,"fwd":551,"left":-1,"right":600,"lon":82.90955149660238,"lat":55.047852456814,"zone":4,"sl":4}, +{"id":551,"fwd":552,"left":-1,"right":601,"lon":82.90948691593881,"lat":55.04783633897233,"zone":4,"sl":4}, +{"id":552,"fwd":553,"left":-1,"right":602,"lon":82.90942233532648,"lat":55.04782022109596,"zone":4,"sl":4}, +{"id":553,"fwd":554,"left":-1,"right":603,"lon":82.9093577547654,"lat":55.04780410318488,"zone":4,"sl":4}, +{"id":554,"fwd":555,"left":-1,"right":604,"lon":82.90929317425555,"lat":55.047787985239104,"zone":4,"sl":4}, +{"id":555,"fwd":556,"left":-1,"right":605,"lon":82.90922859379694,"lat":55.04777186725861,"zone":4,"sl":4}, +{"id":556,"fwd":557,"left":-1,"right":606,"lon":82.90916401338957,"lat":55.047755749243414,"zone":4,"sl":4}, +{"id":557,"fwd":558,"left":-1,"right":619,"lon":82.90909943303346,"lat":55.047739631193515,"zone":4,"sl":4}, +{"id":558,"fwd":559,"left":-1,"right":-1,"lon":82.90903485272855,"lat":55.0477235131089,"zone":4,"sl":4}, +{"id":559,"fwd":-1,"left":-1,"right":-1,"lon":82.90900953508616,"lat":55.04771719426695,"zone":2,"sl":4}, +{"id":574,"fwd":575,"left":526,"right":637,"lon":82.9111441969728,"lat":55.048284231545644,"zone":4,"sl":4}, +{"id":575,"fwd":576,"left":527,"right":638,"lon":82.91107961499003,"lat":55.04826811456046,"zone":4,"sl":4}, +{"id":576,"fwd":577,"left":528,"right":639,"lon":82.91101503305853,"lat":55.04825199754056,"zone":4,"sl":4}, +{"id":577,"fwd":578,"left":529,"right":640,"lon":82.91095045117825,"lat":55.04823588048595,"zone":4,"sl":4}, +{"id":578,"fwd":579,"left":530,"right":641,"lon":82.91088586934922,"lat":55.048219763396624,"zone":4,"sl":4}, +{"id":579,"fwd":580,"left":531,"right":642,"lon":82.91082128757144,"lat":55.048203646272604,"zone":4,"sl":4}, +{"id":580,"fwd":581,"left":532,"right":643,"lon":82.91075670584488,"lat":55.04818752911386,"zone":4,"sl":4}, +{"id":581,"fwd":582,"left":533,"right":644,"lon":82.91069212416956,"lat":55.04817141192043,"zone":4,"sl":4}, +{"id":582,"fwd":583,"left":534,"right":645,"lon":82.91062754254547,"lat":55.048155294692265,"zone":4,"sl":4}, +{"id":583,"fwd":584,"left":535,"right":646,"lon":82.91056296097263,"lat":55.048139177429405,"zone":4,"sl":4}, +{"id":584,"fwd":585,"left":536,"right":647,"lon":82.91049837945103,"lat":55.04812306013184,"zone":4,"sl":4}, +{"id":585,"fwd":586,"left":537,"right":648,"lon":82.91043379798067,"lat":55.04810694279957,"zone":4,"sl":4}, +{"id":586,"fwd":587,"left":538,"right":649,"lon":82.91036921656156,"lat":55.04809082543258,"zone":4,"sl":4}, +{"id":587,"fwd":588,"left":539,"right":650,"lon":82.9103046351937,"lat":55.048074708030896,"zone":4,"sl":4}, +{"id":588,"fwd":589,"left":540,"right":651,"lon":82.91024005387706,"lat":55.04805859059449,"zone":4,"sl":4}, +{"id":589,"fwd":590,"left":541,"right":652,"lon":82.91017547261164,"lat":55.048042473123374,"zone":4,"sl":4}, +{"id":590,"fwd":591,"left":542,"right":653,"lon":82.91011089139748,"lat":55.048026355617566,"zone":4,"sl":4}, +{"id":591,"fwd":592,"left":543,"right":654,"lon":82.91004631023456,"lat":55.04801023807705,"zone":4,"sl":4}, +{"id":592,"fwd":593,"left":544,"right":655,"lon":82.90998172912289,"lat":55.04799412050181,"zone":4,"sl":4}, +{"id":593,"fwd":594,"left":545,"right":656,"lon":82.90991714806243,"lat":55.047978002891874,"zone":4,"sl":4}, +{"id":594,"fwd":595,"left":546,"right":657,"lon":82.90985256705325,"lat":55.04796188524723,"zone":4,"sl":4}, +{"id":595,"fwd":596,"left":547,"right":658,"lon":82.90978798609528,"lat":55.04794576756788,"zone":4,"sl":4}, +{"id":596,"fwd":597,"left":548,"right":659,"lon":82.90972340518857,"lat":55.04792964985381,"zone":4,"sl":4}, +{"id":597,"fwd":598,"left":549,"right":660,"lon":82.9096588243331,"lat":55.04791353210504,"zone":4,"sl":4}, +{"id":598,"fwd":599,"left":550,"right":661,"lon":82.90959424352886,"lat":55.04789741432158,"zone":4,"sl":4}, +{"id":599,"fwd":600,"left":551,"right":662,"lon":82.90952966277585,"lat":55.0478812965034,"zone":4,"sl":4}, +{"id":600,"fwd":601,"left":552,"right":663,"lon":82.9094650820741,"lat":55.047865178650504,"zone":4,"sl":4}, +{"id":601,"fwd":602,"left":553,"right":664,"lon":82.90940050142358,"lat":55.047849060762914,"zone":4,"sl":4}, +{"id":602,"fwd":603,"left":554,"right":665,"lon":82.90933592082432,"lat":55.047832942840614,"zone":4,"sl":4}, +{"id":603,"fwd":604,"left":555,"right":666,"lon":82.90927134027626,"lat":55.0478168248836,"zone":4,"sl":4}, +{"id":604,"fwd":605,"left":556,"right":667,"lon":82.90920675977948,"lat":55.04780070689188,"zone":4,"sl":4}, +{"id":605,"fwd":606,"left":557,"right":668,"lon":82.90914217933394,"lat":55.04778458886546,"zone":4,"sl":4}, +{"id":606,"fwd":619,"left":558,"right":681,"lon":82.90907759893962,"lat":55.04776847080432,"zone":4,"sl":4}, +{"id":619,"fwd":621,"left":-1,"right":-1,"lon":82.90901301859655,"lat":55.04775235270849,"zone":4,"sl":4}, +{"id":621,"fwd":-1,"left":-1,"right":-1,"lon":82.90898770093919,"lat":55.04774603386215,"zone":2,"sl":4}, +{"id":636,"fwd":637,"left":575,"right":-1,"lon":82.91112236407005,"lat":55.04831307151185,"zone":4,"sl":4}, +{"id":637,"fwd":638,"left":576,"right":-1,"lon":82.91105778204911,"lat":55.04829695451543,"zone":4,"sl":4}, +{"id":638,"fwd":639,"left":577,"right":-1,"lon":82.91099320007942,"lat":55.048280837484285,"zone":4,"sl":4}, +{"id":639,"fwd":640,"left":578,"right":-1,"lon":82.91092861816094,"lat":55.048264720418466,"zone":4,"sl":4}, +{"id":640,"fwd":641,"left":579,"right":-1,"lon":82.91086403629372,"lat":55.048248603317916,"zone":4,"sl":4}, +{"id":641,"fwd":642,"left":580,"right":-1,"lon":82.91079945447774,"lat":55.04823248618266,"zone":4,"sl":4}, +{"id":642,"fwd":643,"left":581,"right":-1,"lon":82.910734872713,"lat":55.04821636901272,"zone":4,"sl":4}, +{"id":643,"fwd":644,"left":582,"right":-1,"lon":82.91067029099948,"lat":55.04820025180805,"zone":4,"sl":4}, +{"id":644,"fwd":645,"left":583,"right":-1,"lon":82.9106057093372,"lat":55.04818413456867,"zone":4,"sl":4}, +{"id":645,"fwd":646,"left":584,"right":-1,"lon":82.91054112772618,"lat":55.04816801729459,"zone":4,"sl":4}, +{"id":646,"fwd":647,"left":585,"right":-1,"lon":82.9104765461664,"lat":55.04815189998578,"zone":4,"sl":4}, +{"id":647,"fwd":648,"left":586,"right":-1,"lon":82.91041196465785,"lat":55.04813578264228,"zone":4,"sl":4}, +{"id":648,"fwd":649,"left":587,"right":-1,"lon":82.91034738320053,"lat":55.04811966526408,"zone":4,"sl":4}, +{"id":649,"fwd":650,"left":588,"right":-1,"lon":82.91028280179445,"lat":55.04810354785116,"zone":4,"sl":4}, +{"id":650,"fwd":651,"left":589,"right":-1,"lon":82.91021822043962,"lat":55.048087430403555,"zone":4,"sl":4}, +{"id":651,"fwd":652,"left":590,"right":-1,"lon":82.91015363913604,"lat":55.04807131292121,"zone":4,"sl":4}, +{"id":652,"fwd":653,"left":591,"right":-1,"lon":82.91008905788368,"lat":55.04805519540417,"zone":4,"sl":4}, +{"id":653,"fwd":654,"left":592,"right":-1,"lon":82.91002447668258,"lat":55.04803907785242,"zone":4,"sl":4}, +{"id":654,"fwd":655,"left":593,"right":-1,"lon":82.90995989553272,"lat":55.04802296026597,"zone":4,"sl":4}, +{"id":655,"fwd":656,"left":594,"right":-1,"lon":82.9098953144341,"lat":55.04800684264482,"zone":4,"sl":4}, +{"id":656,"fwd":657,"left":595,"right":-1,"lon":82.90983073338668,"lat":55.047990724988935,"zone":4,"sl":4}, +{"id":657,"fwd":658,"left":596,"right":-1,"lon":82.90976615239055,"lat":55.04797460729838,"zone":4,"sl":4}, +{"id":658,"fwd":659,"left":597,"right":-1,"lon":82.90970157144564,"lat":55.04795848957308,"zone":4,"sl":4}, +{"id":659,"fwd":660,"left":598,"right":-1,"lon":82.90963699055197,"lat":55.04794237181309,"zone":4,"sl":4}, +{"id":660,"fwd":661,"left":599,"right":-1,"lon":82.90957240970954,"lat":55.047926254018385,"zone":4,"sl":4}, +{"id":661,"fwd":662,"left":600,"right":-1,"lon":82.90950782891835,"lat":55.04791013618898,"zone":4,"sl":4}, +{"id":662,"fwd":663,"left":601,"right":-1,"lon":82.9094432481784,"lat":55.047894018324875,"zone":4,"sl":4}, +{"id":663,"fwd":664,"left":602,"right":-1,"lon":82.9093786674897,"lat":55.047877900426045,"zone":4,"sl":4}, +{"id":664,"fwd":665,"left":603,"right":-1,"lon":82.90931408685223,"lat":55.04786178249252,"zone":4,"sl":4}, +{"id":665,"fwd":666,"left":604,"right":-1,"lon":82.90924950626602,"lat":55.047845664524296,"zone":4,"sl":4}, +{"id":666,"fwd":667,"left":605,"right":-1,"lon":82.90918492573103,"lat":55.047829546521356,"zone":4,"sl":4}, +{"id":667,"fwd":668,"left":606,"right":-1,"lon":82.90912034524729,"lat":55.0478134284837,"zone":4,"sl":4}, +{"id":668,"fwd":681,"left":619,"right":-1,"lon":82.90905576481478,"lat":55.04779731041135,"zone":4,"sl":4}, +{"id":681,"fwd":683,"left":-1,"right":-1,"lon":82.90899118443353,"lat":55.047781192304285,"zone":4,"sl":4}, +{"id":683,"fwd":-1,"left":-1,"right":-1,"lon":82.90896586676119,"lat":55.04777487345355,"zone":2,"sl":4}, +{"id":685,"fwd":686,"left":-1,"right":734,"lon":82.9090748121666,"lat":55.047645323593045,"zone":1,"sl":4}, +{"id":686,"fwd":687,"left":-1,"right":735,"lon":82.90913919216128,"lat":55.04766170355451,"zone":4,"sl":4}, +{"id":687,"fwd":688,"left":-1,"right":736,"lon":82.90920357220789,"lat":55.04767808348146,"zone":4,"sl":4}, +{"id":688,"fwd":689,"left":-1,"right":737,"lon":82.90926795230644,"lat":55.04769446337393,"zone":4,"sl":4}, +{"id":689,"fwd":690,"left":-1,"right":738,"lon":82.90933233245693,"lat":55.04771084323188,"zone":4,"sl":4}, +{"id":690,"fwd":691,"left":-1,"right":739,"lon":82.90939671265932,"lat":55.04772722305535,"zone":4,"sl":4}, +{"id":691,"fwd":692,"left":-1,"right":740,"lon":82.90946109291367,"lat":55.047743602844314,"zone":4,"sl":4}, +{"id":692,"fwd":693,"left":-1,"right":741,"lon":82.90952547321993,"lat":55.047759982598755,"zone":4,"sl":4}, +{"id":693,"fwd":694,"left":-1,"right":742,"lon":82.90958985357813,"lat":55.04777636231872,"zone":4,"sl":4}, +{"id":694,"fwd":695,"left":-1,"right":743,"lon":82.90965423398826,"lat":55.04779274200417,"zone":4,"sl":4}, +{"id":695,"fwd":696,"left":-1,"right":744,"lon":82.90971861445031,"lat":55.04780912165513,"zone":4,"sl":4}, +{"id":696,"fwd":697,"left":-1,"right":745,"lon":82.90978299496429,"lat":55.04782550127157,"zone":4,"sl":4}, +{"id":697,"fwd":698,"left":-1,"right":746,"lon":82.90984737553022,"lat":55.04784188085353,"zone":4,"sl":4}, +{"id":698,"fwd":699,"left":-1,"right":747,"lon":82.90991175614805,"lat":55.04785826040097,"zone":4,"sl":4}, +{"id":699,"fwd":700,"left":-1,"right":748,"lon":82.90997613681782,"lat":55.047874639913914,"zone":4,"sl":4}, +{"id":700,"fwd":701,"left":-1,"right":749,"lon":82.91004051753953,"lat":55.04789101939236,"zone":4,"sl":4}, +{"id":701,"fwd":702,"left":-1,"right":750,"lon":82.91010489831316,"lat":55.0479073988363,"zone":4,"sl":4}, +{"id":702,"fwd":703,"left":-1,"right":751,"lon":82.91016927913871,"lat":55.04792377824575,"zone":4,"sl":4}, +{"id":703,"fwd":704,"left":-1,"right":752,"lon":82.9102336600162,"lat":55.04794015762069,"zone":4,"sl":4}, +{"id":704,"fwd":705,"left":-1,"right":753,"lon":82.91029804094562,"lat":55.047956536961124,"zone":4,"sl":4}, +{"id":705,"fwd":706,"left":-1,"right":754,"lon":82.91036242192696,"lat":55.047972916267064,"zone":4,"sl":4}, +{"id":706,"fwd":707,"left":-1,"right":755,"lon":82.91042680296025,"lat":55.047989295538486,"zone":4,"sl":4}, +{"id":707,"fwd":708,"left":-1,"right":756,"lon":82.91049118404545,"lat":55.04800567477543,"zone":4,"sl":4}, +{"id":708,"fwd":709,"left":-1,"right":757,"lon":82.91055556518258,"lat":55.04802205397785,"zone":4,"sl":4}, +{"id":709,"fwd":710,"left":-1,"right":758,"lon":82.91061994637165,"lat":55.04803843314578,"zone":4,"sl":4}, +{"id":710,"fwd":711,"left":-1,"right":759,"lon":82.91068432761264,"lat":55.04805481227921,"zone":4,"sl":4}, +{"id":711,"fwd":712,"left":-1,"right":760,"lon":82.91074870890556,"lat":55.04807119137812,"zone":4,"sl":4}, +{"id":712,"fwd":713,"left":-1,"right":761,"lon":82.9108130902504,"lat":55.04808757044253,"zone":4,"sl":4}, +{"id":713,"fwd":714,"left":-1,"right":762,"lon":82.9108774716472,"lat":55.048103949472456,"zone":4,"sl":4}, +{"id":714,"fwd":715,"left":-1,"right":763,"lon":82.91094185309589,"lat":55.04812032846787,"zone":4,"sl":4}, +{"id":715,"fwd":716,"left":-1,"right":764,"lon":82.91100623459653,"lat":55.048136707428775,"zone":4,"sl":4}, +{"id":716,"fwd":717,"left":-1,"right":777,"lon":82.91107061614908,"lat":55.04815308635519,"zone":4,"sl":4}, +{"id":717,"fwd":718,"left":-1,"right":-1,"lon":82.91113499775359,"lat":55.04816946524709,"zone":4,"sl":4}, +{"id":718,"fwd":862,"left":-1,"right":-1,"lon":82.91116760695915,"lat":55.048177761118026,"zone":4,"sl":4}, +{"id":733,"fwd":734,"left":686,"right":795,"lon":82.90909700097083,"lat":55.04761657338582,"zone":1,"sl":4}, +{"id":734,"fwd":735,"left":687,"right":796,"lon":82.9091613809279,"lat":55.0476329533359,"zone":4,"sl":4}, +{"id":735,"fwd":736,"left":688,"right":797,"lon":82.9092257609369,"lat":55.04764933325149,"zone":4,"sl":4}, +{"id":736,"fwd":737,"left":689,"right":798,"lon":82.90929014099783,"lat":55.047665713132574,"zone":4,"sl":4}, +{"id":737,"fwd":738,"left":690,"right":799,"lon":82.90935452111067,"lat":55.04768209297915,"zone":4,"sl":4}, +{"id":738,"fwd":739,"left":691,"right":800,"lon":82.90941890127546,"lat":55.04769847279123,"zone":4,"sl":4}, +{"id":739,"fwd":740,"left":692,"right":801,"lon":82.90948328149219,"lat":55.0477148525688,"zone":4,"sl":4}, +{"id":740,"fwd":741,"left":693,"right":802,"lon":82.90954766176081,"lat":55.04773123231188,"zone":4,"sl":4}, +{"id":741,"fwd":742,"left":694,"right":803,"lon":82.90961204208139,"lat":55.04774761202047,"zone":4,"sl":4}, +{"id":742,"fwd":743,"left":695,"right":804,"lon":82.9096764224539,"lat":55.04776399169453,"zone":4,"sl":4}, +{"id":743,"fwd":744,"left":696,"right":805,"lon":82.90974080287833,"lat":55.04778037133411,"zone":4,"sl":4}, +{"id":744,"fwd":745,"left":697,"right":806,"lon":82.9098051833547,"lat":55.047796750939185,"zone":4,"sl":4}, +{"id":745,"fwd":746,"left":698,"right":807,"lon":82.90986956388298,"lat":55.04781313050976,"zone":4,"sl":4}, +{"id":746,"fwd":747,"left":699,"right":808,"lon":82.90993394446319,"lat":55.047829510045815,"zone":4,"sl":4}, +{"id":747,"fwd":748,"left":700,"right":809,"lon":82.90999832509534,"lat":55.0478458895474,"zone":4,"sl":4}, +{"id":748,"fwd":749,"left":701,"right":810,"lon":82.91006270577944,"lat":55.04786226901446,"zone":4,"sl":4}, +{"id":749,"fwd":750,"left":702,"right":811,"lon":82.91012708651544,"lat":55.04787864844701,"zone":4,"sl":4}, +{"id":750,"fwd":751,"left":703,"right":812,"lon":82.91019146730338,"lat":55.04789502784509,"zone":4,"sl":4}, +{"id":751,"fwd":752,"left":704,"right":813,"lon":82.91025584814324,"lat":55.04791140720863,"zone":4,"sl":4}, +{"id":752,"fwd":753,"left":705,"right":814,"lon":82.91032022903502,"lat":55.04792778653771,"zone":4,"sl":4}, +{"id":753,"fwd":754,"left":706,"right":815,"lon":82.91038460997876,"lat":55.047944165832256,"zone":4,"sl":4}, +{"id":754,"fwd":755,"left":707,"right":816,"lon":82.91044899097443,"lat":55.0479605450923,"zone":4,"sl":4}, +{"id":755,"fwd":756,"left":708,"right":817,"lon":82.910513372022,"lat":55.047976924317865,"zone":4,"sl":4}, +{"id":756,"fwd":757,"left":709,"right":818,"lon":82.91057775312152,"lat":55.04799330350891,"zone":4,"sl":4}, +{"id":757,"fwd":758,"left":710,"right":819,"lon":82.91064213427295,"lat":55.04800968266546,"zone":4,"sl":4}, +{"id":758,"fwd":759,"left":711,"right":820,"lon":82.91070651547632,"lat":55.0480260617875,"zone":4,"sl":4}, +{"id":759,"fwd":760,"left":712,"right":821,"lon":82.91077089673162,"lat":55.048042440875044,"zone":4,"sl":4}, +{"id":760,"fwd":761,"left":713,"right":822,"lon":82.91083527803883,"lat":55.04805881992809,"zone":4,"sl":4}, +{"id":761,"fwd":762,"left":714,"right":823,"lon":82.910899659398,"lat":55.04807519894661,"zone":4,"sl":4}, +{"id":762,"fwd":763,"left":715,"right":824,"lon":82.91096404080908,"lat":55.04809157793065,"zone":4,"sl":4}, +{"id":763,"fwd":764,"left":716,"right":825,"lon":82.91102842227208,"lat":55.04810795688019,"zone":4,"sl":4}, +{"id":764,"fwd":777,"left":717,"right":838,"lon":82.911092803787,"lat":55.04812433579522,"zone":4,"sl":4}, +{"id":777,"fwd":779,"left":-1,"right":-1,"lon":82.91115718535391,"lat":55.048140714675746,"zone":4,"sl":4}, +{"id":779,"fwd":872,"left":-1,"right":-1,"lon":82.9111897945404,"lat":55.048149010540925,"zone":4,"sl":4}, +{"id":794,"fwd":795,"left":734,"right":-1,"lon":82.90911918974363,"lat":55.04758782317467,"zone":1,"sl":4}, +{"id":795,"fwd":796,"left":735,"right":-1,"lon":82.90918356966307,"lat":55.04760420311337,"zone":4,"sl":4}, +{"id":796,"fwd":797,"left":736,"right":-1,"lon":82.90924794963445,"lat":55.04762058301759,"zone":4,"sl":4}, +{"id":797,"fwd":798,"left":737,"right":-1,"lon":82.90931232965777,"lat":55.04763696288729,"zone":4,"sl":4}, +{"id":798,"fwd":799,"left":738,"right":-1,"lon":82.909376709733,"lat":55.0476533427225,"zone":4,"sl":4}, +{"id":799,"fwd":800,"left":739,"right":-1,"lon":82.90944108986017,"lat":55.047669722523196,"zone":4,"sl":4}, +{"id":800,"fwd":801,"left":740,"right":-1,"lon":82.90950547003925,"lat":55.04768610228938,"zone":4,"sl":4}, +{"id":801,"fwd":802,"left":741,"right":-1,"lon":82.90956985027027,"lat":55.04770248202109,"zone":4,"sl":4}, +{"id":802,"fwd":803,"left":742,"right":-1,"lon":82.90963423055324,"lat":55.047718861718295,"zone":4,"sl":4}, +{"id":803,"fwd":804,"left":743,"right":-1,"lon":82.9096986108881,"lat":55.04773524138097,"zone":4,"sl":4}, +{"id":804,"fwd":805,"left":744,"right":-1,"lon":82.90976299127493,"lat":55.047751621009176,"zone":4,"sl":4}, +{"id":805,"fwd":806,"left":745,"right":-1,"lon":82.90982737171366,"lat":55.04776800060287,"zone":4,"sl":4}, +{"id":806,"fwd":807,"left":746,"right":-1,"lon":82.90989175220435,"lat":55.047784380162064,"zone":4,"sl":4}, +{"id":807,"fwd":808,"left":747,"right":-1,"lon":82.90995613274693,"lat":55.04780075968676,"zone":4,"sl":4}, +{"id":808,"fwd":809,"left":748,"right":-1,"lon":82.91002051334145,"lat":55.047817139176935,"zone":4,"sl":4}, +{"id":809,"fwd":810,"left":749,"right":-1,"lon":82.91008489398793,"lat":55.047833518632636,"zone":4,"sl":4}, +{"id":810,"fwd":811,"left":750,"right":-1,"lon":82.9101492746863,"lat":55.04784989805382,"zone":4,"sl":4}, +{"id":811,"fwd":812,"left":751,"right":-1,"lon":82.91021365543664,"lat":55.0478662774405,"zone":4,"sl":4}, +{"id":812,"fwd":813,"left":752,"right":-1,"lon":82.91027803623886,"lat":55.04788265679267,"zone":4,"sl":4}, +{"id":813,"fwd":814,"left":753,"right":-1,"lon":82.91034241709305,"lat":55.04789903611035,"zone":4,"sl":4}, +{"id":814,"fwd":815,"left":754,"right":-1,"lon":82.91040679799914,"lat":55.04791541539354,"zone":4,"sl":4}, +{"id":815,"fwd":816,"left":755,"right":-1,"lon":82.91047117895717,"lat":55.047931794642196,"zone":4,"sl":4}, +{"id":816,"fwd":817,"left":756,"right":-1,"lon":82.91053555996712,"lat":55.04794817385636,"zone":4,"sl":4}, +{"id":817,"fwd":818,"left":757,"right":-1,"lon":82.910599941029,"lat":55.047964553036046,"zone":4,"sl":4}, +{"id":818,"fwd":819,"left":758,"right":-1,"lon":82.91066432214282,"lat":55.04798093218122,"zone":4,"sl":4}, +{"id":819,"fwd":820,"left":759,"right":-1,"lon":82.91072870330856,"lat":55.04799731129188,"zone":4,"sl":4}, +{"id":820,"fwd":821,"left":760,"right":-1,"lon":82.91079308452625,"lat":55.048013690368045,"zone":4,"sl":4}, +{"id":821,"fwd":822,"left":761,"right":-1,"lon":82.91085746579584,"lat":55.04803006940969,"zone":4,"sl":4}, +{"id":822,"fwd":823,"left":762,"right":-1,"lon":82.91092184711738,"lat":55.048046448416855,"zone":4,"sl":4}, +{"id":823,"fwd":824,"left":763,"right":-1,"lon":82.91098622849083,"lat":55.04806282738953,"zone":4,"sl":4}, +{"id":824,"fwd":825,"left":764,"right":-1,"lon":82.9110506099162,"lat":55.04807920632767,"zone":4,"sl":4}, +{"id":825,"fwd":838,"left":777,"right":-1,"lon":82.91111499139353,"lat":55.04809558523133,"zone":4,"sl":4}, +{"id":838,"fwd":840,"left":-1,"right":-1,"lon":82.91117937292282,"lat":55.04811196410047,"zone":4,"sl":4}, +{"id":840,"fwd":-1,"left":-1,"right":894,"lon":82.91121198209025,"lat":55.04812025995988,"zone":4,"sl":4}, +{"id":842,"fwd":843,"left":-1,"right":-1,"lon":82.91166068178285,"lat":55.04838728821855,"zone":4,"sl":4}, +{"id":843,"fwd":844,"left":-1,"right":-1,"lon":82.91159200106567,"lat":55.048377819503095,"zone":4,"sl":4}, +{"id":844,"fwd":845,"left":-1,"right":-1,"lon":82.91152332038095,"lat":55.048368350748966,"zone":4,"sl":4}, +{"id":845,"fwd":846,"left":-1,"right":-1,"lon":82.91145463972875,"lat":55.04835888195618,"zone":4,"sl":4}, +{"id":846,"fwd":847,"left":-1,"right":-1,"lon":82.91138595910898,"lat":55.04834941312474,"zone":4,"sl":4}, +{"id":847,"fwd":848,"left":-1,"right":-1,"lon":82.91131727852171,"lat":55.04833994425466,"zone":4,"sl":4}, +{"id":848,"fwd":849,"left":-1,"right":-1,"lon":82.91124859796692,"lat":55.04833047534591,"zone":4,"sl":4}, +{"id":849,"fwd":636,"left":-1,"right":-1,"lon":82.91117991744461,"lat":55.04832100639849,"zone":4,"sl":4}, +{"id":852,"fwd":853,"left":-1,"right":-1,"lon":82.91166893777785,"lat":55.04835624375488,"zone":4,"sl":4}, +{"id":853,"fwd":854,"left":-1,"right":-1,"lon":82.91160023985633,"lat":55.048346816216664,"zone":4,"sl":4}, +{"id":854,"fwd":855,"left":-1,"right":-1,"lon":82.91153154196715,"lat":55.04833738863974,"zone":4,"sl":4}, +{"id":855,"fwd":856,"left":-1,"right":-1,"lon":82.9114628441103,"lat":55.04832796102416,"zone":4,"sl":4}, +{"id":856,"fwd":857,"left":-1,"right":-1,"lon":82.91139414628583,"lat":55.04831853336989,"zone":4,"sl":4}, +{"id":857,"fwd":858,"left":-1,"right":-1,"lon":82.91132544849368,"lat":55.04830910567695,"zone":4,"sl":4}, +{"id":858,"fwd":859,"left":-1,"right":-1,"lon":82.9112567507339,"lat":55.04829967794532,"zone":4,"sl":4}, +{"id":859,"fwd":574,"left":-1,"right":-1,"lon":82.91118805300646,"lat":55.04829025017502,"zone":4,"sl":4}, +{"id":862,"fwd":863,"left":-1,"right":-1,"lon":82.9112375808426,"lat":55.04818331031306,"zone":4,"sl":4}, +{"id":863,"fwd":864,"left":-1,"right":-1,"lon":82.9113075547454,"lat":55.04818885946796,"zone":4,"sl":4}, +{"id":864,"fwd":865,"left":-1,"right":-1,"lon":82.91137752866763,"lat":55.048194408582724,"zone":4,"sl":4}, +{"id":865,"fwd":866,"left":-1,"right":-1,"lon":82.91144750260925,"lat":55.048199957657395,"zone":4,"sl":4}, +{"id":866,"fwd":867,"left":-1,"right":-1,"lon":82.91151747657025,"lat":55.048205506691914,"zone":4,"sl":4}, +{"id":867,"fwd":868,"left":-1,"right":-1,"lon":82.91158745055064,"lat":55.048211055686316,"zone":4,"sl":4}, +{"id":868,"fwd":869,"left":-1,"right":-1,"lon":82.91165742455044,"lat":55.04821660464059,"zone":4,"sl":4}, +{"id":869,"fwd":326,"left":-1,"right":-1,"lon":82.91172739856962,"lat":55.04822215355472,"zone":4,"sl":4}, +{"id":872,"fwd":873,"left":-1,"right":-1,"lon":82.91125980210298,"lat":55.04815441831,"zone":4,"sl":4}, +{"id":873,"fwd":874,"left":-1,"right":-1,"lon":82.91132980968446,"lat":55.04815982603893,"zone":4,"sl":4}, +{"id":874,"fwd":875,"left":-1,"right":-1,"lon":82.91139981728485,"lat":55.0481652337277,"zone":4,"sl":4}, +{"id":875,"fwd":876,"left":-1,"right":-1,"lon":82.91146982490413,"lat":55.048170641376295,"zone":4,"sl":4}, +{"id":876,"fwd":877,"left":-1,"right":-1,"lon":82.91153983254233,"lat":55.04817604898473,"zone":4,"sl":4}, +{"id":877,"fwd":878,"left":-1,"right":-1,"lon":82.91160984019945,"lat":55.04818145655299,"zone":4,"sl":4}, +{"id":878,"fwd":879,"left":-1,"right":-1,"lon":82.91167984787545,"lat":55.04818686408108,"zone":4,"sl":4}, +{"id":879,"fwd":388,"left":-1,"right":-1,"lon":82.91174985557038,"lat":55.04819227156903,"zone":4,"sl":4}, +{"id":894,"fwd":895,"left":-1,"right":-1,"lon":82.91128086179357,"lat":55.048128893492624,"zone":4,"sl":4}, +{"id":895,"fwd":896,"left":-1,"right":-1,"lon":82.91135097700963,"lat":55.04812735731258,"zone":4,"sl":4}, +{"id":896,"fwd":897,"left":-1,"right":-1,"lon":82.9114106109347,"lat":55.04810622713641,"zone":4,"sl":4}, +{"id":897,"fwd":64,"left":-1,"right":-1,"lon":82.91145897481314,"lat":55.04807680021314,"zone":4,"sl":4}, +{"id":920,"fwd":921,"left":-1,"right":-1,"lon":82.91167818350472,"lat":55.04832301255435,"zone":4,"sl":4}, +{"id":921,"fwd":922,"left":-1,"right":-1,"lon":82.91161326276352,"lat":55.04830712553846,"zone":4,"sl":4}, +{"id":922,"fwd":923,"left":-1,"right":-1,"lon":82.91155348381879,"lat":55.04828568290679,"zone":4,"sl":4}, +{"id":923,"fwd":924,"left":-1,"right":-1,"lon":82.91150595030489,"lat":55.04825603971438,"zone":4,"sl":4}, +{"id":924,"fwd":925,"left":-1,"right":-1,"lon":82.91147969176778,"lat":55.04821860872812,"zone":4,"sl":4}, +{"id":925,"fwd":926,"left":-1,"right":-1,"lon":82.91146617904971,"lat":55.048178915346135,"zone":4,"sl":4}, +{"id":926,"fwd":927,"left":-1,"right":-1,"lon":82.91145970518784,"lat":55.04813862675876,"zone":4,"sl":4}, +{"id":927,"fwd":897,"left":-1,"right":-1,"lon":82.91145807087874,"lat":55.048098174496275,"zone":4,"sl":4}, +{"id":940,"fwd":941,"left":-1,"right":-1,"lon":82.91167465165432,"lat":55.04812345374448,"zone":4,"sl":4}, +{"id":941,"fwd":942,"left":-1,"right":-1,"lon":82.91172174304792,"lat":55.04815239799328,"zone":4,"sl":4}, +{"id":942,"fwd":463,"left":-1,"right":-1,"lon":82.91178932979341,"lat":55.04816364453288,"zone":4,"sl":4}, +{"id":967,"fwd":968,"left":-1,"right":-1,"lon":82.9116347473113,"lat":55.04812449991369,"zone":4,"sl":4}, +{"id":968,"fwd":969,"left":-1,"right":-1,"lon":82.91161199773147,"lat":55.04816279902463,"zone":4,"sl":4}, +{"id":969,"fwd":970,"left":-1,"right":-1,"lon":82.91158223234932,"lat":55.04819946792011,"zone":4,"sl":4}, +{"id":970,"fwd":971,"left":-1,"right":-1,"lon":82.91154187323293,"lat":55.04823256829886,"zone":4,"sl":4}, +{"id":971,"fwd":972,"left":-1,"right":-1,"lon":82.91148488437432,"lat":55.04825586186964,"zone":4,"sl":4}, +{"id":972,"fwd":973,"left":-1,"right":-1,"lon":82.91141643846731,"lat":55.048265370890874,"zone":4,"sl":4}, +{"id":973,"fwd":974,"left":-1,"right":-1,"lon":82.91134593531535,"lat":55.04826733455973,"zone":4,"sl":4}, +{"id":974,"fwd":975,"left":-1,"right":-1,"lon":82.91127543417808,"lat":55.04826499783473,"zone":4,"sl":4}, +{"id":975,"fwd":525,"left":-1,"right":-1,"lon":82.91120542059247,"lat":55.04825967282137,"zone":4,"sl":4} +] diff --git a/src/agents/vehicle.rs b/src/agents/vehicle.rs index 58a9732..ce36775 100644 --- a/src/agents/vehicle.rs +++ b/src/agents/vehicle.rs @@ -90,6 +90,8 @@ pub struct Vehicle { pub min_safe_distance: i32, /// Final cell for the vehicle's trip pub destination: CellID, + /// Final trip destination (immutable, used for completion check; destination may change for transit cells) + pub trip_destination: CellID, /// A boolean indicating if the vehicle is a confclict participant pub is_conflict_participant: bool, @@ -162,6 +164,7 @@ impl Vehicle { bearing: 0.0, min_safe_distance: 0, destination: -1, + trip_destination: -1, is_conflict_participant: false, trip: -1, transits_made: 0, @@ -672,6 +675,7 @@ impl VehicleBuilder { /// ``` pub fn with_destination(mut self, cell_id: CellID) -> Self { self.vehicle.destination = cell_id; + self.vehicle.trip_destination = cell_id; self } diff --git a/src/intentions/intention.rs b/src/intentions/intention.rs index 156ea13..3dec2e6 100644 --- a/src/intentions/intention.rs +++ b/src/intentions/intention.rs @@ -3,7 +3,7 @@ use crate::behaviour::BehaviourType; use crate::agents::{ TailIntentionManeuver, Vehicle, VehicleError, VehicleID, VehicleIntention, }; -use crate::grid::cell::CellState; +use crate::grid::cell::{Cell, CellState}; use crate::maneuver::LaneChangeType; use crate::grid::{cell::CellID, road_network::GridRoads}; use crate::intentions::{intention_type::IntentionType, Intentions}; @@ -238,6 +238,27 @@ pub fn find_intention<'a>( return Ok(result); } + // Шf stopped at red traffic light do early return + let forward_cell_id = source_cell.get_forward_id(); + if forward_cell_id > 0 { + if let Some(forward_cell) = net.get_cell(&forward_cell_id) { + if forward_cell.get_state() == CellState::Banned { + let result = VehicleIntention { + intention_maneuver: LaneChangeType::Block, + intention_speed: 0, + destination: None, + confusion: None, + intention_cell_id: vehicle.cell_id, + tail_intention_cells: vec![], + intermediate_cells: Vec::with_capacity(0), + tail_maneuver: tail_maneuver, + should_stop: false, + }; + return Ok(result); + } + } + } + // Vehicle's speed should not be greater than speed limit let mut intention_speed = vehicle.speed.min(speed_limit); @@ -263,14 +284,14 @@ pub fn find_intention<'a>( } // Considering that vehicle always wants to accelerate: - let observe_distance = speed_possible + vehicle.min_safe_distance; + let _observe_distance = speed_possible + vehicle.min_safe_distance; // Check if maneuvers are allowed (they could be prohibeted due the vehicle's tail is not done previous maneuver yet) let maneuvers_allowed = vehicle.timer_non_maneuvers <= 0 && tail_maneuver.intention_maneuver != LaneChangeType::ChangeRight && tail_maneuver.intention_maneuver != LaneChangeType::ChangeLeft; - let mut destination: Option = None; + let destination: Option = None; let mut confusion: Option = None; // println!( @@ -293,7 +314,8 @@ pub fn find_intention<'a>( source_cell, net, maneuvers_allowed, - observe_distance + 1, + 1000, + // observe_distance + 1, // depth-limited ) { Ok(path) => path, Err(e) => { @@ -304,6 +326,21 @@ pub fn find_intention<'a>( } } }, + // Reachability over directed edges is monotone: once the destination is + // unreachable from the current cell, it is unreachable from every cell the + // vehicle can ever get to. Full A* would just fail again (the most expensive + // failure mode - it exhausts the whole reachable component), so skip routing + // for confused vehicles entirely. NOTE: this holds only while the grid is + // static during a session and the destination is not reassigned. + _ if vehicle.confusion => { + let new_path = match process_no_route_found(source_cell, net) { + Ok(path) => path, + Err(e) => return Err(IntentionError::NoPathForNoRoute(e)), + }; + intention_speed = 1; + speed_possible = intention_speed; + new_path + }, _ => { let target_cell = net .get_cell(&vehicle.destination) @@ -313,7 +350,8 @@ pub fn find_intention<'a>( target_cell, net, maneuvers_allowed, - Some(observe_distance + 1), + None, + // Some(observe_distance + 1), // depth-limited ) { Ok(path) => path, Err(e) @@ -329,7 +367,7 @@ pub fn find_intention<'a>( Ok(path) => path, Err(e) => return Err(IntentionError::NoPathForNoRoute(e)), }; - destination = Some(new_path.vertices()[new_path.vertices().len() - 1].get_id()); + // Do NOT overwrite destination - keep original trip destination intention_speed = 1; speed_possible = intention_speed; confusion = Some(true); @@ -431,6 +469,70 @@ const UNDEFINED_MANEUVER: LaneChangeType = LaneChangeType::ChangeRight; /// Attempts to find an alternate maneuver (lane change) for a blocked vehicle. /// +/// Helper: Creates a block intention +fn create_block_intention(cell_id: CellID, should_stop: bool) -> VehicleIntention { + VehicleIntention { + intention_maneuver: LaneChangeType::Block, + intention_speed: 0, + destination: None, + confusion: None, + intention_cell_id: cell_id, + tail_intention_cells: vec![], + intermediate_cells: Vec::with_capacity(0), + tail_maneuver: TailIntentionManeuver::default(), + should_stop, + } +} + +/// Helper: Checks if alternate path (left or right) is available and calculates cost +fn check_alternate_direction( + cell_id: CellID, + source_cell: &Cell, + target_cell: &Cell, + net: &GridRoads, + current_state: &HashMap, + direction: &str, + max_depth: Option, +) -> Result<(CellID, f64), IntentionError> { + if cell_id <= 0 { + return Ok((-1, INFINITY)); + } + + let cell = net.get_cell(&cell_id).ok_or_else(|| { + if direction == "left" { + IntentionError::NoLeftCell(source_cell.get_id()) + } else { + IntentionError::NoRightCell(source_cell.get_id()) + } + })?; + + let is_blocked = current_state + .get(&cell_id) + .map(|&id| id > 0) + .unwrap_or(false); + + if is_blocked || cell.get_state() != CellState::Free { + return Ok((-1, INFINITY)); + } + + match shortest_path(cell, target_cell, net, true, max_depth) { + Ok(path) => { + let cost = path.cost() + source_cell.distance_to(cell); + Ok((cell_id, cost)) + } + Err(shortest_path::router::AStarError::NoPathFound { .. }) => { + Ok((-1, INFINITY)) + } + Err(_) => { + if direction == "left" { + Err(IntentionError::LeftPathFind(cell_id)) + } else { + Err(IntentionError::RightPathFind(cell_id)) + } + } + } +} + /// If the vehicle cannot move forward, tries left or right lane changes /// and selects the best available option. /// @@ -447,18 +549,7 @@ pub fn find_alternate_intention<'a>( // If maneuvers are not allowed (tail still completing previous maneuver), block immediately if !maneuvers_allowed { - let result = VehicleIntention { - intention_maneuver: LaneChangeType::Block, - intention_speed: 0, - destination: None, - confusion: None, - intention_cell_id: source_cell_id, - tail_intention_cells: vec![], - intermediate_cells: Vec::with_capacity(0), - tail_maneuver: TailIntentionManeuver::default(), - should_stop: false, - }; - return Ok(result); + return Ok(create_block_intention(source_cell_id, false)); } let source_cell = net @@ -469,103 +560,93 @@ pub fn find_alternate_intention<'a>( .get_cell(&target_cell_id) .ok_or(IntentionError::NoTargetCell(target_cell_id))?; - let mut min_left_dist = INFINITY; - let mut min_right_dist = INFINITY; - - // Check left maneuver - let mut left_cell_id = source_cell.get_left_id(); - if left_cell_id > 0 { - let left_cell = net - .get_cell(&left_cell_id) - .ok_or(IntentionError::NoLeftCell(vehicle.cell_id))?; - - // Check if possible maneuver can't be made - let is_blocked = current_state - .get(&left_cell_id) - .map(|&id| id > 0) - .unwrap_or(false); - - if !is_blocked && left_cell.get_state() == CellState::Free { - match shortest_path(left_cell, target_cell, net, true, Some(vehicle.speed)) { - Ok(path) => { - let cost = path.cost(); - min_left_dist = cost + source_cell.distance_to(left_cell); - } - Err(e) - if e != shortest_path::router::AStarError::NoPathFound { - start_id: left_cell_id, - end_id: target_cell_id, - } => - { - return Err(IntentionError::LeftPathFind(left_cell_id)) - } - Err(_) => { - min_left_dist = INFINITY; - } - } - } else { - left_cell_id = -1; - } - } - - // Check right maneuver - let mut right_cell_id = source_cell.get_right_id(); - if right_cell_id > 0 { - let right_cell = net - .get_cell(&right_cell_id) - .ok_or(IntentionError::NoRightCell(vehicle.cell_id))?; + // Check left and right alternate paths (no depth limit to see full route). + // A confused vehicle's destination is already proven unreachable, and + // reachability is monotone along directed edges - both probes would run a + // full failed A* just to return INFINITY, so skip them. + let (left_cell_id, min_left_dist) = if vehicle.confusion { + (-1, INFINITY) + } else { + check_alternate_direction( + source_cell.get_left_id(), + source_cell, + target_cell, + net, + current_state, + "left", + None, + // Some(vehicle.speed), // depth-limited + )? + }; - let is_blocked = current_state - .get(&right_cell_id) - .map(|&id| id > 0) - .unwrap_or(false); + let (right_cell_id, min_right_dist) = if vehicle.confusion { + (-1, INFINITY) + } else { + check_alternate_direction( + source_cell.get_right_id(), + source_cell, + target_cell, + net, + current_state, + "right", + None, + // Some(vehicle.speed), // depth-limited + )? + }; - if !is_blocked && right_cell.get_state() == CellState::Free { - match shortest_path(right_cell, target_cell, net, true, Some(vehicle.speed)) { - Ok(path) => { - let cost = path.cost(); - min_right_dist = cost + source_cell.distance_to(right_cell); - } - Err(e) - if e != shortest_path::router::AStarError::NoPathFound { - start_id: right_cell_id, - end_id: target_cell_id, - } => + // If both paths are impossible (infinite distance), don't attempt a lane change. + // Before blocking, try to keep rolling forward: a driver stuck next to a jammed + // lane drives along it and merges at a gap further ahead. If the destination + // becomes unreachable ahead, the regular per-tick A* will return NoPathFound on + // the next tick and the confusion fallback takes over (the vehicle may end up + // counted as lost - that is an accepted risk). + if min_left_dist == INFINITY && min_right_dist == INFINITY { + let forward_cell_id = source_cell.get_forward_id(); + if forward_cell_id > 0 { + if let Some(forward_cell) = net.get_cell(&forward_cell_id) { + let is_occupied = current_state + .get(&forward_cell_id) + .map(|&id| id > 0) + .unwrap_or(false); + if !is_occupied + && forward_cell.get_state() == CellState::Free + && forward_cell.get_speed_limit() > 0 { - return Err(IntentionError::RightPathFind(right_cell_id)) - } - Err(_) => { - min_right_dist = INFINITY; + return Ok(VehicleIntention { + intention_maneuver: LaneChangeType::NoChange, + intention_speed: 1, + destination: None, + confusion: None, + intention_cell_id: forward_cell_id, + tail_intention_cells: vec![], + intermediate_cells: Vec::with_capacity(0), + tail_maneuver: TailIntentionManeuver::default(), + should_stop: false, + }); } } - } else { - right_cell_id = -1; } + return Ok(create_block_intention(source_cell_id, true)); } - // Choose best maneuver - let mut min_cell: CellID; - let mut intention_maneuver: LaneChangeType; - if UNDEFINED_MANEUVER == LaneChangeType::ChangeRight { - min_cell = right_cell_id; - intention_maneuver = LaneChangeType::ChangeRight; - if min_left_dist < min_right_dist { - min_cell = left_cell_id; - intention_maneuver = LaneChangeType::ChangeLeft; - } + // Choose best maneuver based on distance comparison + let (min_cell, intention_maneuver) = if min_left_dist < min_right_dist { + (left_cell_id, LaneChangeType::ChangeLeft) + } else if min_right_dist < min_left_dist { + (right_cell_id, LaneChangeType::ChangeRight) } else { - min_cell = left_cell_id; - intention_maneuver = LaneChangeType::ChangeLeft; - if min_right_dist < min_left_dist { - min_cell = right_cell_id; - intention_maneuver = LaneChangeType::ChangeRight; + // Equal distances - use UNDEFINED_MANEUVER as tiebreaker + if UNDEFINED_MANEUVER == LaneChangeType::ChangeRight { + (right_cell_id, LaneChangeType::ChangeRight) + } else { + (left_cell_id, LaneChangeType::ChangeLeft) } - } + }; - // Apply the chosen maneuver + // Apply the chosen maneuver if valid if min_cell > 0 { - let result = VehicleIntention { - intention_maneuver: intention_maneuver, + return Ok(VehicleIntention { + intention_maneuver, intention_speed: 1, destination: None, confusion: None, @@ -574,22 +655,9 @@ pub fn find_alternate_intention<'a>( intermediate_cells: Vec::with_capacity(0), tail_maneuver: TailIntentionManeuver::default(), should_stop: true, - }; - return Ok(result); + }) } - let result = VehicleIntention { - intention_maneuver: LaneChangeType::Block, - intention_speed: 0, - destination: None, - confusion: None, - intention_cell_id: source_cell_id, - tail_intention_cells: vec![], - intermediate_cells: Vec::with_capacity(0), - tail_maneuver: TailIntentionManeuver::default(), - should_stop: false, - }; - - Ok(result) + Ok(create_block_intention(source_cell_id, false)) } #[cfg(test)] diff --git a/src/movement/movement.rs b/src/movement/movement.rs index 106670f..6a7c8c8 100644 --- a/src/movement/movement.rs +++ b/src/movement/movement.rs @@ -142,7 +142,7 @@ pub fn movement( net: &GridRoads, vehicles: &mut IndexMap, verbose: &LocalLogger, -) -> Result<(), MovementError> { +) -> Result<(i32, i32), MovementError> { if verbose.is_at_least(VerboseLevel::Main) { verbose.log_with_fields( EVENT_MOVEMENT, @@ -153,6 +153,8 @@ pub fn movement( // Collect vehicles to remove (to avoid borrowing issues during iteration) let mut vehicles_to_remove = Vec::new(); + let mut vehicles_completed = 0i32; + let mut vehicles_lost = 0i32; for (vehicle_id, vehicle) in vehicles.iter_mut() { @@ -173,23 +175,50 @@ pub fn movement( vehicle.apply_intention(); vehicle.is_conflict_participant = false; - // Update bearing only when next cell is different from current + // Update bearing depending on intention maneuver if vehicle.cell_id != vehicle.intention.intention_cell_id { + // vehicle is moving? then set bearing based on actual movement let cell_from = net.get_cell(&vehicle.cell_id) - .ok_or(MovementError::CellNotFound { - cell_id: vehicle.cell_id, - vehicle_id: vehicle.id + .ok_or(MovementError::CellNotFound { + cell_id: vehicle.cell_id, + vehicle_id: vehicle.id })?; let cell_to = net.get_cell(&vehicle.intention.intention_cell_id) - .ok_or(MovementError::CellNotFound { - cell_id: vehicle.intention.intention_cell_id, - vehicle_id: vehicle.id + .ok_or(MovementError::CellNotFound { + cell_id: vehicle.intention.intention_cell_id, + vehicle_id: vehicle.id })?; let pt_from = cell_from.get_point(); let pt_to = cell_to.get_point(); vehicle.bearing = get_bearing(pt_from, pt_to); + } else { + // vehicle is stopped or blocked? bearing = angle to forward direction + use crate::maneuver::LaneChangeType; + match vehicle.intention.intention_maneuver { + LaneChangeType::Block | LaneChangeType::NoChange => { + let current_cell = net.get_cell(&vehicle.cell_id) + .ok_or(MovementError::CellNotFound { + cell_id: vehicle.cell_id, + vehicle_id: vehicle.id + })?; + let forward_id = current_cell.get_forward_id(); + if forward_id > 0 { + if let Some(forward_cell) = net.get_cell(&forward_id) { + let pt_from = current_cell.get_point(); + let pt_to = forward_cell.get_point(); + vehicle.bearing = get_bearing(pt_from, pt_to); + } + } + } + _ => { + // keep bearing as is, dunno if it's good + } + } + } + // Decrement timers only if vehicle moved + if vehicle.cell_id != vehicle.intention.intention_cell_id { // Decrement timers if vehicle.timer_non_acceleration > 0 { vehicle.timer_non_acceleration -= 1; @@ -257,6 +286,9 @@ pub fn movement( let transits_made = vehicle.get_transits_made(); if (transits_made as usize) < vehicle.transit_cells.len() && vehicle.get_relax_countdown() == 0 { vehicle.destination = vehicle.transit_cells[transits_made as usize]; + // Confusion means "current destination proven unreachable"; the verdict + // does not transfer to a newly assigned destination + vehicle.confusion = false; vehicle.relax_countdown_reset(); } } @@ -264,27 +296,28 @@ pub fn movement( vehicle.travel_time += 1; // Check for vehicle removal conditions - if zone_type == ZoneType::Death && vehicle.cell_id != vehicle.destination { - // Vehicle has reached the death zone + if vehicle.cell_id == vehicle.trip_destination { + // Vehicle has reached the destination (priority check) if verbose.is_at_least(VerboseLevel::Main) { verbose.log_with_fields( - EVENT_MOVEMENT_DEAD_END, - "Vehicle done movement due going to dead-end", + EVENT_MOVEMENT_DESTINATION, + "Vehicle done movement due reaching destination", &[("vehicle_id", &vehicle.id)] ); } vehicles_to_remove.push(*vehicle_id); - } - if vehicle.cell_id == vehicle.destination { - // Vehicle has reached the destination + vehicles_completed += 1; + } else if zone_type == ZoneType::Death { + // Vehicle has reached the death zone without reaching destination (lost) if verbose.is_at_least(VerboseLevel::Main) { verbose.log_with_fields( - EVENT_MOVEMENT_DESTINATION, - "Vehicle done movement due reaching destination", + EVENT_MOVEMENT_DEAD_END, + "Vehicle done movement due going to dead-end", &[("vehicle_id", &vehicle.id)] ); } vehicles_to_remove.push(*vehicle_id); + vehicles_lost += 1; } } @@ -295,5 +328,5 @@ pub fn movement( vehicles.swap_remove(&vehicle_id); } - Ok(()) + Ok((vehicles_completed, vehicles_lost)) } \ No newline at end of file diff --git a/src/simulation/session.rs b/src/simulation/session.rs index 84cde8d..e9cf519 100644 --- a/src/simulation/session.rs +++ b/src/simulation/session.rs @@ -140,6 +140,12 @@ pub struct Session { /// Defines the SRID of the world world_srid: SRID, + + /// Cumulative count of vehicles that reached their destination + vehicles_completed: i32, + + /// Cumulative count of vehicles that were lost (reached death zone without reaching destination) + vehicles_lost: i32, } impl Session { @@ -166,6 +172,8 @@ impl Session { _expire_at: 0, steps: 0, world_srid: picked_srid, + vehicles_completed: 0, + vehicles_lost: 0, } } @@ -193,6 +201,8 @@ impl Session { _expire_at: 0, steps: 0, world_srid: picked_srid, + vehicles_completed: 0, + vehicles_lost: 0, } } @@ -555,7 +565,9 @@ impl Session { // 7. Move vehicles let vehicles_grid = self.grids_storage.get_vehicles_net_ref(); - movement(vehicles_grid, &mut self.vehicles, &self.verbose)?; + let (completed, lost) = movement(vehicles_grid, &mut self.vehicles, &self.verbose)?; + self.vehicles_completed += completed; + self.vehicles_lost += lost; // 8. Collect current vehicles positions for state dump let mut states_dump: Vec = Vec::with_capacity(self.vehicles.len()); @@ -591,10 +603,26 @@ impl Session { let timestamp = self.steps; self.steps += 1; + // Log vehicle completion statistics + if self.verbose.is_at_least(VerboseLevel::Main) { + self.verbose.log_with_fields( + EVENT_STEP_COMPLETE, + "Step completed with vehicle statistics", + &[ + ("timestamp", ×tamp), + ("active_vehicles", &self.vehicles.len()), + ("vehicles_completed", &self.vehicles_completed), + ("vehicles_lost", &self.vehicles_lost), + ] + ); + } + Ok(AutomataState { timestamp: timestamp, vehicles: states_dump, tls: tl_states_dump, + vehicles_completed: self.vehicles_completed, + vehicles_lost: self.vehicles_lost, }) } diff --git a/src/simulation/states.rs b/src/simulation/states.rs index 90431f1..24d6f04 100644 --- a/src/simulation/states.rs +++ b/src/simulation/states.rs @@ -24,6 +24,10 @@ pub struct AutomataState { pub vehicles: Vec, /// States of all traffic light groups at this timestamp pub tls: HashMap>, + /// Cumulative count of vehicles that reached their destination + pub vehicles_completed: i32, + /// Cumulative count of vehicles that were lost (reached death zone without reaching destination) + pub vehicles_lost: i32, } /// State of a single vehicle at a specific timestamp diff --git a/src/verbose/verbose.rs b/src/verbose/verbose.rs index 7d65457..4f52040 100644 --- a/src/verbose/verbose.rs +++ b/src/verbose/verbose.rs @@ -109,6 +109,7 @@ pub const EVENT_MOVEMENT_VEHICLE: &str = "movement_vehicle"; pub const EVENT_MOVEMENT_DEAD_END: &str = "movement_dead_end"; pub const EVENT_MOVEMENT_DESTINATION: &str = "movement_destination"; pub const EVENT_ROUTING_STATS: &str = "routing_stats"; +pub const EVENT_STEP_COMPLETE: &str = "step_complete"; pub const EVENT_SESSION_CREATE: &str = "session_create"; pub const EVENT_SESSION_EXPIRED: &str = "session_expired"; pub const EVENT_SESSION_EXTRACT_STATES: &str = "session_extract";