1use crate::parsers::{FeatureReader, Reader};
2use alloc::{boxed::Box, collections::BTreeMap, string::String, vec, vec::Vec};
3use core::cell::RefCell;
4use s2json::{MValue, Properties, ValueType, VectorFeature, VectorGeometry, VectorPoint};
5
6#[derive(Debug, Clone, PartialEq)]
12pub enum CDFValue {
13 String(String),
15 Number(f64),
17 Array(Vec<f64>),
19}
20impl Default for CDFValue {
21 fn default() -> Self {
22 CDFValue::Number(0.0)
23 }
24}
25impl CDFValue {
26 pub fn to_num(&self) -> f64 {
28 match self {
29 CDFValue::Number(n) => *n,
30 _ => 0.0,
31 }
32 }
33 pub fn get_index(&self, index: u64) -> f64 {
35 match self {
36 CDFValue::Array(v) => v[index as usize],
37 _ => 0.0,
38 }
39 }
40}
41impl From<&CDFValue> for ValueType {
42 fn from(value: &CDFValue) -> Self {
43 match value {
44 CDFValue::String(s) => s.into(),
45 CDFValue::Number(n) => (*n).into(),
46 CDFValue::Array(v) => v.clone().into(),
47 }
48 }
49}
50impl From<&str> for CDFValue {
51 fn from(value: &str) -> Self {
52 CDFValue::String(value.into())
53 }
54}
55impl From<f64> for CDFValue {
56 fn from(value: f64) -> Self {
57 CDFValue::Number(value)
58 }
59}
60impl From<Vec<f64>> for CDFValue {
61 fn from(value: Vec<f64>) -> Self {
62 CDFValue::Array(value)
63 }
64}
65
66pub type CDFVectorFeature = VectorFeature<(), Properties, MValue>;
68
69pub type CDFAttributes = BTreeMap<String, CDFValue>;
71
72#[derive(Debug, Default, Clone, PartialEq)]
74pub struct CDFDimension {
75 pub index: u64,
77 pub name: String,
79 pub size: u64,
81}
82
83#[derive(Debug, Default, Clone, PartialEq)]
85pub struct CDFRecordDimension {
86 pub size: u64,
88 pub id: Option<u64>,
90 pub name: Option<String>,
92 pub record_step: Option<u64>,
94}
95
96#[derive(Debug, Default, Clone, PartialEq)]
98pub struct CDFVariable {
99 pub name: String,
101 pub dimensions: Vec<CDFDimension>,
103 pub attributes: CDFAttributes,
105 pub r#type: CDFDataType,
107 pub size: u64,
109 pub offset: u64,
111 pub record: bool,
113}
114
115const NC_UNLIMITED: u64 = 0;
117const NC_DIMENSION: u64 = 10;
118const NC_VARIABLE: u64 = 11;
119const NC_ATTRIBUTE: u64 = 12;
120
121#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
123pub enum CDFDataType {
124 #[default]
126 BYTE = 1,
127 CHAR = 2,
129 SHORT = 3,
131 INT = 4,
133 FLOAT = 5,
135 DOUBLE = 6,
137}
138impl From<u64> for CDFDataType {
139 fn from(value: u64) -> Self {
140 match value {
141 2 => CDFDataType::CHAR,
142 3 => CDFDataType::SHORT,
143 4 => CDFDataType::INT,
144 5 => CDFDataType::FLOAT,
145 6 => CDFDataType::DOUBLE,
146 _ => CDFDataType::BYTE,
147 }
148 }
149}
150
151pub fn netcdf_type_to_bytes(r#type: CDFDataType) -> u64 {
159 match r#type {
160 CDFDataType::BYTE | CDFDataType::CHAR => 1,
161 CDFDataType::SHORT => 2,
162 CDFDataType::INT | CDFDataType::FLOAT => 4,
163 CDFDataType::DOUBLE => 8,
164 }
165}
166
167#[derive(Debug, Default, Clone, PartialEq)]
169pub struct NetCDFReaderOptions {
170 pub lon_key: Option<String>,
172 pub lat_key: Option<String>,
174 pub height_key: Option<String>,
176 pub prop_fields: Option<Vec<String>>,
178}
179
180#[derive(Debug, Clone)]
231pub struct NetCDFReader<T: Reader> {
232 reader: T,
233 pub record_dimension: CDFRecordDimension,
235 pub dimensions: Vec<CDFDimension>,
237 pub global_attributes: CDFAttributes,
239 pub variables: Vec<CDFVariable>,
241 pub is64: bool,
243 cursor: RefCell<u64>,
245 lon_key: String,
246 lat_key: String,
247 height_key: Option<String>,
248 prop_fields: Vec<String>,
249}
250impl<T: Reader> NetCDFReader<T> {
251 pub fn new(reader: T, options: Option<NetCDFReaderOptions>) -> Self {
253 let magic = reader.parse_string(Some(0), Some(3));
255 if &magic != "CDF" {
256 panic!("Not a valid NetCDF file: should start with CDF");
257 }
258 let is64 = reader.uint8(Some(3)) != 1;
260 let options = options.unwrap_or_default();
261 let mut cdf_reader = NetCDFReader {
262 reader,
263 record_dimension: CDFRecordDimension {
264 size: 0,
265 id: None,
266 name: None,
267 record_step: None,
268 },
269 dimensions: vec![],
270 global_attributes: BTreeMap::new(),
271 variables: vec![],
272 is64,
273 cursor: 4.into(),
274 lon_key: options.lon_key.unwrap_or("lon".into()),
275 lat_key: options.lat_key.unwrap_or("lat".into()),
276 height_key: options.height_key,
277 prop_fields: options.prop_fields.unwrap_or_default(),
278 };
279 cdf_reader.parse_header();
281
282 cdf_reader
283 }
284
285 pub fn len(&self) -> u64 {
287 let lat = self.get_data_variable(self.lat_key.clone());
288 if let Some(lat) = lat {
289 return lat.len() as u64;
290 }
291 0
292 }
293
294 pub fn is_empty(&self) -> bool {
296 self.len() == 0
297 }
298
299 pub fn get_properties(&self, index: u64) -> Option<MValue> {
301 let mut m = MValue::new();
302 for field in self.prop_fields.clone().into_iter() {
303 let value = self.get_data_variable(field.clone());
304 if let Some(value) = value {
305 let field: String = field.clone();
306 let value: ValueType = (&value[index as usize]).into();
307 m.insert(field, value);
308 }
309 }
310 Some(m)
311 }
312
313 pub fn get_point(&self, index: u64) -> Option<VectorPoint<MValue>> {
315 if index >= self.len() {
316 return None;
317 }
318 let lat = self.get_data_variable(self.lat_key.clone());
319 let lon = self.get_data_variable(self.lon_key.clone());
320 let height = self.get_data_variable(self.height_key.clone().unwrap_or_default());
321 if let (Some(lat), Some(lon)) = (lat, lon) {
322 let lat = lat[0].get_index(index);
323 let lon = lon[0].get_index(index);
324 let m = self.get_properties(index);
325 return Some(VectorPoint::new(lon, lat, height.map(|h| h[index as usize].to_num()), m));
326 }
327 None
328 }
329
330 pub fn get_feature(&self, index: u64) -> Option<CDFVectorFeature> {
332 self.get_point(index).map(|point| {
333 VectorFeature::new_wm(
334 None,
335 Properties::default(),
336 VectorGeometry::new_point(point, None),
337 None,
338 )
339 })
340 }
341
342 pub fn get_data_variable(&self, variable_name: String) -> Option<Vec<CDFValue>> {
350 let variable = self.variables.iter().find(|val| val.name == variable_name).cloned();
351 if let Some(variable) = variable {
353 *self.cursor.borrow_mut() = variable.offset;
355 return if variable.record {
357 Some(self.get_record(variable))
358 } else {
359 Some(self.get_non_record(variable))
360 };
361 }
362 None
363 }
364
365 fn get_offset(&self) -> u64 {
372 if self.is64 { self.get_u64() } else { self.get_u32() }
373 }
374
375 fn get_u32(&self) -> u64 {
380 let data = self.reader.uint32_be(Some(*self.cursor.borrow()));
381 *self.cursor.borrow_mut() += 4;
382 data as u64
383 }
384
385 fn get_u64(&self) -> u64 {
390 let data = self.reader.uint64_be(Some(*self.cursor.borrow()));
391 *self.cursor.borrow_mut() += 8;
392 data
393 }
394
395 fn get_name(&self) -> String {
400 let name_length = self.get_u32();
401 let name = self.reader.parse_string(Some(*self.cursor.borrow()), Some(name_length));
402 *self.cursor.borrow_mut() += name_length;
403 self.padding();
404
405 name.trim().into()
406 }
407
408 fn parse_header(&mut self) {
410 self.record_dimension.size = self.get_u32();
412 self.build_dimension_list();
413 self.global_attributes = self.build_attributes();
415 self.build_variables_list();
417 }
418
419 fn get_type(&self, r#type: CDFDataType, size: u64) -> CDFValue {
428 let data = if r#type == CDFDataType::BYTE {
429 let mut res = vec![];
430 let mut i = 0;
431 while i < size {
432 res.push(self.reader.uint8(Some(*self.cursor.borrow())) as f64);
433 *self.cursor.borrow_mut() += 1;
434 i += 1;
435 }
436 CDFValue::Array(res)
437 } else if r#type == CDFDataType::CHAR {
438 let res = self.reader.parse_string(Some(*self.cursor.borrow()), Some(size));
439 *self.cursor.borrow_mut() += size;
440 CDFValue::String(res.trim().into())
441 } else if r#type == CDFDataType::SHORT
442 || r#type == CDFDataType::INT
443 || r#type == CDFDataType::FLOAT
444 || r#type == CDFDataType::DOUBLE
445 {
446 let step = if r#type == CDFDataType::DOUBLE {
447 8
448 } else if r#type == CDFDataType::SHORT {
449 2
450 } else {
451 4
452 };
453 let read_number: Box<dyn Fn(u64) -> f64> = if r#type == CDFDataType::SHORT {
454 Box::new(|offset: u64| self.reader.int16_be(Some(offset)) as f64)
455 } else if r#type == CDFDataType::INT {
456 Box::new(|offset: u64| self.reader.int32_be(Some(offset)) as f64)
457 } else if r#type == CDFDataType::FLOAT {
458 Box::new(|offset: u64| self.reader.f32_be(Some(offset)) as f64)
459 } else {
460 Box::new(|offset: u64| self.reader.f64_be(Some(offset)))
461 };
462 let mut res = vec![];
463 let mut i = 0;
464 while i < size {
465 res.push(read_number(*self.cursor.borrow()));
466 *self.cursor.borrow_mut() += step;
467 i += 1;
468 }
469 if res.len() == 1 { CDFValue::Number(res[0]) } else { CDFValue::Array(res) }
470 } else {
471 panic!("non valid type {:?}", r#type);
472 };
473
474 self.padding();
475
476 data
477 }
478
479 fn build_dimension_list(&mut self) {
481 let dim_list_tag = self.get_u32();
482
483 if dim_list_tag == 0 {
484 let ensure_empty = self.get_u32();
485 if ensure_empty != 0 {
486 panic!("wrong empty tag for list of dimensions");
487 }
488 } else {
489 if dim_list_tag != NC_DIMENSION {
490 panic!("wrong tag for list of dimensions");
491 }
492
493 let dimension_size = self.get_u32();
495 let mut index = 0;
497 while index < dimension_size {
498 let name = self.get_name();
500 let size = self.get_u32();
502 if size == NC_UNLIMITED {
503 self.record_dimension.id = Some(index);
505 self.record_dimension.name = Some(name.clone());
506 }
507 self.dimensions.push(CDFDimension { index, name, size });
509
510 index += 1;
511 }
512 }
513 }
514
515 fn build_attributes(&mut self) -> CDFAttributes {
520 let mut atrributes = CDFAttributes::default();
521 let g_att_tag = self.get_u32();
522 if g_att_tag == 0 {
523 let ensure_empty = self.get_u32();
524 if ensure_empty != 0 {
525 panic!("wrong empty tag for list of attributes");
526 }
527 } else {
528 if g_att_tag != NC_ATTRIBUTE {
529 panic!("wrong tag for list of attributes");
530 }
531 let attribute_size = self.get_u32();
533 let mut ga_idx = 0;
535 while ga_idx < attribute_size {
536 let name = self.get_name();
538 let r#type: CDFDataType = self.get_u32().into();
539 let size = self.get_u32();
540 let data = self.get_type(r#type, size);
542 atrributes.insert(name, data);
543 ga_idx += 1;
544 }
545 }
546
547 atrributes
548 }
549
550 fn build_variables_list(&mut self) {
552 let var_tag = self.get_u32();
553 let mut record_step = 0;
554 if var_tag == 0 {
555 let ensure_empty = self.get_u32();
556 if ensure_empty != 0 {
557 panic!("wrong empty tag for list of variables");
558 }
559 } else {
560 if var_tag != NC_VARIABLE {
561 panic!("wrong tag for list of variables");
562 }
563 let var_size = self.get_u32();
565 let mut v_idx = 0;
566 while v_idx < var_size {
567 let name = self.get_name();
569 let dimensionality = self.get_u32();
570 let mut dimensions_ids = vec![];
571 let mut dim = 0;
572 while dim < dimensionality {
573 dimensions_ids.push(self.get_u32());
574 dim += 1;
575 }
576 let attributes = self.build_attributes();
578 let r#type: CDFDataType = self.get_u32().into();
580 let var_size = self.get_u32();
584 let offset = self.get_offset();
586 let mut record = false;
587 if !dimensions_ids.is_empty()
589 && dimensions_ids[0] == self.record_dimension.id.unwrap_or_default()
590 {
591 record_step += var_size;
592 record = true;
593 }
594 self.variables.push(CDFVariable {
595 name,
596 dimensions: dimensions_ids
597 .iter()
598 .map(|id| self.dimensions[*id as usize].clone())
599 .collect(),
600 attributes,
601 r#type,
602 size: var_size,
603 offset,
604 record,
605 });
606 v_idx += 1;
607 }
608 }
609 self.record_dimension.record_step = Some(record_step);
610 }
611
612 fn get_non_record(&self, variable: CDFVariable) -> Vec<CDFValue> {
620 let CDFVariable { size, r#type, .. } = variable;
622 let total_size = size / netcdf_type_to_bytes(r#type);
624 let mut data = vec![];
626 let mut i = 0;
627 while i < total_size {
628 data.push(self.get_type(r#type, 1));
629 i += 1;
630 }
631
632 data
633 }
634
635 fn get_record(&self, variable: CDFVariable) -> Vec<CDFValue> {
643 let CDFRecordDimension { record_step, size: total_size, .. } = self.record_dimension;
645 let CDFVariable { size, r#type, .. } = variable;
646 let width = if size != 0 { size / netcdf_type_to_bytes(r#type) } else { 1 };
647
648 if record_step.is_none() {
649 panic!("record_dimension.record_step is undefined");
650 }
651 let record_step = record_step.unwrap();
652
653 let mut data = vec![];
655 let mut i = 0;
656 while i < total_size {
657 let current_offset = *self.cursor.borrow();
658 data.push(self.get_type(r#type, width));
659 *self.cursor.borrow_mut() = current_offset + record_step;
660 i += 1;
661 }
662
663 data
664 }
665
666 fn padding(&self) {
668 let cursor = *self.cursor.borrow();
669 if !cursor.is_multiple_of(4) {
670 *self.cursor.borrow_mut() += 4 - (cursor % 4);
671 }
672 }
673}
674
675#[derive(Debug)]
677pub struct CDFIterator<'a, T: Reader> {
678 reader: &'a NetCDFReader<T>,
679 index: u64,
680 len: u64,
681}
682impl<T: Reader> Iterator for CDFIterator<'_, T> {
683 type Item = CDFVectorFeature;
684
685 fn next(&mut self) -> Option<Self::Item> {
686 let cdf_reader = &self.reader;
687 if self.index >= self.len {
688 None
689 } else if let Some(point) = cdf_reader.get_feature(self.index) {
690 self.index += 1;
691 Some(point)
692 } else {
693 None
694 }
695 }
696}
697impl<T: Reader> FeatureReader<(), Properties, MValue> for NetCDFReader<T> {
699 type FeatureIterator<'a>
700 = CDFIterator<'a, T>
701 where
702 T: 'a;
703
704 fn iter(&self) -> Self::FeatureIterator<'_> {
705 CDFIterator { reader: self, index: 0, len: self.len() }
706 }
707
708 fn par_iter(&self, pool_size: usize, thread_id: usize) -> Self::FeatureIterator<'_> {
709 let pool_size = pool_size as u64;
710 let thread_id = thread_id as u64;
711 let start = self.len() * thread_id / pool_size;
712 let end = self.len() * (thread_id + 1) / pool_size;
713 CDFIterator { reader: self, index: start, len: end }
714 }
715}