gistools/readers/osm/
primitive.rs

1use super::{
2    info::InfoBlock,
3    node::{DenseNodes, Node},
4    relation::{IntermediateNodeMember, MemberType, Relation},
5    way::Way,
6};
7use crate::data_structures::HasLayer;
8use alloc::{string::String, vec::Vec};
9use pbf::{ProtoRead, Protobuf};
10use s2json::Properties;
11
12/// A metadata struct for relations
13#[derive(Debug, Clone, PartialEq)]
14pub struct OSMMetadataRelation {
15    /// The role of the relation related to the VectorFeature
16    pub role: String,
17    /// The properties of the relation
18    pub properties: Properties,
19}
20
21/// The expected metadata in the VectorFeature for all types (node, way, relation)
22#[derive(Debug, Default, Clone, PartialEq)]
23pub struct OSMMetadata {
24    /// The type of the VectorFeature
25    pub osm_type: MemberType,
26    /// the info block describing the data storage history
27    pub info: Option<InfoBlock>,
28    /// the nodes that make up the VectorFeature
29    pub nodes: Option<Vec<IntermediateNodeMember>>,
30    /// if this feature is part of a relation, this relation will describe the other components
31    pub relation: Option<OSMMetadataRelation>,
32}
33impl HasLayer for OSMMetadata {
34    fn get_layer(&self) -> Option<String> {
35        None
36    }
37}
38
39/// NOTE: currently relations are stored, but we don't wait for the Block to store all relations
40/// before we start testing primtiveHandle against the data. This is a problem because
41/// relations reference eachother at times, and we need to be able to resolve those references
42/// before we can run relationHandle against the data. This isn't an important issue since
43/// in practice, all relations that reference eachother often produce garbage or unusable data.
44/// But it would be *nice* to fix this. Morbidly enough, the "BEST" solution is to treat relations
45/// like we do nodes and ways since relations could possibly reference eachother outside their own block.
46/// From a practical standpoint, I can't see this being worth the effort or memory/time cost.
47#[derive(Debug)]
48pub struct PrimitiveBlock {
49    stringtable: StringTable,
50    /// Primitive groups are smaller collections of the nodes, ways and relations
51    pub primitive_groups: Vec<PrimitiveGroup>,
52    /// Granularity, units of nanodegrees, used to store coordinates in this block.
53    pub granularity: i32,
54    /// Offset value between the output coordinates and the granularity grid in units of nanodegrees.
55    pub lat_offset: i64,
56    /// Offset value between the output coordinates and the granularity grid in units of nanodegrees.
57    pub lon_offset: i64,
58    /// Granularity of dates, normally represented in units of milliseconds since the 1970 epoch.
59    pub date_granularity: i32,
60}
61impl Default for PrimitiveBlock {
62    fn default() -> Self {
63        Self {
64            stringtable: StringTable::default(),
65            primitive_groups: Vec::new(),
66            granularity: 100,
67            lat_offset: 0,
68            lon_offset: 0,
69            date_granularity: 1000,
70        }
71    }
72}
73impl PrimitiveBlock {
74    /// Get a string from the string table
75    pub fn get_string(&self, index: usize) -> &str {
76        self.stringtable.get(index)
77    }
78
79    /// Get a record of strings from the string table
80    pub fn tags(&self, keys: &[u32], values: &[u32]) -> Properties {
81        let mut res = Properties::default();
82        // zip the keys and values and put them in the map
83        for (key, value) in keys.iter().zip(values.iter()) {
84            let key = self.get_string(*key as usize);
85            let value = self.get_string(*value as usize);
86            res.insert(key.into(), value.into());
87        }
88        res
89    }
90}
91/// Read in the contents of the primitive block
92impl ProtoRead for PrimitiveBlock {
93    fn read(&mut self, tag: u64, pb: &mut Protobuf) {
94        match tag {
95            1 => pb.read_message(&mut self.stringtable),
96            2 => {
97                let mut group = PrimitiveGroup::default();
98                pb.read_message(&mut group);
99                self.primitive_groups.push(group);
100            }
101            17 => self.granularity = pb.read_varint(),
102            18 => self.date_granularity = pb.read_varint(),
103            19 => self.lat_offset = pb.read_varint(),
104            20 => self.lon_offset = pb.read_varint(),
105            _ => panic!("unknown tag {}", tag),
106        }
107    }
108}
109
110/// Group of OSMPrimitives. All primitives in a group must be the same type.
111#[derive(Debug, Default)]
112pub struct PrimitiveGroup {
113    /// Nodes (points)
114    pub nodes: Vec<Node>,
115    /// Ways (lines and polygons)
116    pub ways: Vec<Way>,
117    /// Relations - collections of nodes, ways and relations
118    pub relations: Vec<Relation>,
119    /// Changesets
120    pub changesets: Vec<ChangeSet>,
121}
122/// Read in the contents of the primitive block
123impl ProtoRead for PrimitiveGroup {
124    fn read(&mut self, tag: u64, pb: &mut Protobuf) {
125        match tag {
126            1 => {
127                let mut node = Node::default();
128                pb.read_message(&mut node);
129                self.nodes.push(node);
130            }
131            2 => {
132                let mut dense_nodes = DenseNodes::default();
133                pb.read_message(&mut dense_nodes);
134                self.nodes.extend(dense_nodes.nodes());
135            }
136            3 => {
137                let mut way = Way::default();
138                pb.read_message(&mut way);
139                self.ways.push(way);
140            }
141            4 => {
142                let mut relation = Relation::default();
143                pb.read_message(&mut relation);
144                self.relations.push(relation);
145            }
146            5 => {
147                let mut changeset = ChangeSet::default();
148                pb.read_message(&mut changeset);
149                self.changesets.push(changeset);
150            }
151            _ => panic!("unknown tag {}", tag),
152        }
153    }
154}
155
156/// String table, contains the common strings in each block.
157/// Note that we reserve index '0' as a delimiter, so the entry at that
158/// index in the table is ALWAYS blank and unused.
159/// NOTE: OSM isn't safe and allows " inside of strings, so we have to replace them with '
160/// NOTE: OSM isn't safe and allows \ at the end of strings, so we have to remove them so it can be properly parsed.
161#[derive(Debug, Default)]
162pub struct StringTable {
163    strings: Vec<String>,
164    empty_string: String,
165}
166impl StringTable {
167    /// Get a string from the string table
168    pub fn get(&self, index: usize) -> &str {
169        self.strings.get(index).unwrap_or(&self.empty_string)
170    }
171}
172/// Read in the contents of the header block
173impl ProtoRead for StringTable {
174    fn read(&mut self, tag: u64, pb: &mut Protobuf) {
175        match tag {
176            1 => self.strings.push(pb.read_string()),
177            _ => panic!("unknown tag {}", tag),
178        }
179    }
180}
181
182/// This is kept for backwards compatibility but not used anywhere.
183#[derive(Debug, Default)]
184pub struct ChangeSet {
185    /// The id of the changeset
186    pub id: i64,
187}
188/// Read in the contents of the header block
189impl ProtoRead for ChangeSet {
190    fn read(&mut self, tag: u64, pb: &mut Protobuf) {
191        match tag {
192            1 => self.id = pb.read_varint(),
193            _ => panic!("unknown tag {}", tag),
194        }
195    }
196}