1use strum::{EnumCount as _, IntoEnumIterator as _};
2
3use crate::v01::sort::{reorder_features, spatial_sort_likely_to_help};
4use crate::v01::{
5 EncodeProperties as _, EncodedLayer01, GeometryEncoder, GeometryProfile, IdEncoder, IdProfile,
6 PropertyEncoder, PropertyProfile, SortStrategy, StagedLayer01, TileLayer01,
7};
8use crate::{MltError, MltResult};
9
10impl StagedLayer01 {
11 pub fn encode(self, encoder: StagedLayer01Encoder) -> MltResult<EncodedLayer01> {
13 let geometry = self.geometry.encode(encoder.geometry)?;
14
15 let id = match (encoder.id, self.id) {
16 (Some(id_enc), Some(id)) => id.encode(id_enc)?,
17 (None, Some(id)) => id.encode_auto()?.0,
18 _ => None,
19 };
20
21 let properties = self.properties.encode(encoder.properties)?;
22
23 Ok(EncodedLayer01 {
24 name: self.name,
25 extent: self.extent,
26 id,
27 geometry,
28 properties,
29 #[cfg(fuzzing)]
30 layer_order: vec![],
31 })
32 }
33
34 pub fn encode_with_profile(
39 self,
40 profile: &Tag01Profile,
41 ) -> Result<(EncodedLayer01, StagedLayer01Encoder), MltError> {
42 let (geometry, geom_enc) = self.geometry.encode_with_profile(&profile.geometry)?;
43
44 let id_enc;
45 let id;
46 if let Some(parsed_id) = self.id {
47 let (enc_id, enc) = parsed_id.encode_with_profile(&profile.id)?;
48 id = enc_id;
49 id_enc = enc;
50 } else {
51 id = None;
52 id_enc = None;
53 }
54
55 let (properties, props_enc) = self.properties.encode_with_profile(&profile.properties)?;
56
57 let encoder = StagedLayer01Encoder {
58 id: id_enc,
59 properties: props_enc,
60 geometry: geom_enc,
61 };
62
63 Ok((
64 EncodedLayer01 {
65 name: self.name,
66 extent: self.extent,
67 id,
68 geometry,
69 properties,
70 #[cfg(fuzzing)]
71 layer_order: vec![],
72 },
73 encoder,
74 ))
75 }
76
77 pub fn encode_auto(self) -> Result<(EncodedLayer01, StagedLayer01Encoder), MltError> {
82 let (geom_enc_result, geom_enc) = self.geometry.encode_auto()?;
83 let (id_enc_result, id_enc) = match self.id {
84 Some(parsed_id) => parsed_id.encode_auto()?,
85 None => (None, None),
86 };
87 let (encoded_properties, props_enc) = self.properties.encode_auto()?;
88
89 let stream_encoder = StagedLayer01Encoder {
90 id: id_enc,
91 properties: props_enc,
92 geometry: geom_enc,
93 };
94
95 let layer = EncodedLayer01 {
96 name: self.name,
97 extent: self.extent,
98 id: id_enc_result,
99 geometry: geom_enc_result,
100 properties: encoded_properties,
101 #[cfg(fuzzing)]
102 layer_order: vec![],
103 };
104
105 Ok((layer, stream_encoder))
106 }
107}
108
109const SORT_TRIAL_THRESHOLD: usize = 512;
112
113const TRIAL_STRATEGIES: [Option<SortStrategy>; 3] = [
115 None,
116 Some(SortStrategy::SpatialMorton),
117 Some(SortStrategy::Id),
118];
119
120#[derive(Debug, Clone)]
126pub struct StagedLayer01Encoder {
127 pub id: Option<IdEncoder>,
128 pub properties: Vec<PropertyEncoder>,
129 pub geometry: GeometryEncoder,
130}
131
132#[derive(Debug, Clone, Default)]
137pub struct Tile01Encoder {
138 pub sort_strategy: Option<SortStrategy>,
141}
142
143impl Tile01Encoder {
144 pub fn encode(&self, data: &mut TileLayer01) -> StagedLayer01 {
147 reorder_features(data, self.sort_strategy);
148 StagedLayer01::from(data.clone())
149 }
150
151 pub fn encode_auto(
170 source: &TileLayer01,
171 ) -> Result<(EncodedLayer01, StagedLayer01Encoder), MltError> {
172 struct TrialResult {
173 layer: EncodedLayer01,
174 stream_enc: StagedLayer01Encoder,
175 byte_count: usize,
176 }
177
178 let n = source.features.len();
179
180 let filtered: [Option<SortStrategy>; 2];
181 let candidates: &[Option<SortStrategy>] =
182 if n < SORT_TRIAL_THRESHOLD || spatial_sort_likely_to_help(source) {
183 &TRIAL_STRATEGIES
184 } else {
185 filtered = [None, Some(SortStrategy::Id)];
186 &filtered
187 };
188
189 let mut best: Option<TrialResult> = None;
190
191 for &strategy in candidates {
192 let mut trial_source = source.clone();
193 reorder_features(&mut trial_source, strategy);
194 let trial_staged = StagedLayer01::from(trial_source);
195
196 let (trial_layer, trial_stream_enc) = trial_staged.encode_auto()?;
197
198 let mut buf: Vec<u8> = Vec::new();
199 trial_layer.write_to(&mut buf)?;
200 let byte_count = buf.len();
201
202 if best.as_ref().is_none_or(|b| byte_count < b.byte_count) {
203 best = Some(TrialResult {
204 layer: trial_layer,
205 stream_enc: trial_stream_enc,
206 byte_count,
207 });
208 }
209 }
210
211 let winner = best.unwrap();
213 Ok((winner.layer, winner.stream_enc))
214 }
215}
216
217fn sort_strategy_index(s: Option<SortStrategy>) -> usize {
219 match s {
220 None => 0,
221 Some(s) => {
222 1 + SortStrategy::iter()
223 .position(|v| v == s)
224 .expect("variant must be present in iter()")
225 }
226 }
227}
228
229#[derive(Debug, Clone)]
232pub struct Tag01Profile {
233 strategy_votes: [u32; SortStrategy::COUNT + 1],
234 pub id: IdProfile,
235 pub properties: PropertyProfile,
236 pub geometry: GeometryProfile,
237}
238
239impl Tag01Profile {
240 #[must_use]
241 pub fn new(
242 sort_strategy: Option<SortStrategy>,
243 id: IdProfile,
244 properties: PropertyProfile,
245 geometry: GeometryProfile,
246 ) -> Self {
247 let mut strategy_votes = [0u32; SortStrategy::COUNT + 1];
248 strategy_votes[sort_strategy_index(sort_strategy)] = 1;
249 Self {
250 strategy_votes,
251 id,
252 properties,
253 geometry,
254 }
255 }
256
257 #[must_use]
258 pub fn sort_strategy(&self) -> Option<SortStrategy> {
259 let winner_idx = self
260 .strategy_votes
261 .iter()
262 .enumerate()
263 .fold(0usize, |best, (i, &v)| {
264 if v > self.strategy_votes[best] {
265 i
266 } else {
267 best
268 }
269 });
270 if winner_idx == 0 {
271 None
272 } else {
273 SortStrategy::iter().nth(winner_idx - 1)
274 }
275 }
276
277 pub fn set_sort_strategy(&mut self, strategy: Option<SortStrategy>) {
278 self.strategy_votes = [0u32; SortStrategy::COUNT + 1];
279 self.strategy_votes[sort_strategy_index(strategy)] = 1;
280 }
281
282 #[must_use]
283 pub fn merge(self, other: &Self) -> Self {
284 let mut votes = self.strategy_votes;
285 for (v, &o) in votes.iter_mut().zip(other.strategy_votes.iter()) {
286 *v = v.saturating_add(o);
287 }
288 Self {
289 strategy_votes: votes,
290 id: self.id.merge(&other.id),
291 properties: self.properties.merge(&other.properties),
292 geometry: self.geometry.merge(&other.geometry),
293 }
294 }
295}