1use serde_json::{Map, Value, json};
7
8use crate::crs::*;
9
10impl Crs {
15 pub fn to_projjson(&self) -> Value {
17 match self {
18 Crs::ProjectedCrs(crs) => crs.to_projjson(),
19 Crs::GeogCrs(crs) => crs.to_projjson(),
20 Crs::GeodCrs(crs) => crs.to_projjson(),
21 Crs::VertCrs(crs) => crs.to_projjson(),
22 Crs::CompoundCrs(crs) => crs.to_projjson(),
23 }
24 }
25}
26
27impl ProjectedCrs {
28 pub fn to_projjson(&self) -> Value {
30 let mut obj = Map::new();
31 insert_schema(&mut obj);
32 obj.insert("type".into(), json!("ProjectedCRS"));
33 obj.insert("name".into(), json!(self.name));
34 obj.insert("base_crs".into(), base_crs_to_json(&self.base_geodetic_crs));
35 obj.insert(
36 "conversion".into(),
37 conversion_to_json(&self.map_projection),
38 );
39 obj.insert(
40 "coordinate_system".into(),
41 cs_to_json(&self.coordinate_system),
42 );
43
44 insert_usages(&mut obj, &self.usages);
45 if let Some(ref remark) = self.remark {
46 obj.insert("remarks".into(), json!(remark));
47 }
48 insert_ids(&mut obj, &self.identifiers);
49
50 Value::Object(obj)
51 }
52}
53
54impl GeogCrs {
55 pub fn to_projjson(&self) -> Value {
57 let mut obj = Map::new();
58 insert_schema(&mut obj);
59 obj.insert("type".into(), json!("GeographicCRS"));
60 obj.insert("name".into(), json!(self.name));
61
62 match &self.datum {
63 Datum::ReferenceFrame(rf) => {
64 obj.insert("datum".into(), datum_to_json(rf, self.dynamic.as_ref()));
65 }
66 Datum::Ensemble(ens) => {
67 obj.insert("datum_ensemble".into(), ensemble_to_json(ens));
68 }
69 }
70
71 if let Some(ref dynamic) = self.dynamic
72 && let Some(ref model) = dynamic.deformation_model
73 {
74 let mut m = Map::new();
75 m.insert("name".into(), json!(model.name));
76 insert_ids(&mut m, &model.identifiers);
77 obj.insert("deformation_models".into(), json!([Value::Object(m)]));
78 }
79
80 obj.insert(
81 "coordinate_system".into(),
82 cs_to_json(&self.coordinate_system),
83 );
84
85 insert_usages(&mut obj, &self.usages);
86 if let Some(ref remark) = self.remark {
87 obj.insert("remarks".into(), json!(remark));
88 }
89 insert_ids(&mut obj, &self.identifiers);
90
91 Value::Object(obj)
92 }
93}
94
95impl GeodCrs {
96 pub fn to_projjson(&self) -> Value {
98 let mut obj = Map::new();
99 insert_schema(&mut obj);
100 obj.insert("type".into(), json!("GeodeticCRS"));
101 obj.insert("name".into(), json!(self.name));
102
103 match &self.datum {
104 Datum::ReferenceFrame(rf) => {
105 obj.insert("datum".into(), datum_to_json(rf, self.dynamic.as_ref()));
106 }
107 Datum::Ensemble(ens) => {
108 obj.insert("datum_ensemble".into(), ensemble_to_json(ens));
109 }
110 }
111
112 if let Some(ref dynamic) = self.dynamic
113 && let Some(ref model) = dynamic.deformation_model
114 {
115 let mut m = Map::new();
116 m.insert("name".into(), json!(model.name));
117 insert_ids(&mut m, &model.identifiers);
118 obj.insert("deformation_models".into(), json!([Value::Object(m)]));
119 }
120
121 obj.insert(
122 "coordinate_system".into(),
123 cs_to_json(&self.coordinate_system),
124 );
125
126 insert_usages(&mut obj, &self.usages);
127 if let Some(ref remark) = self.remark {
128 obj.insert("remarks".into(), json!(remark));
129 }
130 insert_ids(&mut obj, &self.identifiers);
131
132 Value::Object(obj)
133 }
134}
135
136impl VertCrs {
137 pub fn to_projjson(&self) -> Value {
139 let mut obj = Map::new();
140 insert_schema(&mut obj);
141
142 match &self.source {
143 VertCrsSource::Datum { dynamic, datum } => {
144 obj.insert("type".into(), json!("VerticalCRS"));
145 obj.insert("name".into(), json!(self.name));
146
147 match datum {
148 VerticalDatum::ReferenceFrame(rf) => {
149 obj.insert("datum".into(), vertical_datum_to_json(rf, dynamic.as_ref()));
150 }
151 VerticalDatum::Ensemble(ens) => {
152 obj.insert("datum_ensemble".into(), ensemble_to_json(ens));
153 }
154 }
155
156 if let Some(dynamic) = dynamic
157 && let Some(ref model) = dynamic.deformation_model
158 {
159 let mut m = Map::new();
160 m.insert("name".into(), json!(model.name));
161 insert_ids(&mut m, &model.identifiers);
162 obj.insert("deformation_models".into(), json!([Value::Object(m)]));
163 }
164 }
165 VertCrsSource::Derived {
166 base_vert_crs,
167 deriving_conversion,
168 } => {
169 obj.insert("type".into(), json!("DerivedVerticalCRS"));
170 obj.insert("name".into(), json!(self.name));
171 obj.insert("base_crs".into(), base_vert_crs_to_json(base_vert_crs));
172 obj.insert("conversion".into(), conversion_to_json(deriving_conversion));
173 }
174 }
175
176 obj.insert(
177 "coordinate_system".into(),
178 cs_to_json(&self.coordinate_system),
179 );
180
181 if !self.geoid_models.is_empty() {
182 let models: Vec<Value> = self
183 .geoid_models
184 .iter()
185 .map(|gm| {
186 let mut m = Map::new();
187 m.insert("name".into(), json!(gm.name));
188 insert_ids(&mut m, &gm.identifiers);
189 Value::Object(m)
190 })
191 .collect();
192 obj.insert("geoid_models".into(), Value::Array(models));
193 }
194
195 insert_usages(&mut obj, &self.usages);
196 if let Some(ref remark) = self.remark {
197 obj.insert("remarks".into(), json!(remark));
198 }
199 insert_ids(&mut obj, &self.identifiers);
200
201 Value::Object(obj)
202 }
203}
204
205impl CompoundCrs {
206 pub fn to_projjson(&self) -> Value {
208 let mut obj = Map::new();
209 insert_schema(&mut obj);
210 obj.insert("type".into(), json!("CompoundCRS"));
211 obj.insert("name".into(), json!(self.name));
212
213 let components: Vec<Value> = self
214 .components
215 .iter()
216 .map(|c| match c {
217 SingleCrs::ProjectedCrs(crs) => crs.to_projjson(),
218 SingleCrs::GeogCrs(crs) => crs.to_projjson(),
219 SingleCrs::GeodCrs(crs) => crs.to_projjson(),
220 SingleCrs::VertCrs(crs) => crs.to_projjson(),
221 SingleCrs::Other(raw) => json!({ "type": "Unknown", "wkt": raw }),
222 })
223 .collect();
224 obj.insert("components".into(), Value::Array(components));
225
226 insert_usages(&mut obj, &self.usages);
227 if let Some(ref remark) = self.remark {
228 obj.insert("remarks".into(), json!(remark));
229 }
230 insert_ids(&mut obj, &self.identifiers);
231
232 Value::Object(obj)
233 }
234}
235
236fn insert_schema(obj: &mut Map<String, Value>) {
237 obj.insert(
238 "$schema".into(),
239 json!("https://proj.org/schemas/v0.7/projjson.schema.json"),
240 );
241}
242
243fn base_crs_to_json(base: &BaseGeodeticCrs) -> Value {
248 let mut obj = Map::new();
249
250 let crs_type = match base.keyword {
251 BaseGeodeticCrsKeyword::BaseGeogCrs => "GeographicCRS",
252 BaseGeodeticCrsKeyword::BaseGeodCrs => "GeodeticCRS",
253 };
254 obj.insert("type".into(), json!(crs_type));
255 obj.insert("name".into(), json!(base.name));
256
257 match &base.datum {
258 Datum::ReferenceFrame(rf) => {
259 obj.insert("datum".into(), datum_to_json(rf, base.dynamic.as_ref()));
260 }
261 Datum::Ensemble(ens) => {
262 obj.insert("datum_ensemble".into(), ensemble_to_json(ens));
263 }
264 }
265
266 if let Some(ref dynamic) = base.dynamic
268 && let Some(ref model) = dynamic.deformation_model
269 {
270 let mut m = Map::new();
271 m.insert("name".into(), json!(model.name));
272 insert_ids(&mut m, &model.identifiers);
273 obj.insert("deformation_models".into(), json!([Value::Object(m)]));
274 }
275
276 insert_ids(&mut obj, &base.identifiers);
281
282 Value::Object(obj)
283}
284
285fn base_vert_crs_to_json(base: &BaseVertCrs) -> Value {
286 let mut obj = Map::new();
287 obj.insert("type".into(), json!("VerticalCRS"));
288 obj.insert("name".into(), json!(base.name));
289
290 match &base.datum {
291 VerticalDatum::ReferenceFrame(rf) => {
292 obj.insert(
293 "datum".into(),
294 vertical_datum_to_json(rf, base.dynamic.as_ref()),
295 );
296 }
297 VerticalDatum::Ensemble(ens) => {
298 obj.insert("datum_ensemble".into(), ensemble_to_json(ens));
299 }
300 }
301
302 if let Some(ref dynamic) = base.dynamic
303 && let Some(ref model) = dynamic.deformation_model
304 {
305 let mut m = Map::new();
306 m.insert("name".into(), json!(model.name));
307 insert_ids(&mut m, &model.identifiers);
308 obj.insert("deformation_models".into(), json!([Value::Object(m)]));
309 }
310
311 insert_ids(&mut obj, &base.identifiers);
312
313 Value::Object(obj)
314}
315
316fn datum_to_json(rf: &GeodeticReferenceFrame, dynamic: Option<&DynamicCrs>) -> Value {
321 let mut obj = Map::new();
322
323 if let Some(dyn_crs) = dynamic {
324 obj.insert("type".into(), json!("DynamicGeodeticReferenceFrame"));
325 obj.insert(
326 "frame_reference_epoch".into(),
327 json!(dyn_crs.frame_reference_epoch),
328 );
329 } else {
330 obj.insert("type".into(), json!("GeodeticReferenceFrame"));
331 }
332
333 obj.insert("name".into(), json!(rf.name));
334
335 if let Some(ref anchor) = rf.anchor {
336 obj.insert("anchor".into(), json!(anchor));
337 }
338 if let Some(epoch) = rf.anchor_epoch {
339 obj.insert("anchor_epoch".into(), json!(epoch));
340 }
341
342 obj.insert("ellipsoid".into(), ellipsoid_to_json(&rf.ellipsoid));
343
344 if let Some(ref pm) = rf.prime_meridian {
345 obj.insert("prime_meridian".into(), prime_meridian_to_json(pm));
346 }
347
348 insert_ids(&mut obj, &rf.identifiers);
349
350 Value::Object(obj)
351}
352
353fn vertical_datum_to_json(rf: &VerticalReferenceFrame, dynamic: Option<&DynamicCrs>) -> Value {
354 let mut obj = Map::new();
355
356 if let Some(dyn_crs) = dynamic {
357 obj.insert("type".into(), json!("DynamicVerticalReferenceFrame"));
358 obj.insert(
359 "frame_reference_epoch".into(),
360 json!(dyn_crs.frame_reference_epoch),
361 );
362 } else {
363 obj.insert("type".into(), json!("VerticalReferenceFrame"));
364 }
365
366 obj.insert("name".into(), json!(rf.name));
367
368 if let Some(ref anchor) = rf.anchor {
369 obj.insert("anchor".into(), json!(anchor));
370 }
371 if let Some(epoch) = rf.anchor_epoch {
372 obj.insert("anchor_epoch".into(), json!(epoch));
373 }
374
375 insert_ids(&mut obj, &rf.identifiers);
376
377 Value::Object(obj)
378}
379
380fn ensemble_to_json(ens: &DatumEnsemble) -> Value {
381 let mut obj = Map::new();
382 obj.insert("type".into(), json!("DatumEnsemble"));
383 obj.insert("name".into(), json!(ens.name));
384
385 let members: Vec<Value> = ens
386 .members
387 .iter()
388 .map(|m| {
389 let mut mobj = Map::new();
390 mobj.insert("name".into(), json!(m.name));
391 insert_ids(&mut mobj, &m.identifiers);
392 Value::Object(mobj)
393 })
394 .collect();
395 obj.insert("members".into(), Value::Array(members));
396
397 if let Some(ref ellipsoid) = ens.ellipsoid {
398 obj.insert("ellipsoid".into(), ellipsoid_to_json(ellipsoid));
399 }
400
401 obj.insert("accuracy".into(), json!(ens.accuracy.to_string()));
403
404 insert_ids(&mut obj, &ens.identifiers);
405
406 Value::Object(obj)
407}
408
409fn ellipsoid_to_json(e: &Ellipsoid) -> Value {
414 let mut obj = Map::new();
415 obj.insert("name".into(), json!(e.name));
416 obj.insert(
417 "semi_major_axis".into(),
418 value_with_optional_unit(e.semi_major_axis, e.unit.as_ref()),
419 );
420 obj.insert("inverse_flattening".into(), json!(e.inverse_flattening));
421 insert_ids(&mut obj, &e.identifiers);
422 Value::Object(obj)
423}
424
425fn prime_meridian_to_json(pm: &PrimeMeridian) -> Value {
426 let mut obj = Map::new();
427 obj.insert("name".into(), json!(pm.name));
428 obj.insert(
429 "longitude".into(),
430 value_with_optional_unit(pm.irm_longitude, pm.unit.as_ref()),
431 );
432 insert_ids(&mut obj, &pm.identifiers);
433 Value::Object(obj)
434}
435
436fn value_with_optional_unit(value: f64, unit: Option<&Unit>) -> Value {
439 match unit {
440 Some(u) => {
441 json!({
442 "value": value,
443 "unit": unit_to_json(u),
444 })
445 }
446 None => json!(value),
447 }
448}
449
450fn conversion_to_json(mp: &MapProjection) -> Value {
455 let mut obj = Map::new();
456 obj.insert("name".into(), json!(mp.name));
457 obj.insert("method".into(), method_to_json(&mp.method));
458
459 if !mp.parameters.is_empty() {
460 let params: Vec<Value> = mp.parameters.iter().map(parameter_to_json).collect();
461 obj.insert("parameters".into(), Value::Array(params));
462 }
463
464 insert_ids(&mut obj, &mp.identifiers);
465 Value::Object(obj)
466}
467
468fn method_to_json(m: &MapProjectionMethod) -> Value {
469 let mut obj = Map::new();
470 obj.insert("name".into(), json!(m.name));
471 insert_ids(&mut obj, &m.identifiers);
472 Value::Object(obj)
473}
474
475fn parameter_to_json(p: &MapProjectionParameter) -> Value {
476 let mut obj = Map::new();
477 obj.insert("name".into(), json!(p.name));
478 obj.insert("value".into(), json!(p.value));
479 if let Some(ref unit) = p.unit {
480 obj.insert("unit".into(), unit_to_json(unit));
481 }
482 insert_ids(&mut obj, &p.identifiers);
483 Value::Object(obj)
484}
485
486fn cs_to_json(cs: &CoordinateSystem) -> Value {
491 let mut obj = Map::new();
492 obj.insert("subtype".into(), json!(cs.cs_type.to_string()));
493
494 let axes: Vec<Value> = cs
495 .axes
496 .iter()
497 .map(|a| axis_to_json(a, cs.cs_unit.as_ref()))
498 .collect();
499 obj.insert("axis".into(), Value::Array(axes));
500
501 insert_ids(&mut obj, &cs.identifiers);
502 Value::Object(obj)
503}
504
505fn axis_to_json(axis: &Axis, cs_unit: Option<&Unit>) -> Value {
506 let mut obj = Map::new();
507
508 let (name, abbreviation) = split_axis_name_abbrev(&axis.name_abbrev);
509 obj.insert("name".into(), json!(name));
510 obj.insert("abbreviation".into(), json!(abbreviation));
511 obj.insert("direction".into(), json!(axis.direction));
512
513 let effective_unit = axis.unit.as_ref().or(cs_unit);
515 if let Some(unit) = effective_unit {
516 obj.insert("unit".into(), unit_to_json(unit));
517 }
518
519 if let Some(ref meridian) = axis.meridian {
520 obj.insert("meridian".into(), meridian_to_json(meridian));
521 }
522
523 if let Some(min) = axis.axis_min_value {
524 obj.insert("minimum_value".into(), json!(min));
525 }
526 if let Some(max) = axis.axis_max_value {
527 obj.insert("maximum_value".into(), json!(max));
528 }
529 if let Some(rm) = axis.range_meaning {
530 obj.insert(
531 "range_meaning".into(),
532 json!(match rm {
533 RangeMeaning::Exact => "exact",
534 RangeMeaning::Wraparound => "wraparound",
535 }),
536 );
537 }
538
539 insert_ids(&mut obj, &axis.identifiers);
540 Value::Object(obj)
541}
542
543fn meridian_to_json(m: &Meridian) -> Value {
544 json!({
545 "longitude": m.value,
546 "unit": unit_to_json(&m.unit),
547 })
548}
549
550fn split_axis_name_abbrev(name_abbrev: &str) -> (&str, &str) {
553 if let Some(paren_start) = name_abbrev.rfind('(')
554 && let Some(paren_end) = name_abbrev[paren_start..].find(')')
555 {
556 let name = name_abbrev[..paren_start].trim();
557 let abbrev = &name_abbrev[paren_start + 1..paren_start + paren_end];
558 return (name, abbrev);
559 }
560 (name_abbrev, "")
561}
562
563fn unit_to_json(unit: &Unit) -> Value {
568 match (unit.name.as_str(), unit.conversion_factor) {
570 ("metre", Some(1.0)) => return json!("metre"),
571 ("degree", Some(f)) if (f - 0.0174532925199433).abs() < 1e-15 => {
572 return json!("degree");
573 }
574 ("unity", Some(1.0)) => return json!("unity"),
575 _ => {}
576 }
577
578 let mut obj = Map::new();
579 let type_str = match unit.keyword {
580 UnitKeyword::AngleUnit => "AngularUnit",
581 UnitKeyword::LengthUnit => "LinearUnit",
582 UnitKeyword::ScaleUnit => "ScaleUnit",
583 UnitKeyword::TimeUnit => "TimeUnit",
584 UnitKeyword::ParametricUnit => "ParametricUnit",
585 UnitKeyword::Unit => "Unit",
586 };
587 obj.insert("type".into(), json!(type_str));
588 obj.insert("name".into(), json!(unit.name));
589 if let Some(factor) = unit.conversion_factor {
590 obj.insert("conversion_factor".into(), json!(factor));
591 }
592 insert_ids(&mut obj, &unit.identifiers);
593 Value::Object(obj)
594}
595
596fn id_to_json(id: &Identifier) -> Value {
601 let mut obj = Map::new();
602 obj.insert("authority".into(), json!(id.authority_name));
603 match &id.authority_unique_id {
604 AuthorityId::Number(n) => {
605 if *n == (*n as i64) as f64 {
607 obj.insert("code".into(), json!(*n as i64));
608 } else {
609 obj.insert("code".into(), json!(n));
610 }
611 }
612 AuthorityId::Text(s) => {
613 obj.insert("code".into(), json!(s));
614 }
615 }
616 if let Some(ref version) = id.version {
617 match version {
618 AuthorityId::Number(n) => obj.insert("version".into(), json!(n)),
619 AuthorityId::Text(s) => obj.insert("version".into(), json!(s)),
620 };
621 }
622 if let Some(ref citation) = id.citation {
623 obj.insert("authority_citation".into(), json!(citation));
624 }
625 if let Some(ref uri) = id.uri {
626 obj.insert("uri".into(), json!(uri));
627 }
628 Value::Object(obj)
629}
630
631fn insert_ids(obj: &mut Map<String, Value>, identifiers: &[Identifier]) {
633 match identifiers.len() {
634 0 => {}
635 1 => {
636 obj.insert("id".into(), id_to_json(&identifiers[0]));
637 }
638 _ => {
639 let ids: Vec<Value> = identifiers.iter().map(id_to_json).collect();
640 obj.insert("ids".into(), Value::Array(ids));
641 }
642 }
643}
644
645fn insert_usages(obj: &mut Map<String, Value>, usages: &[Usage]) {
650 if usages.is_empty() {
651 return;
652 }
653
654 let arr: Vec<Value> = usages.iter().map(usage_to_json).collect();
656 obj.insert("usages".into(), Value::Array(arr));
657}
658
659fn usage_to_json(u: &Usage) -> Value {
660 let mut obj = Map::new();
661 obj.insert("scope".into(), json!(u.scope));
662 if let Some(ref area) = u.area {
663 obj.insert("area".into(), json!(area));
664 }
665 if let Some(ref bbox) = u.bbox {
666 obj.insert("bbox".into(), bbox_to_json(bbox));
667 }
668 if let Some(ref ve) = u.vertical_extent {
669 obj.insert("vertical_extent".into(), vertical_extent_to_json(ve));
670 }
671 if let Some(ref te) = u.temporal_extent {
672 obj.insert("temporal_extent".into(), temporal_extent_to_json(te));
673 }
674 Value::Object(obj)
675}
676
677fn bbox_to_json(bbox: &BBox) -> Value {
678 json!({
679 "south_latitude": bbox.lower_left_latitude,
680 "west_longitude": bbox.lower_left_longitude,
681 "north_latitude": bbox.upper_right_latitude,
682 "east_longitude": bbox.upper_right_longitude,
683 })
684}
685
686fn vertical_extent_to_json(ve: &VerticalExtent) -> Value {
687 let mut obj = Map::new();
688 obj.insert("minimum".into(), json!(ve.minimum_height));
689 obj.insert("maximum".into(), json!(ve.maximum_height));
690 if let Some(ref unit) = ve.unit {
691 obj.insert("unit".into(), unit_to_json(unit));
692 }
693 Value::Object(obj)
694}
695
696fn temporal_extent_to_json(te: &TemporalExtent) -> Value {
697 json!({
698 "start": te.start,
699 "end": te.end,
700 })
701}