1use super::{
3 Axis, Conversion, CoordinateSystem, Datum, DatumEnsemble, DatumEnsembleMember, Ellipsoid,
4 EngineeringDatum, GeodeticCRS, GeodeticReferenceFrame, Id, Meridian, Method, ObjectUsage,
5 ParameterValue, ParametricDatum, PrimeMeridian, ProjBBox, ProjJSON, ProjectedCRS,
6 TemporalDatum, TemporalExtent, ToProjJSON, Unit, UnitObject, UnitType,
7 ValueInDegreeOrValueAndUnit, ValueInMetreOrValueAndUnit, VerticalExtent,
8 VerticalReferenceFrame,
9};
10use crate::{
11 parsers::{WKTParser, WKTValue, parse_wkt_object},
12 proj::AxisDirection,
13};
14use alloc::format;
15
16impl WKTParser for ObjectUsage {
17 fn from_wkt(val: &WKTValue) -> Self {
18 let mut usage = ObjectUsage::default();
19 if let WKTValue::Array(arr) = val {
20 if !arr.is_empty()
21 && arr[0].to_string() == "SCOPE"
22 && let WKTValue::Array(arr) = &arr[1]
23 {
24 usage.scope = arr.first().map(|s| s.to_string()).unwrap_or_default();
25 }
26 if arr.len() >= 2 {
27 handle_common_fields(&mut usage, arr, 2);
28 }
29 }
30 usage
31 }
32}
33
34impl WKTParser for ProjBBox {
35 fn from_wkt(val: &WKTValue) -> Self {
36 let mut bbox = ProjBBox::default();
37 if let WKTValue::Array(arr) = val
38 && arr.len() >= 4
39 {
40 bbox.south_latitude = arr[0].to_float();
41 bbox.west_longitude = arr[1].to_float();
42 bbox.north_latitude = arr[2].to_float();
43 bbox.east_longitude = arr[3].to_float();
44 }
45 bbox
46 }
47}
48
49impl WKTParser for VerticalExtent {
50 fn from_wkt(val: &WKTValue) -> Self {
51 let mut ve = VerticalExtent::default();
52 if let WKTValue::Array(arr) = val
53 && arr.len() >= 2
54 {
55 ve.minimum = arr[0].to_float();
56 ve.maximum = arr[1].to_float();
57 if arr.len() >= 4 && arr[2].to_string() == "LENGTHUNIT" {
58 ve.unit = Unit::from_wkt(&arr[3]);
59 ve.unit.set_unit_type(UnitType::LinearUnit);
60 }
61 }
62 ve
63 }
64}
65
66impl WKTParser for Unit {
67 fn from_wkt(unit_xml: &WKTValue) -> Self {
69 let mut unit = UnitObject::default();
70 if let WKTValue::Array(arr) = unit_xml {
71 if let Some(name) = arr.first() {
72 unit.name = name.to_string();
73 }
74 if let Some(conversion_factor) = arr.get(1) {
75 unit.conversion_factor = Some(conversion_factor.to_float());
76 }
77 handle_common_fields(&mut unit, arr, 2);
78 }
79 Unit::UnitObject(unit)
80 }
81}
82
83impl WKTParser for TemporalExtent {
84 fn from_wkt(val: &WKTValue) -> Self {
85 let mut te = TemporalExtent::default();
86 if let WKTValue::Array(arr) = val
87 && arr.len() >= 2
88 {
89 te.start = arr.first().map(|v| v.to_string()).unwrap_or_default();
90 te.end = arr.get(1).map(|v| v.to_string()).unwrap_or_default();
91 }
92 te
93 }
94}
95
96impl WKTParser for Id {
97 fn from_wkt(val: &WKTValue) -> Self {
98 let mut id = Id::default();
99 if let WKTValue::Array(arr) = val
100 && arr.len() >= 2
101 {
102 id.authority = arr[0].to_string();
103 id.code = arr[1].to_string().into();
104
105 let mut i = 2;
106 while i < arr.len() {
107 match arr[i].to_string().as_str() {
108 "CITATION" => {
109 if let WKTValue::Array(arr) = &arr[i + 1] {
110 id.authority_citation =
111 Some(arr.first().map(|s| s.to_string()).unwrap_or_default());
112 }
113 i += 2;
114 }
115 "URI" => {
116 if let WKTValue::Array(arr) = &arr[i + 1] {
117 id.uri = Some(arr.first().map(|s| s.to_string()).unwrap_or_default());
118 }
119 i += 2;
120 }
121 other => {
122 id.version = Some(other.into());
123 i += 1;
124 }
125 }
126 }
127 }
128 id
129 }
130}
131
132impl WKTParser for Ellipsoid {
133 fn from_wkt(val: &WKTValue) -> Self {
134 let mut ellipsoid = Ellipsoid::default();
135 let mut unit = Unit::default();
136 let mut semi_major_axis = 0.0;
137 let mut inverse_flattening = 0.0;
138
139 if let WKTValue::Array(arr) = val
140 && arr.len() >= 3
141 {
142 ellipsoid.name = arr[0].to_string();
143 semi_major_axis = arr[1].to_float();
144 inverse_flattening = arr[2].to_float();
145
146 handle_common_fields(&mut unit, arr, 3);
148 handle_common_fields(&mut ellipsoid, arr, 3);
150 }
151 if let Unit::UnitObject(_) = unit {
153 ellipsoid.semi_major_axis =
154 Some(ValueInMetreOrValueAndUnit::from_unit(unit.clone(), semi_major_axis));
155 ellipsoid.inverse_flattening =
156 Some(ValueInMetreOrValueAndUnit::from_unit(unit, inverse_flattening));
157 } else {
158 if semi_major_axis != 0.0 {
159 ellipsoid.semi_major_axis = Some(semi_major_axis.into());
160 }
161 if inverse_flattening != 0.0 {
162 ellipsoid.inverse_flattening = Some(inverse_flattening.into());
163 }
164 }
165 ellipsoid
166 }
167}
168
169impl WKTParser for ParameterValue {
170 fn from_wkt(wkt: &WKTValue) -> Self {
171 let mut parameter_value = ParameterValue::default();
172
173 if let WKTValue::Array(arr) = wkt {
174 if arr.len() >= 2 {
175 parameter_value.name = arr[0].to_string().to_lowercase().replace(" ", "_");
177 parameter_value.value = arr[1].to_string().into();
178 }
179 handle_common_fields(&mut parameter_value, arr, 2);
180 }
181 parameter_value
182 }
183}
184
185impl WKTParser for CoordinateSystem {
186 fn from_wkt(val: &WKTValue) -> Self {
187 let mut cs = CoordinateSystem::default();
188 if let WKTValue::Array(arr) = val
189 && !arr.is_empty()
190 {
191 cs.subtype =
192 serde_json::from_str(&format!("\"{}\"", arr[0].to_string())).unwrap_or_default();
193 }
194 cs
195 }
196}
197
198impl WKTParser for Axis {
199 fn from_wkt(val: &WKTValue) -> Self {
200 let mut axis = Axis::default();
201 if let WKTValue::Array(arr) = val
202 && arr.len() >= 2
203 {
204 axis.abbreviation = arr[0].to_string();
205 axis.name = axis.abbreviation.clone();
206 axis.direction = AxisDirection::from(arr[1].to_string());
207 handle_common_fields(&mut axis, arr, 2);
208 }
210 axis.adjust_if_needed();
211 axis
212 }
213}
214
215impl WKTParser for DatumEnsembleMember {
216 fn from_wkt(val: &WKTValue) -> Self {
217 let mut datum_ensemble_member = DatumEnsembleMember::default();
218 if let WKTValue::Array(arr) = val {
219 if let Some(name) = arr.first() {
220 datum_ensemble_member.name = name.to_string();
221 }
222 handle_common_fields(&mut datum_ensemble_member, arr, 1);
223 }
224 datum_ensemble_member
225 }
226}
227
228impl WKTParser for Method {
229 fn from_wkt(val: &WKTValue) -> Self {
230 let mut method = Method::default();
231 if let WKTValue::Array(arr) = val {
232 if let Some(name) = arr.first() {
233 method.name = name.to_string();
234 }
235 handle_common_fields(&mut method, arr, 1);
236 }
237 method
238 }
239}
240
241impl WKTParser for DatumEnsemble {
242 fn from_wkt(val: &WKTValue) -> Self {
243 let mut ensemble = DatumEnsemble::default();
244 if let WKTValue::Array(arr) = val {
245 if let Some(name) = arr.first() {
246 ensemble.name = name.to_string();
247 }
248 handle_common_fields(&mut ensemble, arr, 1);
249 }
250 ensemble
251 }
252}
253
254impl WKTParser for PrimeMeridian {
255 fn from_wkt(val: &WKTValue) -> Self {
256 let mut pm = PrimeMeridian::default();
257 if let WKTValue::Array(arr) = val {
258 if let Some(name) = arr.first() {
259 pm.name = name.to_string();
260 }
261 if let Some(lon) = arr.get(1) {
262 pm.longitude =
263 ValueInDegreeOrValueAndUnit::from_unit(Unit::new_deg(), lon.to_float());
264 }
265 handle_common_fields(&mut pm, arr, 2);
266 }
267 pm
268 }
269}
270
271impl WKTParser for Meridian {
272 fn from_wkt(val: &WKTValue) -> Self {
273 let mut meridian = Meridian::default();
274 if let WKTValue::Array(arr) = val {
275 let mut unit = Unit::default();
276 if let Some(unit_xml) = arr.get(1) {
277 unit = Unit::from_wkt(unit_xml);
278 }
279 if let Some(lon) = arr.first() {
280 meridian.longitude = ValueInDegreeOrValueAndUnit::from_unit(unit, lon.to_float());
281 }
282 handle_common_fields(&mut meridian, arr, 2);
283 }
284 meridian
285 }
286}
287
288impl WKTParser for GeodeticReferenceFrame {
289 fn from_wkt(val: &WKTValue) -> Self {
290 let mut geodetic_reference_frame = GeodeticReferenceFrame::default();
291 if let WKTValue::Array(arr) = val {
292 if let Some(name) = arr.first() {
293 geodetic_reference_frame.name = name.to_string();
294 }
295 handle_common_fields(&mut geodetic_reference_frame, arr, 1);
296 }
297 geodetic_reference_frame
298 }
299}
300
301impl WKTParser for VerticalReferenceFrame {
302 fn from_wkt(val: &WKTValue) -> Self {
303 let mut vertical_reference_frame = VerticalReferenceFrame::default();
304 if let WKTValue::Array(arr) = val {
305 if let Some(name) = arr.first() {
306 vertical_reference_frame.name = name.to_string();
307 }
308 handle_common_fields(&mut vertical_reference_frame, arr, 1);
309 }
310 vertical_reference_frame
311 }
312}
313
314impl WKTParser for TemporalDatum {
315 fn from_wkt(val: &WKTValue) -> Self {
316 let mut temporal_datum = TemporalDatum::default();
319 if let WKTValue::Array(arr) = val {
320 if let Some(name) = arr.first() {
321 temporal_datum.name = name.to_string();
322 }
323 handle_common_fields(&mut temporal_datum, arr, 1);
324 }
325 temporal_datum
326 }
327}
328
329impl WKTParser for EngineeringDatum {
330 fn from_wkt(val: &WKTValue) -> Self {
331 let mut engineering_datum = EngineeringDatum::default();
332 if let WKTValue::Array(arr) = val {
333 if let Some(name) = arr.first() {
334 engineering_datum.name = name.to_string();
335 }
336 handle_common_fields(&mut engineering_datum, arr, 1);
337 }
338 engineering_datum
339 }
340}
341
342impl WKTParser for ParametricDatum {
343 fn from_wkt(val: &WKTValue) -> Self {
344 let mut parametric_datum = ParametricDatum::default();
345 if let WKTValue::Array(arr) = val {
346 if let Some(name) = arr.first() {
347 parametric_datum.name = name.to_string();
348 }
349 handle_common_fields(&mut parametric_datum, arr, 1);
350 }
351 parametric_datum
352 }
353}
354
355impl WKTParser for Conversion {
356 fn from_wkt(val: &WKTValue) -> Self {
357 let mut conversion = Conversion::default();
358 if let WKTValue::Array(arr) = val {
359 if let Some(name) = arr.first() {
360 conversion.name = name.to_string();
361 }
362 handle_common_fields(&mut conversion, arr, 1);
363 }
364 conversion
365 }
366}
367
368impl WKTParser for GeodeticCRS {
369 fn from_wkt(val: &WKTValue) -> Self {
370 let mut geodetic_crs = GeodeticCRS::default();
371 if let WKTValue::Array(arr) = val {
372 if let Some(name) = arr.first() {
373 geodetic_crs.name = name.to_string();
374 }
375 handle_common_fields(&mut geodetic_crs, arr, 1);
376 }
377 geodetic_crs
378 }
379}
380
381impl WKTParser for ProjectedCRS {
382 fn from_wkt(val: &WKTValue) -> Self {
383 let mut projected_crs = ProjectedCRS::default();
384 if let WKTValue::Array(arr) = val {
385 if let Some(name) = arr.first() {
386 projected_crs.name = name.to_string();
387 }
388 handle_common_fields(&mut projected_crs, arr, 1);
389 }
390 projected_crs
391 }
392}
393
394const TOP_LEVEL_PROJ_KEYWORDS: [&str; 27] = [
395 "BoundCRS",
396 "PROJCRS",
397 "PROJCS",
398 "BASEPROJCRS",
399 "PROJECTEDCRS",
400 "COORDINATEOPERATION",
401 "VERTCRS",
402 "VERTICALCRS",
403 "VERT_CS",
404 "GEOGCS",
405 "GEOGCRS",
406 "GEODCRS",
407 "BASEGEODCRS",
408 "BASEGEOGCRS",
409 "GEODETICCRS",
410 "GEOGRAPHICCRS",
411 "COMPOUNDCRS",
412 "COMPD_CS",
413 "CONCATENATEDOPERATION",
414 "DERIVEDPROJECTED",
415 "EngineeringCRS",
416 "ENGCRS",
417 "LOCAL_CS",
418 "ParametricCRS",
419 "POINTMOTIONOPERATION",
420 "TemporalCRS",
421 "TIMECRS",
422];
423
424impl ProjJSON {
425 pub fn parse_wkt(val: &str) -> Self {
427 let wkt = parse_wkt_object(val);
428 Self::from_wkt(&wkt)
429 }
430}
431impl WKTParser for ProjJSON {
432 fn from_wkt(val: &WKTValue) -> Self {
433 let mut proj_json = ProjJSON::default();
434 if let WKTValue::Array(arr) = val {
435 if let Some(name) = arr.first() {
437 let name = name.to_string();
438 if !TOP_LEVEL_PROJ_KEYWORDS.contains(&name.as_str()) {
439 panic!("Expected one of {}", TOP_LEVEL_PROJ_KEYWORDS.join(", "));
440 }
441 }
442 handle_common_fields(&mut proj_json, arr, 0);
443 }
444 proj_json
445 }
446}
447
448fn handle_common_fields<T: ToProjJSON>(res: &mut T, arr: &[WKTValue], start_index: usize) {
450 let mut i = start_index;
451 while i < arr.len() {
452 if let Some(WKTValue::String(item_keyword)) = arr.get(i) {
453 let key = item_keyword.as_str();
454 match key {
455 "ID" | "AUTHORITY" => {
456 res.set_id(Id::from_wkt(&arr[i + 1]));
457 i += 1;
458 }
459 "UNIT" | "LENGTHUNIT" | "ANGLEUNIT" | "SCALEUNIT" | "TIMEUNIT"
460 | "PARAMETRICUNIT" => {
461 let mut unit = Unit::from_wkt(&arr[i + 1]);
462 unit.set_unit_type(key.into());
463 res.set_unit(unit);
464 i += 1;
465 }
466 "CS" => {
467 res.set_coordinate_system(CoordinateSystem::from_wkt(&arr[i + 1]));
468 i += 1;
469 }
470 "AXIS" => {
471 res.set_axis(Axis::from_wkt(&arr[i + 1]));
472 i += 1;
473 }
474 "MEMBERS" => {
475 res.set_member(DatumEnsembleMember::from_wkt(&arr[i + 1]));
476 i += 1;
477 }
478 "ELLIPSOID" | "SPHEROID" => {
479 res.set_ellipsoid(Ellipsoid::from_wkt(&arr[i + 1]));
480 i += 1;
481 }
482 "ENSEMBLEACCURACY" | "OPERATIONACCURACY" => {
483 res.set_accuracy(arr[i + 1].to_string());
484 i += 1;
485 }
486 "EPOCH" | "ANCHOREPOCH" | "COORDEPOCH" => {
487 let WKTValue::Array(arr) = &arr[i + 1] else {
488 continue;
489 };
490 let epoch = arr.first().map(|s| s.to_float()).unwrap_or_default();
491 res.set_epoch(epoch);
492 i += 1;
493 }
494 "FRAMEEPOCH" => {
495 res.set_frame_epoch(arr[i + 1].to_float());
496 i += 1;
497 }
498 "ENSEMBLE" => {
499 res.set_ensemble(DatumEnsemble::from_wkt(&arr[i + 1]));
500 i += 1;
501 }
502 "DATUM" | "GEODETICDATUM" | "TRF" => {
503 res.set_datum(Datum::GeodeticReferenceFrame(GeodeticReferenceFrame::from_wkt(
504 &arr[i + 1],
505 )));
506 i += 1;
507 }
508 "VDATUM" | "VRF" | "VERTICALDATUM" => {
509 res.set_datum(Datum::VerticalReferenceFrame(VerticalReferenceFrame::from_wkt(
510 &arr[i + 1],
511 )));
512 i += 1;
513 }
514 "TDATUM" | "TIMEDATUM" => {
515 res.set_datum(Datum::TemporalDatum(TemporalDatum::from_wkt(&arr[i + 1])));
516 i += 1;
517 }
518 "EDATUM" | "ENGINEERINGDATUM" => {
519 res.set_datum(Datum::EngineeringDatum(EngineeringDatum::from_wkt(&arr[i + 1])));
520 i += 1;
521 }
522 "PDATUM" | "PARAMETRICDATUM" => {
523 res.set_datum(Datum::ParametricDatum(ParametricDatum::from_wkt(&arr[i + 1])));
524 i += 1;
525 }
526 "METHOD" => {
527 res.set_method(Method::from_wkt(&arr[i + 1]));
528 i += 1;
529 }
530 "PARAMETER" | "PARAMETERFILE" => {
531 let mut param = ParameterValue::from_wkt(&arr[i + 1]);
532 param.is_file = key == "PARAMETERFILE";
533 res.set_parameter(param);
534 i += 1;
535 }
536 "PRIMEM" | "PRIMEMERIDIAN" => {
537 res.set_prime_meridian(PrimeMeridian::from_wkt(&arr[i + 1]));
538 i += 1;
539 }
540 "MERIDIAN" => {
541 res.set_meridian(Meridian::from_wkt(&arr[i + 1]));
542 i += 1;
543 }
544 "TIMEEXTENT" => {
545 res.set_temporal_extent(TemporalExtent::from_wkt(&arr[i + 1]));
546 i += 1;
547 }
548 "VERTICALEXTENT" => {
549 res.set_vertical_extent(VerticalExtent::from_wkt(&arr[i + 1]));
550 i += 1;
551 }
552 "BBOX" => {
553 res.set_bbox(ProjBBox::from_wkt(&arr[i + 1]));
554 i += 1;
555 }
556 "AREA" => {
557 if let WKTValue::Array(arr) = &arr[i + 1] {
558 res.set_area(arr.first().map(|s| s.to_string()));
559 i += 1;
560 }
561 }
562 "DERIVINGCONVERSION" | "CONVERSION" => {
563 res.set_conversion(Conversion::from_wkt(&arr[i + 1]));
564 i += 1;
565 }
566 "GEOGCS" | "GEOGCRS" | "GEOGRAPHICCRS" | "GEODCRS" | "GEODETICCRS"
567 | "BASEGEODCRS" | "BASEGEOGCRS" => {
568 res.set_geodetic_crs(GeodeticCRS::from_wkt(&arr[i + 1]));
569 i += 1;
570 }
571 "PROJCRS" | "PROJECTEDCRS" | "PROJCS" | "BASEPROJCRS" => {
572 res.set_projected_crs(ProjectedCRS::from_wkt(&arr[i + 1]));
573 i += 1;
574 }
575 "ANCHOR" => {
576 let WKTValue::Array(arr) = &arr[i + 1] else {
577 continue;
578 };
579 let anchor = arr.first().map(|s| s.to_string()).unwrap_or_default();
580 res.set_anchor(anchor);
581 i += 1;
582 }
583 "USAGE" => {
584 res.set_usage(ObjectUsage::from_wkt(&arr[i + 1]));
585 i += 1;
586 }
587 "PROJECTION" => {
588 let WKTValue::Array(arr) = &arr[i + 1] else {
589 continue;
590 };
591 let proj = arr.first().map(|s| s.to_string()).unwrap_or_default();
592 res.set_projection(proj);
593 i += 1;
594 }
595 "ORDER" => {
596 let WKTValue::Array(arr) = &arr[i + 1] else {
597 continue;
598 };
599 let order = arr.first().map(|s| s.to_float() as usize).unwrap_or_default();
600 res.set_order(order);
601 i += 1;
602 }
603 _ => {}
605 }
606 }
607 i += 1;
608 }
609}
610
611