gistools/readers/osm/
node.rs

1use super::{
2    OSMFilterable, OSMReader, OSMTagFilterType,
3    info::{DenseInfo, Info, InfoBlock},
4    primitive::{OSMMetadata, PrimitiveBlock},
5    relation::{IntermediateRelation, MemberType},
6    way::{IntermediateWay, WayNodes},
7};
8use crate::{data_store::kv::KVStore, parsers::Reader};
9use alloc::{string::String, vec, vec::Vec};
10use pbf::{ProtoRead, Protobuf};
11use s2json::{
12    BBox3D, MValue, Properties, VectorFeature, VectorFeatureType, VectorGeometry, VectorPoint,
13};
14use serde::{Deserialize, Serialize};
15
16/// Intermediate node
17#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
18pub struct IntermediateNode {
19    /// The node id
20    pub id: u64,
21    /// The nodes longitude
22    pub point: VectorPoint<MValue>,
23    /// The key-value pairs of the node
24    pub properties: Properties,
25    /// The node metadata
26    pub info: Option<InfoBlock>,
27}
28impl IntermediateNode {
29    /// Convert the node to a vector feature
30    pub fn to_vector_feature(
31        &self,
32        add_bbox: bool,
33    ) -> VectorFeature<OSMMetadata, Properties, MValue> {
34        let coordinates = self.point.clone();
35        let bbox = if add_bbox { Some(BBox3D::from_point(&coordinates)) } else { None };
36        VectorFeature {
37            id: Some(self.id),
38            face: 0.into(),
39            _type: VectorFeatureType::VectorFeature,
40            properties: self.properties.clone(),
41            geometry: VectorGeometry::new_point(coordinates, bbox),
42            metadata: Some(OSMMetadata {
43                osm_type: MemberType::Node,
44                info: self.info.clone(),
45                nodes: None,
46                relation: None,
47            }),
48        }
49    }
50}
51
52/// Node class
53/// contains a single node.
54#[derive(Debug, Default, PartialEq)]
55pub struct Node {
56    /// The node id
57    pub id: u64,
58    /// The node info block if any. May be a dead info block
59    info: Option<Info>,
60    /// The nodes latitude
61    lat: i64,
62    /// The nodes longitude
63    lon: i64,
64    /// The keys of the node
65    keys: Vec<u32>,
66    /// The values of the node
67    vals: Vec<u32>,
68}
69impl OSMFilterable for Node {
70    fn is_filterable<
71        T: Reader,
72        _N: KVStore<u64, VectorPoint<MValue>>,
73        N: KVStore<u64, IntermediateNode>,
74        _W: KVStore<u64, WayNodes>,
75        W: KVStore<u64, IntermediateWay>,
76        R: KVStore<u64, IntermediateRelation>,
77    >(
78        &self,
79        pb: &PrimitiveBlock,
80        reader: &mut OSMReader<T, _N, N, _W, W, R>,
81    ) -> bool {
82        if reader.skip_nodes || (reader.skip_empty_nodes && self.keys.is_empty()) {
83            return true;
84        }
85        if let Some(tag_filter) = &mut reader.tag_filter {
86            for i in 0..self.keys.len() {
87                let key_str = pb.get_string(self.keys[i] as usize);
88                let val_str = pb.get_string(self.vals[i] as usize);
89                if tag_filter.match_found(OSMTagFilterType::Node, key_str, val_str) {
90                    return false;
91                }
92            }
93            // if we make it here, we didn't find any matching tags
94            return true;
95        }
96
97        false
98    }
99}
100impl Node {
101    /// Get the lon and lat of the node
102    pub fn get_lon_lat(&self, pb: &PrimitiveBlock) -> (f64, f64) {
103        let lon_offset = pb.lon_offset as f64;
104        let lat_offset = pb.lat_offset as f64;
105        let granularity = pb.granularity as f64;
106        (
107            0.000000001 * (lon_offset + granularity * self.lon as f64),
108            0.000000001 * (lat_offset + granularity * self.lat as f64),
109        )
110    }
111
112    /// Get the properties of the node
113    pub fn properties(&self, pb: &PrimitiveBlock) -> Properties {
114        pb.tags(&self.keys, &self.vals)
115    }
116
117    /// Gain access to the nodes geometry
118    pub fn to_vector_geometry(&self, pb: &PrimitiveBlock) -> VectorPoint<MValue> {
119        let (lon, lat) = self.get_lon_lat(pb);
120        let z = get_elevation(&self.properties(pb));
121        VectorPoint::new(lon, lat, z, None)
122    }
123
124    /// Converts the way to an intermediate vector feature (way's nodes have not been parsed)
125    ///
126    /// ## Returns
127    /// The way as an intermediate vector feature
128    pub fn to_intermediate_feature(&self, pb: &PrimitiveBlock) -> IntermediateNode {
129        let point = self.to_vector_geometry(pb);
130        IntermediateNode {
131            id: self.id,
132            point,
133            properties: self.properties(pb),
134            info: self.info.as_ref().map(|info| info.to_block(pb)),
135        }
136    }
137}
138/// Read in the contents of the Node
139impl ProtoRead for Node {
140    fn read(&mut self, tag: u64, pb: &mut Protobuf) {
141        match tag {
142            1 => self.id = pb.read_varint(),
143            2 => self.keys = pb.read_packed(),
144            3 => self.vals = pb.read_packed(),
145            4 => {
146                let mut info = Info::default();
147                pb.read_message(&mut info);
148                self.info = Some(info);
149            }
150            8 => self.lat = pb.read_s_varint(),
151            9 => self.lon = pb.read_s_varint(),
152            _ => panic!("unknown tag {}", tag),
153        }
154    }
155}
156
157/// Used to densly represent a sequence of nodes that do not have any tags.
158/// We represent these nodes columnwise as five columns: ID's, lats, and
159/// lons, all delta coded. When metadata is not omitted,
160/// We encode keys & vals for all nodes as a single array of integers
161/// containing key-stringid and val-stringid, using a stringid of 0 as a
162/// delimiter between nodes.
163///
164/// `( (<keyid> <valid>)* '0' )*`
165#[derive(Debug, Default, PartialEq)]
166pub struct DenseNodes {
167    ids: Vec<i64>, // DELTA coded
168    denseinfo: Option<DenseInfo>,
169    lats: Vec<i64>, // DELTA coded
170    lons: Vec<i64>, // DELTA coded
171    // Special packing of keys and vals into one array. May be empty if all nodes in this block are tagless.
172    keys_vals: Vec<i64>,
173}
174impl DenseNodes {
175    /// Access the nodes in this block
176    pub fn nodes(&self) -> Vec<Node> {
177        let mut res: Vec<Node> = vec![];
178        let info_map = self.denseinfo.as_ref().map(|info| info.infos()).unwrap_or_default();
179        let mut j = 0;
180        let mut cur_id = 0;
181        let mut cur_lat = 0;
182        let mut cur_lon = 0;
183        for i in 0..self.ids.len() {
184            let cur_info = info_map.get(i);
185            cur_id += self.ids[i];
186            cur_lat += self.lats[i];
187            cur_lon += self.lons[i];
188            let mut keys: Vec<u32> = vec![];
189            let mut vals: Vec<u32> = vec![];
190            if !self.keys_vals.is_empty() {
191                while self.keys_vals[j] != 0 {
192                    keys.push(self.keys_vals[j] as u32);
193                    vals.push(self.keys_vals[j + 1] as u32);
194                    j += 2;
195                }
196                j += 1;
197            }
198
199            res.push(Node {
200                id: cur_id as u64,
201                keys,
202                vals,
203                info: cur_info.cloned(),
204                lat: cur_lat,
205                lon: cur_lon,
206            });
207        }
208
209        res
210    }
211}
212/// Read in the contents of the Dense Nodes
213impl ProtoRead for DenseNodes {
214    fn read(&mut self, tag: u64, pb: &mut Protobuf) {
215        match tag {
216            1 => self.ids = pb.read_s_packed(),
217            5 => {
218                let mut info = DenseInfo::default();
219                pb.read_message(&mut info);
220                self.denseinfo = Some(info);
221            }
222            8 => self.lats = pb.read_s_packed(),
223            9 => self.lons = pb.read_s_packed(),
224            10 => self.keys_vals = pb.read_packed(),
225            _ => panic!("unknown tag {}", tag),
226        }
227    }
228}
229
230/// returns the altitude assuming it is in meters
231fn get_elevation(props: &Properties) -> Option<f64> {
232    for s in ["altitude", "ele", "elevation", "height", "depth"] {
233        if let Some(elevation) = props.get(s) {
234            let val = parse_altitude(&elevation.to_prim().unwrap().to_string().unwrap());
235            if let Some(val) = val {
236                if s == "depth" {
237                    return Some(-val);
238                }
239                return Some(val);
240            }
241        }
242    }
243    None
244}
245
246/// returns the altitude assuming it is in meters
247fn parse_altitude(alt: &str) -> Option<f64> {
248    let digits: String = alt.chars().filter(|c| c.is_ascii_digit()).collect();
249    if digits.is_empty() { None } else { digits.parse().ok() }
250}