1use crate::loaders::vnas_crc::CrcVideoMapRef;
2use crate::loaders::{
3 ese::FreeTextGroup,
4 euroscope::{
5 line::{ColouredLine, LineGroup},
6 sector::{LabelGroup, RegionGroup},
7 },
8};
9use anyhow::{anyhow, bail, Context};
10use geojson::{Feature, FeatureCollection, GeoJson, Geometry, Value};
11use serde::{Deserialize, Serialize};
12use serde_json::Map;
13use std::collections::HashMap;
14use std::fs::File;
15use std::io::BufReader;
16use std::path::Path;
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub enum AtcMapData {
20 Embedded {features: FeatureCollection},
21 ExternalFile {filename: String}
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct AtcMap {
26 pub name: String,
27 pub data: AtcMapData
28}
29
30impl AtcMap {
31 pub fn try_from_es_line_group(sector_file_id: String, item_type: String, value: LineGroup<ColouredLine>) -> anyhow::Result<Self> {
32 let name = format!("{}_{}_{}", sector_file_id, item_type, value.name);
33 let mut features = Vec::with_capacity(value.lines.len());
34 for line in value.lines {
35 let mut props_map = Map::new();
37 props_map.insert("itemType".to_string(), serde_json::to_value(&item_type)?);
38 if let Some(line_color) = line.colour {
39 props_map.insert(
40 "color".to_string(),
41 serde_json::to_value(format!("#{:02X}{:02X}{:02X}", line_color.r, line_color.g, line_color.b))?,
42 );
43 }
44
45 features.push(Feature {
46 id: None,
47 bbox: None,
48 foreign_members: None,
49 geometry: Some(Geometry::new(Value::LineString(vec![
50 vec![line.line.start.lon, line.line.start.lat],
51 vec![line.line.end.lon, line.line.end.lat],
52 ]))),
53 properties: Some(props_map),
54 });
55 }
56
57 Ok(AtcMap {
58 name: name,
59 data: AtcMapData::Embedded {
60 features: FeatureCollection {
61 bbox: None,
62 features: features,
63 foreign_members: None,
64 }
65 }
66 })
67 }
68
69 pub fn try_from_es_region_group(sector_file_id: String, item_type: String, value: RegionGroup) -> anyhow::Result<Self> {
70 let name = format!("{}_{}_{}", sector_file_id, item_type, value.name);
71 let mut features = Vec::with_capacity(value.regions.capacity());
72 for region in value.regions {
73 let mut props_map = Map::new();
75 props_map.insert("itemType".to_string(), serde_json::to_value(&item_type)?);
76 props_map.insert(
77 "color".to_string(),
78 serde_json::to_value(format!("#{:02X}{:02X}{:02X}", region.colour.r, region.colour.g, region.colour.b))?,
79 );
80
81 let mut points = region.vertices.iter().map(|vert| vec![vert.lon, vert.lat]).collect::<Vec<Vec<f64>>>();
82 if let Some(start_pt) = points.get(0) {
83 points.push(start_pt.clone());
84 }
85
86 features.push(Feature {
87 id: None,
88 bbox: None,
89 foreign_members: None,
90 geometry: Some(Geometry::new(Value::Polygon(vec![points]))),
91 properties: Some(props_map),
92 });
93 }
94
95 Ok(AtcMap {
96 name: name,
97 data: AtcMapData::Embedded {
98 features: FeatureCollection {
99 bbox: None,
100 features: features,
101 foreign_members: None,
102 }
103 }
104 })
105 }
106
107 pub fn try_from_es_labels_group(sector_file_id: String, item_type: String, value: LabelGroup) -> anyhow::Result<Self> {
108 let name = format!("{}_{}_{}", sector_file_id, item_type, value.name);
109 let mut features = Vec::with_capacity(value.labels.capacity());
110 for label in value.labels {
111 let mut props_map = Map::new();
113 props_map.insert("itemType".to_string(), serde_json::to_value(&item_type)?);
114 props_map.insert(
115 "textColor".to_string(),
116 serde_json::to_value(format!("#{:02X}{:02X}{:02X}", label.colour.r, label.colour.g, label.colour.b))?,
117 );
118 props_map.insert("text".to_string(), serde_json::to_value(label.name.to_string())?);
119 props_map.insert("showText".to_string(), serde_json::to_value(true)?);
120
121 features.push(Feature {
122 id: None,
123 bbox: None,
124 foreign_members: None,
125 geometry: Some(Geometry::new(Value::Point(vec![label.position.lon, label.position.lat]))),
126 properties: Some(props_map),
127 });
128 }
129
130 Ok(AtcMap {
131 name: name,
132 data: AtcMapData::Embedded {
133 features: FeatureCollection {
134 bbox: None,
135 features: features,
136 foreign_members: None,
137 }
138 }
139 })
140 }
141
142 pub fn try_from_es_freetext_group(sector_file_id: String, item_type: String, value: FreeTextGroup) -> anyhow::Result<Self> {
143 let name = format!("{}_{}_{}", sector_file_id, item_type, value.name);
144 let mut features = Vec::with_capacity(value.entries.capacity());
145 for label in value.entries {
146 let mut props_map = Map::new();
148 props_map.insert("itemType".to_string(), serde_json::to_value(&item_type)?);
149 props_map.insert("text".to_string(), serde_json::to_value(label.text.to_string())?);
150 props_map.insert("showText".to_string(), serde_json::to_value(true)?);
151
152 features.push(Feature {
153 id: None,
154 bbox: None,
155 foreign_members: None,
156 geometry: Some(Geometry::new(Value::Point(vec![label.position.lon, label.position.lat]))),
157 properties: Some(props_map),
158 });
159 }
160
161 Ok(AtcMap {
162 name: name,
163 data: AtcMapData::Embedded {
164 features: FeatureCollection {
165 bbox: None,
166 features: features,
167 foreign_members: None,
168 }
169 }
170 })
171 }
172
173 pub fn try_from_crc_video_map(map_ref: &CrcVideoMapRef, facility_file_path: impl AsRef<Path>, facility_name: String) -> anyhow::Result<AtcMap> {
174 let video_map_path = facility_file_path
176 .as_ref()
177 .join("..")
178 .join("..")
179 .join("VideoMaps")
180 .join(facility_name.to_string())
181 .join(format!("{}.geojson", map_ref.id))
182 .canonicalize()?;
183
184 let geojson = GeoJson::from_reader(BufReader::new(File::open(&video_map_path).context(format!(
185 "Couldn't open video map at path {}",
186 &video_map_path.to_str().unwrap_or_default().to_string()
187 ))?))
188 .context("Couldn't parse GeoJSON")?;
189
190 if let GeoJson::FeatureCollection(mut features) = geojson {
191 let mut new_features = Vec::new();
192 let mut default_line_style = "solid".to_string();
193 let mut default_line_thickness = 1;
194 let mut default_text_opaque = false;
195 let mut default_text_size = 1;
196 let mut default_text_underline = false;
197 let mut default_text_offset = (0, 0);
198 let mut default_symbol_style = "".to_string();
199 let mut default_symbol_size = 1;
200
201 for feature in &features {
202 let properties = feature.properties.clone().unwrap_or_default();
203 if properties
205 .get(&"isLineDefaults".to_string())
206 .unwrap_or(&serde_json::Value::Bool(false))
207 .as_bool()
208 .unwrap_or(false)
209 {
210 if let Some(value) = properties.get(&"style".to_string()) {
211 default_line_style = value.as_str().unwrap_or_default().to_lowercase();
212 }
213 if let Some(value) = properties.get(&"thickness".to_string()) {
214 default_line_thickness = value.as_i64().unwrap_or(1) as i32;
215 }
216 } else if properties
217 .get(&"isTextDefaults".to_string())
218 .unwrap_or(&serde_json::Value::Bool(false))
219 .as_bool()
220 .unwrap_or(false)
221 {
222 if let Some(value) = properties.get(&"size".to_string()) {
223 default_text_size = value.as_i64().unwrap_or(1) as i32;
224 }
225 if let Some(value) = properties.get(&"xOffset".to_string()) {
226 default_text_offset.0 = value.as_i64().unwrap_or(0) as i32;
227 }
228 if let Some(value) = properties.get(&"yOffset".to_string()) {
229 default_text_offset.1 = value.as_i64().unwrap_or(0) as i32;
230 }
231 if let Some(value) = properties.get(&"opaque".to_string()) {
232 default_text_opaque = value.as_bool().unwrap_or(false);
233 }
234 if let Some(value) = properties.get(&"underline".to_string()) {
235 default_text_underline = value.as_bool().unwrap_or(false);
236 }
237 } else if properties
238 .get(&"isSymbolDefaults".to_string())
239 .unwrap_or(&serde_json::Value::Bool(false))
240 .as_bool()
241 .unwrap_or(false)
242 {
243 if let Some(value) = properties.get(&"style".to_string()) {
244 default_symbol_style = value.as_str().unwrap_or_default().to_lowercase();
245 }
246 if let Some(value) = properties.get(&"size".to_string()) {
247 default_symbol_size = value.as_i64().unwrap_or(1) as i32;
248 }
249 } else if let Some(geometry) = &feature.geometry {
250 let mut new_props = serde_json::Map::new();
251
252 if let Some(color) = properties.get(&"color".to_string()) {
254 new_props.insert("color".to_string(), color.clone());
255 }
256
257 if let Some(z_index) = properties.get(&"zIndex".to_string()) {
258 new_props.insert("zIndex".to_string(), z_index.clone());
259 }
260
261 match geometry.value.type_name() {
262 "Point" => {
263 if let Some(text) = properties.get(&"text".to_string()) {
265 new_props.insert(
266 "size".to_string(),
267 properties
268 .get(&"size".to_string())
269 .cloned()
270 .unwrap_or(serde_json::to_value(default_text_size)?),
271 );
272 new_props.insert(
273 "opaque".to_string(),
274 properties
275 .get(&"opaque".to_string())
276 .cloned()
277 .unwrap_or(serde_json::to_value(default_text_opaque)?),
278 );
279 new_props.insert(
280 "underline".to_string(),
281 properties
282 .get(&"underline".to_string())
283 .cloned()
284 .unwrap_or(serde_json::to_value(default_text_underline)?),
285 );
286 new_props.insert(
287 "size".to_string(),
288 properties
289 .get(&"size".to_string())
290 .cloned()
291 .unwrap_or(serde_json::to_value(default_text_size)?),
292 );
293 let mut text_str = "".to_string();
294 for line in (&text).as_array().unwrap_or(&Vec::new()) {
295 if text_str == "" {
296 text_str = line.as_str().unwrap_or_default().to_string();
297 } else {
298 text_str = format!("{}\n{}", &text_str, line.as_str().unwrap_or_default());
299 }
300 }
301 new_props.insert("text".to_string(), serde_json::to_value(&text_str)?);
302 new_props.insert("showText".to_string(), serde_json::to_value(true)?);
303 } else {
304 new_props.insert(
305 "style".to_string(),
306 properties
307 .get(&"style".to_string())
308 .cloned()
309 .unwrap_or(serde_json::to_value(default_symbol_style.to_string())?),
310 );
311 new_props.insert(
312 "size".to_string(),
313 properties
314 .get(&"size".to_string())
315 .cloned()
316 .unwrap_or(serde_json::to_value(default_symbol_size)?),
317 );
318 new_props.insert("showSymbol".to_string(), serde_json::to_value(true)?);
319 }
320 }
321 "LineString" => {
322 new_props.insert(
323 "style".to_string(),
324 properties
325 .get(&"style".to_string())
326 .cloned()
327 .unwrap_or(serde_json::to_value(default_line_style.to_string())?),
328 );
329
330 new_props.insert(
331 "thickness".to_string(),
332 properties
333 .get(&"thickness".to_string())
334 .cloned()
335 .unwrap_or(serde_json::to_value(&default_line_thickness)?),
336 );
337 }
338 &_ => {}
339 };
340
341 if let Some(asdex_item_type) = properties.get(&"asdex".to_string()) {
342 new_props.insert("itemType".to_string(), asdex_item_type.clone());
343 new_props.remove(&"color".to_string());
344 } else {
345 new_props.insert(
346 "itemType".to_string(),
347 serde_json::to_value(format!("stars-{}", &map_ref.stars_brightness_category))?,
348 );
349 }
350
351 new_features.push(Feature {
352 bbox: feature.bbox.clone(),
353 geometry: feature.geometry.clone(),
354 id: feature.id.clone(),
355 properties: Some(new_props),
356 foreign_members: feature.foreign_members.clone(),
357 });
358 }
359 }
360
361 return Ok(AtcMap {
362 name: map_ref.name.to_string(),
363 data: AtcMapData::Embedded {
364 features: FeatureCollection {
365 bbox: None,
366 features: new_features,
367 foreign_members: None,
368 }
369 }
370 });
371 }
372
373 Err(anyhow!("No Features found in GeoJSON!"))
374 }
375}