1use std::cmp::Ordering;
3use std::convert::TryFrom;
4use std::error::Error;
5use std::str::FromStr;
6
7use serde::{Deserialize, Serialize};
8use serde_json::{Map, Value};
9
10use crate::constants::EPSILON;
11use crate::errors::UtilesCoreError;
12use crate::errors::UtilesCoreResult;
13use crate::fns::{bounds, children, neighbors, parent, siblings, xy};
14use crate::projection::Projection;
15use crate::tile_feature::TileFeature;
16use crate::tile_like::TileLike;
17use crate::tile_tuple::TileTuple;
18use crate::traits::TileParent;
19use crate::{
20 children1_zorder, children_zorder, quadkey2tile, rmid2xyz, utile, xyz2quadkey,
21 IsOk, TileChildren1,
22};
23
24#[cfg(feature = "pmtiles")]
25use crate::pmtiles;
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
29pub struct Tile {
30 pub x: u32,
32
33 pub y: u32,
35
36 pub z: u8,
38}
39
40#[derive(Debug, Serialize, Deserialize)]
42pub struct TileFeatureGeometry {
43 #[serde(rename = "type")]
45 pub type_: String,
46
47 pub coordinates: Vec<Vec<Vec<f64>>>,
49}
50
51#[derive(Debug, Serialize)]
53pub struct FeatureOptions {
54 pub fid: Option<String>,
56
57 pub props: Option<Map<String, Value>>,
59
60 pub projection: Projection,
62
63 pub buffer: Option<f64>,
65
66 pub precision: Option<i32>,
68}
69
70impl Default for FeatureOptions {
71 fn default() -> Self {
72 FeatureOptions {
73 fid: None,
74 props: None,
75 projection: Projection::Geographic,
76 buffer: None,
77 precision: None,
78 }
79 }
80}
81
82impl From<TileTuple> for Tile {
83 fn from(xyz: TileTuple) -> Self {
84 Tile {
85 x: xyz.0,
86 y: xyz.1,
87 z: xyz.2,
88 }
89 }
90}
91
92impl std::fmt::Display for Tile {
93 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94 write!(f, "x{}y{}z{}", self.x, self.y, self.z)
95 }
96}
97
98impl PartialOrd<Self> for Tile {
99 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
100 Some(self.cmp(other))
101 }
102
103 fn lt(&self, other: &Self) -> bool {
104 self.cmp(other) == Ordering::Less
105 }
106}
107
108impl Ord for Tile {
109 fn cmp(&self, other: &Self) -> Ordering {
110 (self.z, self.x, self.y).cmp(&(other.z, other.x, other.y))
111 }
112}
113
114impl FromStr for Tile {
115 type Err = Box<dyn Error>;
116
117 fn from_str(s: &str) -> Result<Self, Self::Err> {
118 if s.starts_with('{') {
120 let r = Tile::from_json_obj(s);
122 match r {
123 Ok(tile) => Ok(tile),
124 Err(_e) => {
125 Err(Box::from(UtilesCoreError::TileParseError(s.to_string())))
126 }
127 }
128 } else if s.starts_with('[') {
129 let r = Tile::from_json_arr(s);
131 match r {
132 Ok(tile) => Ok(tile),
133 Err(_e) => {
134 Err(Box::from(UtilesCoreError::TileParseError(s.to_string())))
135 }
136 }
137 } else {
138 Err(Box::from(UtilesCoreError::TileParseError(s.to_string())))
139 }
140 }
141}
142
143impl TileLike for Tile {
144 fn x(&self) -> u32 {
145 self.x
146 }
147
148 fn y(&self) -> u32 {
149 self.y
150 }
151
152 fn z(&self) -> u8 {
153 self.z
154 }
155}
156
157impl TileParent for Tile {
158 fn parent(&self, zoom: Option<u8>) -> Option<Tile> {
159 self.parent(zoom)
160 }
161
162 fn root() -> Tile {
163 utile!(0, 0, 0)
164 }
165}
166
167impl TileChildren1 for Tile {
168 fn children1(&self) -> [Self; 4] {
169 self.children1()
170 }
171}
172
173impl Tile {
174 #[must_use]
176 pub fn new(x: u32, y: u32, z: u8) -> Self {
177 Tile { x, y, z }
178 }
179
180 #[must_use]
182 pub fn flip(&self) -> Self {
183 Tile::new(self.x, self.flipy(), self.z)
184 }
185
186 #[must_use]
188 pub fn bounds(&self) -> (f64, f64, f64, f64) {
189 bounds(self.x, self.y, self.z)
190 }
191
192 #[must_use]
194 pub fn from_row_major_id(id: u64) -> Self {
195 Tile::from(rmid2xyz(id))
196 }
197
198 #[must_use]
200 pub fn from_rmid(id: u64) -> Self {
201 Tile::from_row_major_id(id)
202 }
203
204 #[must_use]
206 pub fn fmt_zxy(&self, sep: Option<&str>) -> String {
207 if let Some(sep) = sep {
208 format!("{}{}{}{}{}", self.z, sep, self.x, sep, self.y)
209 } else {
210 format!("{}/{}/{}", self.z, self.x, self.y)
211 }
212 }
213
214 #[must_use]
224 pub fn fmt_zxy_ext(&self, ext: &str, sep: Option<&str>) -> String {
225 if let Some(sep) = sep {
226 format!("{}{}{}{}{}.{}", self.z, sep, self.x, sep, self.y, ext)
227 } else {
228 format!("{}/{}/{}.{}", self.z, self.x, self.y, ext)
229 }
230 }
231
232 pub fn from_quadkey(quadkey: &str) -> UtilesCoreResult<Self> {
238 quadkey2tile(quadkey)
239 }
240
241 pub fn from_qk(qk: &str) -> UtilesCoreResult<Self> {
247 quadkey2tile(qk)
248 }
249
250 pub fn from_json_obj(json: &str) -> UtilesCoreResult<Self> {
263 let res = serde_json::from_str::<Tile>(json);
264 match res {
265 Ok(tile) => Ok(tile),
266 Err(_e) => Err(UtilesCoreError::TileParseError(json.to_string())),
267 }
268 }
269
270 pub fn from_json_arr(json: &str) -> UtilesCoreResult<Self> {
283 let res = serde_json::from_str::<(u32, u32, u8)>(json);
284 match res {
285 Ok((x, y, z)) => Ok(Tile::new(x, y, z)),
286 Err(_e) => Err(UtilesCoreError::TileParseError(json.to_string())),
287 }
288 }
289
290 pub fn from_json(json: &str) -> Result<Self, UtilesCoreError> {
306 let json_no_space = if json.starts_with(' ') {
307 json.trim()
308 } else {
309 json
310 };
311 if json_no_space.starts_with('{') {
312 Self::from_json_obj(json_no_space)
313 } else {
314 Self::from_json_arr(json_no_space)
315 }
316 }
317
318 pub fn from_json_loose(json: &str) -> UtilesCoreResult<Self> {
324 let v = serde_json::from_str::<Value>(json)?;
325 let t = Tile::try_from(&v)?;
326 Ok(t)
327 }
328
329 #[must_use]
331 pub fn quadkey(&self) -> String {
332 xyz2quadkey(self.x, self.y, self.z)
333 }
334
335 #[must_use]
337 pub fn qk(&self) -> String {
338 xyz2quadkey(self.x, self.y, self.z)
339 }
340
341 #[allow(clippy::used_underscore_items)]
347 #[allow(clippy::cast_possible_truncation)]
348 pub fn from_lnglat_zoom(
349 lng: f64,
350 lat: f64,
351 zoom: u8,
352 truncate: Option<bool>,
353 ) -> UtilesCoreResult<Self> {
354 let (x, y) = crate::_xy(lng, lat, truncate)?;
355 let z2 = 2.0_f64.powi(i32::from(zoom));
356 let z2f = z2;
357
358 let xtile = if x <= 0.0 {
359 0
360 } else if x >= 1.0 {
361 u32::try_from((z2 - 1.0).floor() as i64).unwrap_or(0)
362 } else {
363 let xt = (x + EPSILON) * z2f;
364 u32::try_from(xt.floor() as i64).unwrap_or(0)
365 };
366
367 let ytile = if y <= 0.0 {
368 0
369 } else if y >= 1.0 {
370 u32::try_from((z2 - 1.0).floor() as i64).unwrap_or(0)
371 } else {
372 let yt = (y + EPSILON) * z2f;
373 u32::try_from(yt.floor() as i64).unwrap_or(0)
374 };
375
376 Ok(Self {
377 x: xtile,
378 y: ytile,
379 z: zoom,
380 })
381 }
382
383 #[must_use]
385 pub fn up(&self) -> Self {
386 Self {
387 x: self.x + 1,
388 y: self.y,
389 z: self.z,
390 }
391 }
392
393 #[must_use]
395 pub fn down(&self) -> Self {
396 Self {
397 x: self.x - 1,
398 y: self.y,
399 z: self.z,
400 }
401 }
402
403 #[must_use]
405 pub fn left(&self) -> Self {
406 Self {
407 x: self.x,
408 y: self.y - 1,
409 z: self.z,
410 }
411 }
412
413 #[must_use]
415 pub fn right(&self) -> Self {
416 Self {
417 x: self.x,
418 y: self.y + 1,
419 z: self.z,
420 }
421 }
422
423 #[must_use]
425 pub fn up_left(&self) -> Self {
426 Self {
427 x: self.x + 1,
428 y: self.y - 1,
429 z: self.z,
430 }
431 }
432
433 #[must_use]
435 pub fn up_right(&self) -> Self {
436 Self {
437 x: self.x + 1,
438 y: self.y + 1,
439 z: self.z,
440 }
441 }
442
443 #[must_use]
445 pub fn down_left(&self) -> Self {
446 Self {
447 x: self.x - 1,
448 y: self.y - 1,
449 z: self.z,
450 }
451 }
452
453 #[must_use]
455 pub fn down_right(&self) -> Self {
456 Self {
457 x: self.x - 1,
458 y: self.y + 1,
459 z: self.z,
460 }
461 }
462
463 #[must_use]
465 pub fn neighbors(&self) -> Vec<Self> {
466 neighbors(self.x, self.y, self.z)
467 }
468
469 #[must_use]
471 pub fn children1(&self) -> [Tile; 4] {
472 children1_zorder(self.x, self.y, self.z)
473 }
474
475 #[must_use]
477 pub fn children(&self, zoom: Option<u8>) -> Vec<Tile> {
478 children(self.x, self.y, self.z, zoom)
479 }
480
481 #[must_use]
483 pub fn children_zorder(&self, zoom: Option<u8>) -> Vec<Tile> {
484 children_zorder(self.x, self.y, self.z, zoom)
485 }
486
487 #[must_use]
489 pub fn parent(&self, zoom: Option<u8>) -> Option<Self> {
490 parent(self.x, self.y, self.z, zoom)
491 }
492
493 #[must_use]
495 pub fn siblings(&self) -> Vec<Self> {
496 siblings(self.x, self.y, self.z)
497 }
498
499 pub fn feature(&self, opts: &FeatureOptions) -> UtilesCoreResult<TileFeature> {
505 let buffer = opts.buffer.unwrap_or(0.0);
506 let precision = opts.precision.unwrap_or(-1);
507 let (west, south, east, north) = self.bbox();
509 let (mut west, mut south, mut east, mut north) = match opts.projection {
511 Projection::Mercator => {
513 let (west_merc, south_merc) = xy(west, south, None);
515 let (east_merc, north_merc) = xy(east, north, None);
516 (west_merc, south_merc, east_merc, north_merc)
517 }
518 Projection::Geographic => (west, south, east, north),
519 };
520
521 west -= buffer;
523 south -= buffer;
524 east += buffer;
525 north += buffer;
526
527 if precision >= 0 {
529 let precision_factor = 10_f64.powi(precision);
530 west = (west * precision_factor).round() / precision_factor;
531 south = (south * precision_factor).round() / precision_factor;
532 east = (east * precision_factor).round() / precision_factor;
533 north = (north * precision_factor).round() / precision_factor;
534 }
535
536 let bbox = (
538 west.min(east),
539 south.min(north),
540 west.max(east),
541 south.max(north),
542 );
543 let xyz = self.tuple_string();
544 let geometry_coordinates = vec![vec![
545 vec![west, south],
546 vec![east, south],
547 vec![east, north],
548 vec![west, north],
549 vec![west, south],
550 ]];
551 let mut properties: Map<String, Value> = Map::new();
552 properties.insert("title".to_string(), Value::from(format!("XYZ tile {xyz}")));
553 properties.extend(opts.props.clone().unwrap_or_default());
554 let id = opts.fid.clone().unwrap_or(xyz);
555 let tile_feature = TileFeature {
556 id,
557 type_: "Feature".to_string(),
558 geometry: TileFeatureGeometry {
559 type_: "Polygon".to_string(),
560 coordinates: geometry_coordinates,
561 },
562 bbox,
563 properties,
564 };
565 Ok(tile_feature)
566 }
567}
568
569#[cfg(feature = "pmtiles")]
570impl Tile {
571 #[must_use]
573 pub fn pmtileid(&self) -> u64 {
574 pmtiles::xyz2pmid(self.x, self.y, self.z)
575 }
576
577 #[must_use]
579 pub fn from_pmtileid(id: u64) -> Self {
580 pmtiles::pmid2xyz(id).into()
581 }
582
583 #[must_use]
585 pub fn from_pmid(id: u64) -> Self {
586 pmtiles::pmid2xyz(id).into()
587 }
588
589 #[must_use]
591 pub fn parent_pmtileid(&self) -> Option<u64> {
592 self.parent(None).map(|t| Self::pmtileid(&t))
593 }
594}
595impl IsOk for Tile {
596 fn ok(&self) -> UtilesCoreResult<Self> {
597 if self.z > 30 {
598 Err(UtilesCoreError::InvalidTile(format!(
599 "({},{},{}) 0 <= zoom <= 30",
600 self.x, self.y, self.z
601 )))
602 } else {
603 let z2 = 2_u32.pow(u32::from(self.z));
604 if self.x >= z2 || self.y >= z2 {
605 Err(UtilesCoreError::InvalidTile(format!(
606 "({},{},{}) x < 2^z and y < 2^z",
607 self.x, self.y, self.z
608 )))
609 } else {
610 Ok(*self)
611 }
612 }
613 }
614}
615
616impl From<(u32, u32, u8)> for Tile {
617 fn from(tuple: (u32, u32, u8)) -> Self {
618 TileTuple::from(tuple).into()
619 }
620}
621
622impl TryFrom<&Map<String, Value>> for Tile {
623 type Error = UtilesCoreError;
624
625 fn try_from(map: &Map<String, Value>) -> Result<Self, Self::Error> {
626 let x = u32::try_from(map["x"].as_u64().ok_or_else(|| {
627 UtilesCoreError::InvalidJson(
628 serde_json::to_string(&map).unwrap_or_default(),
629 )
630 })?)
631 .map_err(|_| {
632 UtilesCoreError::InvalidJson(
633 serde_json::to_string(&map).unwrap_or_default(),
634 )
635 })?;
636 let y = u32::try_from(map["y"].as_u64().ok_or_else(|| {
637 UtilesCoreError::InvalidJson(
638 serde_json::to_string(&map).unwrap_or_default(),
639 )
640 })?)
641 .map_err(|_| {
642 UtilesCoreError::InvalidJson(
643 serde_json::to_string(&map).unwrap_or_default(),
644 )
645 })?;
646 let z = u8::try_from(map["z"].as_u64().ok_or_else(|| {
647 UtilesCoreError::InvalidJson(
648 serde_json::to_string(&map).unwrap_or_default(),
649 )
650 })?)
651 .map_err(|_| {
652 UtilesCoreError::InvalidJson(
653 serde_json::to_string(&map).unwrap_or_default(),
654 )
655 })?;
656 Ok(Tile::new(x, y, z))
657 }
658}
659
660impl TryFrom<(u64, u64, u64)> for Tile {
661 type Error = UtilesCoreError;
662
663 fn try_from(tuple: (u64, u64, u64)) -> Result<Self, Self::Error> {
664 let x = u32::try_from(tuple.0).map_err(|_| {
665 UtilesCoreError::InvalidTile(format!(
666 "({},{},{})",
667 tuple.0, tuple.1, tuple.2
668 ))
669 })?;
670 let y = u32::try_from(tuple.1).map_err(|_| {
671 UtilesCoreError::InvalidTile(format!(
672 "({},{},{})",
673 tuple.0, tuple.1, tuple.2
674 ))
675 })?;
676 let z = u8::try_from(tuple.2).map_err(|_| {
677 UtilesCoreError::InvalidTile(format!(
678 "({},{},{})",
679 tuple.0, tuple.1, tuple.2
680 ))
681 })?;
682 Ok(Tile::new(x, y, z))
683 }
684}
685
686impl TryFrom<&Vec<Value>> for Tile {
687 type Error = UtilesCoreError;
688
689 fn try_from(arr: &Vec<Value>) -> Result<Self, Self::Error> {
690 if arr.len() < 3 {
691 Err(UtilesCoreError::InvalidJson(
692 serde_json::to_string(&arr).unwrap_or_default(),
693 ))
694 } else {
695 let x = arr[0].as_u64().ok_or_else(|| {
696 UtilesCoreError::InvalidJson(
697 serde_json::to_string(&arr).unwrap_or_default(),
698 )
699 })?;
700 let y = arr[1].as_u64().ok_or_else(|| {
701 UtilesCoreError::InvalidJson(
702 serde_json::to_string(&arr).unwrap_or_default(),
703 )
704 })?;
705 let z = arr[2].as_u64().ok_or_else(|| {
706 UtilesCoreError::InvalidJson(
707 serde_json::to_string(&arr).unwrap_or_default(),
708 )
709 })?;
710 Tile::try_from((x, y, z))
711 }
712 }
713}
714
715impl TryFrom<&Value> for Tile {
716 type Error = UtilesCoreError;
717
718 fn try_from(val: &Value) -> Result<Self, Self::Error> {
719 match val {
720 Value::Array(v) => {
721 let t = Tile::try_from(v)?;
722 Ok(t)
723 }
724 Value::Object(v) => {
725 if v.contains_key("x") && v.contains_key("y") && v.contains_key("z") {
726 let x = v["x"].as_u64().ok_or_else(|| {
727 UtilesCoreError::InvalidJson(
728 serde_json::to_string(&v)
729 .expect("Invalid json object for Tile from Value"),
730 )
731 })?;
732 let y = v["y"].as_u64().ok_or_else(|| {
733 UtilesCoreError::InvalidJson(
734 serde_json::to_string(&v)
735 .expect("Invalid json object for Tile from Value"),
736 )
737 })?;
738 let z = v["z"].as_u64().ok_or_else(|| {
739 UtilesCoreError::InvalidJson(
740 serde_json::to_string(&v)
741 .expect("Invalid json object for Tile from Value"),
742 )
743 })?;
744 Tile::try_from((x, y, z))
745 } else if v.contains_key("tile")
746 && v["tile"].is_array()
747 && v["tile"]
748 .as_array()
749 .expect("Unable to get tile array from Value")
750 .len()
751 == 3
752 {
753 let tuple = serde_json::from_value::<TileTuple>(v["tile"].clone())?;
754 Ok(Tile::from(tuple))
755 } else {
756 Err(UtilesCoreError::InvalidJson(
757 serde_json::to_string(&v)
758 .expect("Invalid json object for Tile from Value"),
759 ))
760 }
761 }
762 _ => Err(UtilesCoreError::InvalidJson(val.to_string())),
763 }
764 }
765}
766
767impl TryFrom<Value> for Tile {
768 type Error = UtilesCoreError;
769
770 fn try_from(val: Value) -> Result<Self, Self::Error> {
771 Tile::try_from(&val)
772 }
773}
774
775impl TryFrom<&str> for Tile {
782 type Error = UtilesCoreError;
783
784 fn try_from(s: &str) -> Result<Self, Self::Error> {
785 let res = Tile::from_str(s);
786 match res {
787 Ok(tile) => Ok(tile),
788 Err(e) => Err(UtilesCoreError::TileParseError(e.to_string())),
789 }
790 }
791}
792
793impl From<Tile> for (u32, u32, u8) {
794 fn from(tile: Tile) -> Self {
795 (tile.x, tile.y, tile.z)
796 }
797}
798
799#[cfg(test)]
800mod tests {
801 #![allow(clippy::unwrap_used)]
802
803 use super::*;
804
805 #[test]
806 fn parse_json_obj() {
807 let json_obj = r#"{"x": 1, "y": 2, "z": 3}"#;
808 let tile = Tile::from_json_obj(json_obj).unwrap();
809 assert_eq!(tile, Tile::new(1, 2, 3));
810 }
811
812 #[test]
813 fn parse_json_arr() {
814 let json_arr = r"[1, 2, 3]";
815 let tile = Tile::from_json_arr(json_arr).unwrap();
816 assert_eq!(tile, Tile::new(1, 2, 3));
817 }
818
819 #[test]
820 fn parse_quadkey() {
821 let quadkey = "023010203";
822 let tile = Tile::from_quadkey(quadkey).unwrap();
823 assert_eq!(tile, Tile::new(81, 197, 9));
824 }
825
826 #[test]
827 fn tile_from_value_obj() {
828 let json_obj = r#"{"x": 1, "y": 2, "z": 3}"#;
829 let val_obj = serde_json::from_str::<Value>(json_obj).unwrap();
830 let tile_from_obj = Tile::try_from(val_obj).unwrap();
831 assert_eq!(tile_from_obj, Tile::new(1, 2, 3));
832 }
833
834 #[test]
835 fn tile_from_value_arr() {
836 let json_arr = r"[1, 2, 3]";
837 let val_arr = serde_json::from_str::<Value>(json_arr).unwrap();
838 let tile_from_arr = Tile::try_from(val_arr).unwrap();
839 assert_eq!(tile_from_arr, Tile::new(1, 2, 3));
840 }
841
842 #[test]
843 fn tile_from_value_obj_with_array() {
844 let json_obj_with_tile_array = r#"{"tile": [1, 2, 3]}"#;
845 let val_obj_with_tile_array =
846 serde_json::from_str::<Value>(json_obj_with_tile_array).unwrap();
847 let tile_from_obj_with_tile_array =
848 Tile::try_from(val_obj_with_tile_array).unwrap();
849 assert_eq!(tile_from_obj_with_tile_array, Tile::new(1, 2, 3));
850 }
851}