1use std::{borrow::Cow, f64::consts::PI};
5
6use geozero::{FeatureProcessor, GeomProcessor, GeozeroDatasource, PropertyProcessor};
7use thiserror::Error;
8
9use crate::{
10 coords::{WorldTileCoords, EXTENT},
11 io::apc::{Context, SendError},
12 sdf::{tessellation::TextTessellator, tessellation_new::TextTessellatorNew},
13 style::layer::{LayerPaint, StyleLayer},
14 vector::{
15 tessellation::{IndexDataType, ZeroTessellator},
16 transferables::{
17 LayerMissing, LayerTessellated, SymbolLayerTessellated, TileTessellated,
18 VectorTransferables,
19 },
20 },
21};
22
23#[derive(Error, Debug)]
24pub enum ProcessGeoJsonError {
25 #[error("sending data back through context failed")]
26 SendError(SendError),
27 #[error("GeoJSON parsing failed: {0}")]
28 Parse(Cow<'static, str>),
29}
30
31pub struct ProjectingTessellator<T> {
34 inner: T,
35 tile_x: i32,
36 tile_y: i32,
37 zoom: u8,
38 project: bool,
39}
40
41impl<T> ProjectingTessellator<T> {
42 pub fn new(coords: WorldTileCoords, project: bool, inner: T) -> Self {
43 Self {
44 inner,
45 tile_x: coords.x,
46 tile_y: coords.y,
47 zoom: u8::from(coords.z),
48 project,
49 }
50 }
51
52 fn project(&self, lon: f64, lat: f64) -> (f64, f64) {
54 let lat = lat.clamp(-85.05112877980659, 85.05112877980659);
55 let scale = (1u64 << self.zoom) as f64;
56 let mx = (180.0 + lon) / 360.0;
57 let my = (180.0 - (180.0 / PI * ((PI / 4.0 + lat * PI / 360.0).tan()).ln())) / 360.0;
58 let x = (mx * scale - self.tile_x as f64) * EXTENT;
59 let y = (my * scale - self.tile_y as f64) * EXTENT;
60 (x, y)
61 }
62
63 pub fn into_inner(self) -> T {
64 self.inner
65 }
66}
67
68impl<T: GeomProcessor> GeomProcessor for ProjectingTessellator<T> {
69 fn xy(&mut self, x: f64, y: f64, idx: usize) -> geozero::error::Result<()> {
70 if x.is_nan() || y.is_nan() {
71 println!(
72 "ProjectingTessellator received NaN Input! x={}, y={}, idx={}",
73 x, y, idx
74 );
75 }
76 let (tx, ty) = self.project(x, y);
77 if !tx.is_finite() || !ty.is_finite() {
78 println!(
79 "ProjectingTessellator output non-finite! lon={}, lat={} -> tx={}, ty={}",
80 x, y, tx, ty
81 );
82 }
83 self.inner.xy(tx, ty, idx)
84 }
85
86 fn point_begin(&mut self, idx: usize) -> geozero::error::Result<()> {
87 self.inner.point_begin(idx)
88 }
89
90 fn point_end(&mut self, idx: usize) -> geozero::error::Result<()> {
91 self.inner.point_end(idx)
92 }
93
94 fn multipoint_begin(&mut self, size: usize, idx: usize) -> geozero::error::Result<()> {
95 self.inner.multipoint_begin(size, idx)
96 }
97
98 fn multipoint_end(&mut self, idx: usize) -> geozero::error::Result<()> {
99 self.inner.multipoint_end(idx)
100 }
101
102 fn linestring_begin(
103 &mut self,
104 tagged: bool,
105 size: usize,
106 idx: usize,
107 ) -> geozero::error::Result<()> {
108 self.inner.linestring_begin(tagged, size, idx)
109 }
110
111 fn linestring_end(&mut self, tagged: bool, idx: usize) -> geozero::error::Result<()> {
112 self.inner.linestring_end(tagged, idx)
113 }
114
115 fn multilinestring_begin(&mut self, size: usize, idx: usize) -> geozero::error::Result<()> {
116 self.inner.multilinestring_begin(size, idx)
117 }
118
119 fn multilinestring_end(&mut self, idx: usize) -> geozero::error::Result<()> {
120 self.inner.multilinestring_end(idx)
121 }
122
123 fn polygon_begin(
124 &mut self,
125 tagged: bool,
126 size: usize,
127 idx: usize,
128 ) -> geozero::error::Result<()> {
129 self.inner.polygon_begin(tagged, size, idx)
130 }
131
132 fn polygon_end(&mut self, tagged: bool, idx: usize) -> geozero::error::Result<()> {
133 self.inner.polygon_end(tagged, idx)
134 }
135
136 fn multipolygon_begin(&mut self, size: usize, idx: usize) -> geozero::error::Result<()> {
137 self.inner.multipolygon_begin(size, idx)
138 }
139
140 fn multipolygon_end(&mut self, idx: usize) -> geozero::error::Result<()> {
141 self.inner.multipolygon_end(idx)
142 }
143}
144
145impl<T: PropertyProcessor> PropertyProcessor for ProjectingTessellator<T> {
146 fn property(
147 &mut self,
148 idx: usize,
149 name: &str,
150 value: &geozero::ColumnValue,
151 ) -> geozero::error::Result<bool> {
152 self.inner.property(idx, name, value)
153 }
154}
155
156impl<T: FeatureProcessor> FeatureProcessor for ProjectingTessellator<T> {
157 fn dataset_begin(&mut self, name: Option<&str>) -> geozero::error::Result<()> {
158 self.inner.dataset_begin(name)
159 }
160 fn dataset_end(&mut self) -> geozero::error::Result<()> {
161 self.inner.dataset_end()
162 }
163 fn feature_begin(&mut self, idx: u64) -> geozero::error::Result<()> {
164 self.inner.feature_begin(idx)
165 }
166 fn properties_begin(&mut self) -> geozero::error::Result<()> {
167 self.inner.properties_begin()
168 }
169 fn properties_end(&mut self) -> geozero::error::Result<()> {
170 self.inner.properties_end()
171 }
172 fn geometry_begin(&mut self) -> geozero::error::Result<()> {
173 self.inner.geometry_begin()
174 }
175 fn geometry_end(&mut self) -> geozero::error::Result<()> {
176 self.inner.geometry_end()
177 }
178 fn feature_end(&mut self, idx: u64) -> geozero::error::Result<()> {
179 self.inner.feature_end(idx)
180 }
181}
182
183pub struct GeoJsonTileRequest {
185 pub coords: WorldTileCoords,
186 pub layers: Vec<StyleLayer>,
187 pub source_name: String,
189 pub project: bool,
191}
192
193pub fn process_geojson_features<T: VectorTransferables, C: Context>(
203 geojson_value: &serde_json::Value,
204 request: GeoJsonTileRequest,
205 context: &C,
206) -> Result<(), ProcessGeoJsonError> {
207 let coords = request.coords;
208 let json_str = geojson_value.to_string();
209
210 for style_layer in &request.layers {
211 let matches_source = style_layer
212 .source
213 .as_deref()
214 .map_or(false, |s| s == request.source_name);
215 if !matches_source {
216 continue;
217 }
218
219 let Some(paint) = &style_layer.paint else {
220 log::warn!("GeoJSON style layer {} has no paint", style_layer.id);
221 continue;
222 };
223
224 match paint {
225 LayerPaint::Fill(_) | LayerPaint::Line(_) | LayerPaint::Background(_) => {
226 let mut tessellator = ZeroTessellator::<IndexDataType>::default();
227 match paint {
228 LayerPaint::Fill(p) => tessellator.style_property = p.fill_color.clone(),
229 LayerPaint::Line(p) => {
230 tessellator.style_property = p.line_color.clone();
231 tessellator.is_line_layer = true;
232 }
233 LayerPaint::Background(p) => {
234 tessellator.style_property = p.background_color.clone()
235 }
236 _ => {}
237 }
238
239 let mut projecting =
240 ProjectingTessellator::new(coords, request.project, tessellator);
241
242 let mut geojson_src = geozero::geojson::GeoJson(json_str.as_str());
243 if let Err(e) = geojson_src.process(&mut projecting) {
244 log::warn!(
245 "GeoJSON tessellation for layer {} failed: {e:?}",
246 style_layer.id
247 );
248 context
249 .send_back(T::LayerMissing::build_from(coords, style_layer.id.clone()))
250 .map_err(ProcessGeoJsonError::SendError)?;
251 continue;
252 }
253
254 let mut inner = projecting.into_inner();
255 if inner.feature_indices.is_empty() && !inner.buffer.indices.is_empty() {
260 let _ = inner.feature_end(0);
261 }
262
263 let synthetic_layer = geozero::mvt::tile::Layer {
264 version: 2,
265 name: style_layer.id.clone(),
266 ..Default::default()
267 };
268
269 context
270 .send_back(T::LayerTessellated::build_from(
271 coords,
272 inner.buffer.into(),
273 inner.feature_indices,
274 inner.feature_colors,
275 synthetic_layer,
276 style_layer.id.clone(),
277 ))
278 .map_err(ProcessGeoJsonError::SendError)?;
279 }
280 LayerPaint::Symbol(symbol_paint) => {
281 let mut tessellator = TextTessellator::<IndexDataType>::default();
282 let text_field = symbol_paint
283 .text_field
284 .clone()
285 .unwrap_or_else(|| "name".to_string());
286 let mut tessellator_new = TextTessellatorNew::new(text_field);
287 let mut projecting =
288 ProjectingTessellator::new(coords, request.project, tessellator_new);
289
290 let mut geojson_src = geozero::geojson::GeoJson(json_str.as_str());
291 if let Err(e) = geojson_src.process(&mut projecting) {
292 log::warn!(
293 "GeoJSON text tessellation for layer {} failed: {e:?}",
294 style_layer.id
295 );
296 context
297 .send_back(T::LayerMissing::build_from(coords, style_layer.id.clone()))
298 .map_err(ProcessGeoJsonError::SendError)?;
299 continue;
300 }
301
302 let mut inner = projecting.into_inner();
303 inner.finish();
304
305 let synthetic_layer = geozero::mvt::tile::Layer {
306 version: 2,
307 name: style_layer.id.clone(),
308 ..Default::default()
309 };
310
311 context
312 .send_back(T::SymbolLayerTessellated::build_from(
313 coords,
314 tessellator.quad_buffer.into(),
315 inner.quad_buffer.into(),
316 inner.features,
317 synthetic_layer,
318 style_layer.id.clone(),
319 ))
320 .map_err(ProcessGeoJsonError::SendError)?;
321 }
322 _ => {
323 log::trace!(
324 "GeoJSON layer {} has unsupported paint type, skipping",
325 style_layer.id
326 );
327 }
328 }
329 }
330
331 context
332 .send_back(T::TileTessellated::build_from(coords))
333 .map_err(ProcessGeoJsonError::SendError)?;
334
335 Ok(())
336}