gistools/readers/osm/
way.rs

1use super::{
2    OSMFilterable, OSMReader, OSMTagFilterType,
3    info::{Info, InfoBlock},
4    node::IntermediateNode,
5    primitive::{OSMMetadata, PrimitiveBlock},
6    relation::{IntermediateRelation, MemberType},
7};
8use crate::{data_store::kv::KVStore, parsers::Reader};
9use alloc::{vec, vec::Vec};
10use pbf::{ProtoRead, Protobuf};
11use s2json::{
12    BBox3D, MValue, Properties, VectorFeature, VectorFeatureType, VectorGeometry, VectorLineString,
13    VectorPoint,
14};
15use serde::{Deserialize, Serialize};
16
17/// Linebased node reference store
18pub type WayNodes = Vec<u64>;
19
20/// An intermediate vector feature where the way nodes haven't been resolved yet.
21#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
22pub struct IntermediateWay {
23    /// The way's ID
24    pub id: u64,
25    /// The way's properties
26    pub properties: Properties,
27    /// Optional metadata
28    pub info: Option<InfoBlock>,
29    /// The way's nodes IDs to be resolved
30    pub way_nodes: WayNodes,
31    /// Whether the way is an area
32    pub is_area: bool,
33}
34impl IntermediateWay {
35    /// Convert the node to a vector feature
36    pub fn to_vector_feature<_N: KVStore<u64, VectorPoint<MValue>>>(
37        &self,
38        node_geometry: &_N,
39        add_bbox: bool,
40    ) -> VectorFeature<OSMMetadata, Properties, MValue> {
41        let IntermediateWay { id, is_area, way_nodes, properties, info } = &self;
42        let mut bbox = BBox3D::default();
43        // build line
44        let mut vector_line: VectorLineString<MValue> = vec![];
45        for way_node in way_nodes {
46            let node = node_geometry.get(*way_node);
47            if let Some(node) = node {
48                if add_bbox {
49                    bbox.extend_from_point(node)
50                }
51                vector_line.push(node.clone());
52            }
53        }
54        // build geometry
55        let bbox = if add_bbox { Some(bbox) } else { None };
56        let geometry = match is_area {
57            true => VectorGeometry::new_polygon(vec![vector_line], bbox),
58            false => VectorGeometry::new_linestring(vector_line, bbox),
59        };
60
61        VectorFeature {
62            id: Some(*id),
63            face: 0.into(),
64            _type: VectorFeatureType::VectorFeature,
65            properties: properties.clone(),
66            geometry,
67            metadata: Some(OSMMetadata {
68                osm_type: MemberType::Way,
69                info: info.clone(),
70                nodes: None,
71                relation: None,
72            }),
73        }
74    }
75}
76
77/// Way Class
78#[derive(Debug, Default, PartialEq)]
79pub struct Way {
80    /// The way's ID
81    pub id: u64,
82    info: Option<Info>,
83    // Parallel arrays
84    keys: Vec<u32>,
85    vals: Vec<u32>,
86    // DELTA coded
87    refs: Vec<i64>,
88    // Optional infield lat-lon
89    // NOTE: I'm not going to bother implementing this, I've never seen it used.
90    //   lats: Vec<i32>, // optional DELTA coded
91    //   lons: Vec<i32>, // optional DELTA coded */
92}
93impl Way {
94    /// Get the properties of the node
95    pub fn properties(&self, pb: &PrimitiveBlock) -> Properties {
96        pb.tags(&self.keys, &self.vals)
97    }
98
99    /// Checks if the way is an area based on it's key-value pairs
100    pub fn is_area<
101        T: Reader,
102        _N: KVStore<u64, VectorPoint<MValue>>,
103        N: KVStore<u64, IntermediateNode>,
104        _W: KVStore<u64, WayNodes>,
105        W: KVStore<u64, IntermediateWay>,
106        R: KVStore<u64, IntermediateRelation>,
107    >(
108        &self,
109        pb: &PrimitiveBlock,
110        reader: &mut OSMReader<T, _N, N, _W, W, R>,
111    ) -> bool {
112        if (reader.upgrade_ways_to_areas
113            && self.refs.len() >= 4
114            && self.refs[0] == self.refs[self.refs.len() - 1])
115            || self.has_key_value(pb, "area", Some("yes"))
116        {
117            return true;
118        }
119        false
120    }
121
122    /// Checks if the way has a key-value pair (value optional)
123    pub fn has_key_value(&self, pb: &PrimitiveBlock, key: &str, val: Option<&str>) -> bool {
124        for i in 0..self.keys.len() {
125            if pb.get_string(self.keys[i] as usize) == key {
126                match val {
127                    None => return true,
128                    Some(v) => {
129                        if pb.get_string(self.vals[i] as usize) == v {
130                            return true;
131                        }
132                    }
133                }
134            }
135        }
136        false
137    }
138
139    /// Access the way's node IDs associated with this way
140    pub fn node_refs(&self) -> WayNodes {
141        let mut res = vec![];
142        let mut _ref = 0;
143        // for (let i = 0; i < this.#refs.length; i++) {
144        for i in 0..self.refs.len() {
145            _ref += self.refs[i];
146            res.push(_ref as u64);
147        }
148        res
149    }
150
151    /// Converts the way to an intermediate vector feature (way's nodes have not been parsed)
152    ///
153    /// ## Returns
154    /// The way as an intermediate vector feature
155    pub fn to_intermediate_feature<
156        T: Reader,
157        _N: KVStore<u64, VectorPoint<MValue>>,
158        N: KVStore<u64, IntermediateNode>,
159        _W: KVStore<u64, WayNodes>,
160        W: KVStore<u64, IntermediateWay>,
161        R: KVStore<u64, IntermediateRelation>,
162    >(
163        &self,
164        pb: &PrimitiveBlock,
165        reader: &mut OSMReader<T, _N, N, _W, W, R>,
166    ) -> Option<IntermediateWay> {
167        let is_area = self.is_area(pb, reader);
168        let way_nodes = self.node_refs();
169        if way_nodes.len() < 2 {
170            None
171        } else {
172            Some(IntermediateWay {
173                id: self.id,
174                is_area,
175                properties: self.properties(pb),
176                way_nodes,
177                info: self.info.as_ref().map(|info| info.to_block(pb)),
178            })
179        }
180    }
181}
182impl OSMFilterable for Way {
183    fn is_filterable<
184        T: Reader,
185        _N: KVStore<u64, VectorPoint<MValue>>,
186        N: KVStore<u64, IntermediateNode>,
187        _W: KVStore<u64, WayNodes>,
188        W: KVStore<u64, IntermediateWay>,
189        R: KVStore<u64, IntermediateRelation>,
190    >(
191        &self,
192        pb: &PrimitiveBlock,
193        reader: &mut OSMReader<T, _N, N, _W, W, R>,
194    ) -> bool {
195        if reader.skip_ways {
196            return true;
197        }
198        if let Some(tag_filter) = &mut reader.tag_filter {
199            for i in 0..self.keys.len() {
200                let key_str = pb.get_string(self.keys[i] as usize);
201                let val_str = pb.get_string(self.vals[i] as usize);
202                if tag_filter.match_found(OSMTagFilterType::Way, key_str, val_str) {
203                    return false;
204                }
205            }
206            // if we make it here, we didn't find any matching tags
207            return true;
208        }
209        false
210    }
211}
212/// Read in the contents of the way
213impl ProtoRead for Way {
214    fn read(&mut self, tag: u64, pb: &mut Protobuf) {
215        match tag {
216            1 => self.id = pb.read_varint(),
217            2 => self.keys = pb.read_packed(),
218            3 => self.vals = pb.read_packed(),
219            4 => {
220                let mut info = Info::default();
221                pb.read_message(&mut info);
222                self.info = Some(info);
223            }
224            8 => self.refs = pb.read_s_packed(),
225            // skip, not used.
226            9 | 10 => (),
227            _ => panic!("unknown tag {}", tag),
228        }
229    }
230}