From d67a994a9eaf0448cda22c4ba5f1ceed39e2c231 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Tue, 31 Oct 2023 05:18:37 -0700 Subject: [PATCH 01/80] refactoring! --- crates/utiles/src/bbox.rs | 186 +++++ crates/utiles/src/lib.rs | 785 ++-------------------- crates/utiles/src/lnglat.rs | 64 ++ crates/utiles/src/sibling_relationship.rs | 2 +- crates/utiles/src/tile.rs | 330 +++++++++ crates/utiles/src/tile_range.rs | 137 ++++ src/lib.rs | 2 +- src/pyutiles/pybbox.rs | 2 +- src/pyutiles/pylnglatbbox.rs | 2 +- src/pyutiles/pytile.rs | 5 +- 10 files changed, 767 insertions(+), 748 deletions(-) create mode 100644 crates/utiles/src/bbox.rs create mode 100644 crates/utiles/src/lnglat.rs create mode 100644 crates/utiles/src/tile.rs create mode 100644 crates/utiles/src/tile_range.rs diff --git a/crates/utiles/src/bbox.rs b/crates/utiles/src/bbox.rs new file mode 100644 index 00000000..81c2a592 --- /dev/null +++ b/crates/utiles/src/bbox.rs @@ -0,0 +1,186 @@ +use crate::lnglat::LngLat; +use crate::tile::Tile; +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct BBox { + pub north: f64, + pub south: f64, + pub east: f64, + pub west: f64, +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct WebMercatorBbox { + pub left: f64, + pub bottom: f64, + pub right: f64, + pub top: f64, +} + +pub enum BBoxContainable { + LngLat(LngLat), + BBox(BBox), + Tile(Tile), +} + +impl From<(f64, f64, f64, f64)> for BBox { + fn from(bbox: (f64, f64, f64, f64)) -> Self { + BBox { + north: bbox.0, + south: bbox.1, + east: bbox.2, + west: bbox.3, + } + } +} + +impl From<(i32, i32, i32, i32)> for BBox { + fn from(bbox: (i32, i32, i32, i32)) -> Self { + // convert to f64 + let bbox = ( + f64::from(bbox.0), + f64::from(bbox.1), + f64::from(bbox.2), + f64::from(bbox.3), + ); + BBox { + north: bbox.0, + south: bbox.1, + east: bbox.2, + west: bbox.3, + } + } +} + +impl BBox { + pub fn crosses_antimeridian(&self) -> bool { + self.west > self.east + } + + pub fn tuple(&self) -> (f64, f64, f64, f64) { + (self.north, self.south, self.east, self.west) + } + + pub fn north(&self) -> f64 { + self.north + } + pub fn south(&self) -> f64 { + self.south + } + pub fn east(&self) -> f64 { + self.east + } + pub fn west(&self) -> f64 { + self.west + } + pub fn top(&self) -> f64 { + self.north + } + pub fn bottom(&self) -> f64 { + self.south + } + pub fn right(&self) -> f64 { + self.east + } + pub fn left(&self) -> f64 { + self.west + } + + pub fn contains_lnglat(&self, lnglat: LngLat) -> bool { + let lng = lnglat.lng(); + let lat = lnglat.lat(); + if self.crosses_antimeridian() { + if (lng >= self.west || lng <= self.east) + && lat >= self.south + && lat <= self.north + { + return true; + } + } else if lng >= self.west + && lng <= self.east + && lat >= self.south + && lat <= self.north + { + return true; + } + false + } + + pub fn contains_tile(&self, tile: Tile) -> bool { + let bbox = tile.bbox(); + self.contains_bbox(bbox.into()) + } + + pub fn contains_bbox(&self, other: BBox) -> bool { + self.north >= other.north + && self.south <= other.south + && self.east >= other.east + && self.west <= other.west + } + + pub fn contains(&self, other: BBoxContainable) -> bool { + match other { + BBoxContainable::LngLat(lnglat) => self.contains_lnglat(lnglat), + BBoxContainable::BBox(bbox) => self.contains_bbox(bbox), + BBoxContainable::Tile(tile) => self.contains_tile(tile), + } + } + + pub fn is_within(&self, other: &BBox) -> bool { + self.north <= other.north + && self.south >= other.south + && self.east <= other.east + && self.west >= other.west + } + + pub fn intersects(&self, other: &BBox) -> bool { + self.north >= other.south + && self.south <= other.north + && self.east >= other.west + && self.west <= other.east + } + + pub fn bboxes(&self) -> Vec { + if self.crosses_antimeridian() { + let mut bboxes = Vec::new(); + let bbox1 = BBox { + north: self.north, + south: self.south, + east: 180.0, + west: self.west, + }; + let bbox2 = BBox { + north: self.north, + south: self.south, + east: self.east, + west: -180.0, + }; + bboxes.push(bbox1); + bboxes.push(bbox2); + bboxes + } else { + vec![*self] + } + } + + pub fn ul(&self) -> LngLat { + LngLat::new(self.west, self.north) + } + + pub fn ur(&self) -> LngLat { + LngLat::new(self.east, self.north) + } + + pub fn lr(&self) -> LngLat { + LngLat::new(self.east, self.south) + } + + pub fn ll(&self) -> LngLat { + LngLat::new(self.west, self.south) + } +} + +impl From for WebMercatorBbox { + fn from(tile: Tile) -> Self { + crate::xyz2bbox(tile.x, tile.y, tile.z) + } +} diff --git a/crates/utiles/src/lib.rs b/crates/utiles/src/lib.rs index a8748502..1381eb3b 100644 --- a/crates/utiles/src/lib.rs +++ b/crates/utiles/src/lib.rs @@ -1,16 +1,25 @@ -use constants::{EARTH_CIRCUMFERENCE, EARTH_RADIUS, EPSILON, LL_EPSILON}; -use geo_types::{coord, Coord}; -use serde::{Deserialize, Serialize}; -use sibling_relationship::SiblingRelationship; -use std::cmp::Ordering; use std::collections::{HashMap, HashSet}; use std::num::FpCategory; use std::{error::Error, f64::consts::PI}; + +use bbox::{BBox, WebMercatorBbox}; +use constants::{EARTH_CIRCUMFERENCE, EARTH_RADIUS, LL_EPSILON}; +use geo_types::coord; + +pub use lnglat::LngLat; +use sibling_relationship::SiblingRelationship; +pub use tile::Tile; +use tile_range::{TileRange, TileRanges}; use zoom::ZoomOrZooms; -mod constants; + +pub mod bbox; +pub mod constants; pub mod libtiletype; -mod pmtiles; -mod sibling_relationship; +pub mod lnglat; +pub mod pmtiles; +pub mod sibling_relationship; +pub mod tile; +pub mod tile_range; pub mod traits; pub mod zoom; @@ -28,34 +37,24 @@ macro_rules! utile { #[allow(clippy::upper_case_acronyms)] pub struct XYZ(pub u32, pub u32, pub u8); -impl traits::Utiles for Tile { - fn ul(&self) -> LngLat { - ul(self.x, self.y, self.z) - } - - fn ur(&self) -> LngLat { - ur(self.x, self.y, self.z) - } - - fn lr(&self) -> LngLat { - lr(self.x, self.y, self.z) +pub fn ul(x: u32, y: u32, z: u8) -> LngLat { + let (lon_deg, lat_deg) = ult(x, y, z); + LngLat { + xy: coord! {x: lon_deg, y: lat_deg}, } +} - fn ll(&self) -> LngLat { - ll(self.x, self.y, self.z) - } +pub fn ll(x: u32, y: u32, z: u8) -> LngLat { + ul(x, y + 1, z) +} - fn bbox(&self) -> BBox { - let (west, south, east, north) = bounds(self.x, self.y, self.z); - BBox { - north, - south, - east, - west, - } - } +pub fn ur(x: u32, y: u32, z: u8) -> LngLat { + ul(x + 1, y, z) } +pub fn lr(x: u32, y: u32, z: u8) -> LngLat { + ul(x + 1, y + 1, z) +} pub fn ult(x: u32, y: u32, z: u8) -> (f64, f64) { let z2 = f64::from(2_u32.pow(u32::from(z))); let lon_deg = (f64::from(x) / z2) * 360.0 - 180.0; @@ -71,97 +70,7 @@ pub struct XY { pub y: u32, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct Tile { - pub x: u32, - pub y: u32, - pub z: u8, -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct LngLat { - pub xy: Coord, -} - -impl From for LngLat { - fn from(xy: Coord) -> Self { - LngLat::new(xy.x, xy.y) - } -} - -impl From<(f64, f64)> for LngLat { - fn from(xy: (f64, f64)) -> Self { - LngLat::new(xy.0, xy.1) - } -} - -impl LngLat { - pub fn new(lng: f64, lat: f64) -> Self { - LngLat { - xy: coord! { x: lng, y: lat}, - } - } - - pub fn lng(&self) -> f64 { - self.xy.x - } - - pub fn lat(&self) -> f64 { - self.xy.y - } - - pub fn lon(&self) -> f64 { - self.xy.x - } -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct BBox { - pub north: f64, - pub south: f64, - pub east: f64, - pub west: f64, -} - -impl From for Tile { - fn from(xyz: XYZ) -> Self { - Tile { - x: xyz.0, - y: xyz.1, - z: xyz.2, - } - } -} - -impl From<(f64, f64, f64, f64)> for BBox { - fn from(bbox: (f64, f64, f64, f64)) -> Self { - BBox { - north: bbox.0, - south: bbox.1, - east: bbox.2, - west: bbox.3, - } - } -} - -impl From<(i32, i32, i32, i32)> for BBox { - fn from(bbox: (i32, i32, i32, i32)) -> Self { - // convert to f64 - let bbox = ( - f64::from(bbox.0), - f64::from(bbox.1), - f64::from(bbox.2), - f64::from(bbox.3), - ); - BBox { - north: bbox.0, - south: bbox.1, - east: bbox.2, - west: bbox.3, - } - } -} - +/// Truncate a bounding box to the valid range of longitude and latitude. pub fn bbox_truncate( west: f64, south: f64, @@ -191,148 +100,6 @@ pub fn bbox_truncate( (west, south, east, north) } -pub enum BBoxContainable { - LngLat(LngLat), - BBox(BBox), - Tile(Tile), -} - -impl BBox { - pub fn crosses_antimeridian(&self) -> bool { - self.west > self.east - } - - pub fn tuple(&self) -> (f64, f64, f64, f64) { - (self.north, self.south, self.east, self.west) - } - - pub fn north(&self) -> f64 { - self.north - } - pub fn south(&self) -> f64 { - self.south - } - pub fn east(&self) -> f64 { - self.east - } - pub fn west(&self) -> f64 { - self.west - } - pub fn top(&self) -> f64 { - self.north - } - pub fn bottom(&self) -> f64 { - self.south - } - pub fn right(&self) -> f64 { - self.east - } - pub fn left(&self) -> f64 { - self.west - } - - pub fn contains_lnglat(&self, lnglat: LngLat) -> bool { - let lng = lnglat.lng(); - let lat = lnglat.lat(); - if self.crosses_antimeridian() { - if (lng >= self.west || lng <= self.east) - && lat >= self.south - && lat <= self.north - { - return true; - } - } else if lng >= self.west - && lng <= self.east - && lat >= self.south - && lat <= self.north - { - return true; - } - false - } - - pub fn contains_tile(&self, tile: Tile) -> bool { - let bbox = tile.bbox(); - self.contains_bbox(bbox.into()) - } - - pub fn contains_bbox(&self, other: BBox) -> bool { - self.north >= other.north - && self.south <= other.south - && self.east >= other.east - && self.west <= other.west - } - - pub fn contains(&self, other: BBoxContainable) -> bool { - match other { - BBoxContainable::LngLat(lnglat) => self.contains_lnglat(lnglat), - BBoxContainable::BBox(bbox) => self.contains_bbox(bbox), - BBoxContainable::Tile(tile) => self.contains_tile(tile), - } - } - - pub fn is_within(&self, other: &BBox) -> bool { - self.north <= other.north - && self.south >= other.south - && self.east <= other.east - && self.west >= other.west - } - - pub fn intersects(&self, other: &BBox) -> bool { - self.north >= other.south - && self.south <= other.north - && self.east >= other.west - && self.west <= other.east - } - - pub fn bboxes(&self) -> Vec { - if self.crosses_antimeridian() { - let mut bboxes = Vec::new(); - let bbox1 = BBox { - north: self.north, - south: self.south, - east: 180.0, - west: self.west, - }; - let bbox2 = BBox { - north: self.north, - south: self.south, - east: self.east, - west: -180.0, - }; - bboxes.push(bbox1); - bboxes.push(bbox2); - bboxes - } else { - vec![*self] - } - } - - pub fn ul(&self) -> LngLat { - LngLat::new(self.west, self.north) - } - - pub fn ur(&self) -> LngLat { - LngLat::new(self.east, self.north) - } - - pub fn lr(&self) -> LngLat { - LngLat::new(self.east, self.south) - } - - pub fn ll(&self) -> LngLat { - LngLat::new(self.west, self.south) - } -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct WebMercatorBbox { - pub left: f64, - pub bottom: f64, - pub right: f64, - pub top: f64, -} - pub fn minmax(zoom: u32) -> (u32, u32) { (0, 2_u32.pow(zoom) - 1) } @@ -347,7 +114,7 @@ pub fn flipy(y: u32, z: u8) -> u32 { 2_u32.pow(u32::from(z)) - 1 - y } -pub fn get_bbox_zoom(bbox: (u32, u32, u32, u32)) -> u8 { +pub fn bbox2zoom(bbox: (u32, u32, u32, u32)) -> u8 { let max_zoom = 28; let (west, south, east, north) = bbox; for z in 0..max_zoom { @@ -359,13 +126,6 @@ pub fn get_bbox_zoom(bbox: (u32, u32, u32, u32)) -> u8 { max_zoom } -pub fn ul(x: u32, y: u32, z: u8) -> LngLat { - let (lon_deg, lat_deg) = ult(x, y, z); - LngLat { - xy: coord! {x: lon_deg, y: lat_deg}, - } -} - pub fn bounds(x: u32, y: u32, z: u8) -> (f64, f64, f64, f64) { let ul_corner = ul(x, y, z); let lr_corner = ul(x + 1, y + 1, z); @@ -402,6 +162,7 @@ pub fn truncate_lnglat(lnglat: &LngLat) -> LngLat { xy: coord! {x: truncate_lng(lnglat.lng()), y: truncate_lat(lnglat.lat())}, } } + pub fn _xy( lng: f64, lat: f64, @@ -427,6 +188,7 @@ pub fn _xy( } } } + pub fn _xy_og(lng: f64, lat: f64, truncate: Option) -> (f64, f64) { let trunc = truncate.unwrap_or(false); @@ -465,18 +227,6 @@ pub fn xy(lng: f64, lat: f64, truncate: Option) -> (f64, f64) { lat = truncate_lat(lat); } let x = EARTH_RADIUS * lng.to_radians(); - - // let y = match (1.0 + sinlat) / (1.0 - sinlat) { - // y if y.is_infinite() => { - // panic!("Invalid latitude: {:?}", lat); - // } - // y if y.is_nan() => { - // panic!("Invalid latitude: {:?}", lat); - // } - // y => 0.5 - 0.25 * y.ln() / std::f64::consts::PI, - // }; - // y = 0.5 - 0.25 * math.log((1.0 + sinlat) / (1.0 - sinlat)) / math.pi - // let y = 0.5 - 0.25 * ((1.0 + sinlat) / (1.0 - sinlat)).ln() / PI; let y = if lat == 90.0 { f64::INFINITY } else if lat == -90.0 { @@ -485,11 +235,6 @@ pub fn xy(lng: f64, lat: f64, truncate: Option) -> (f64, f64) { // (1.0 + (lat.to_radians()).sin()) / (1.0 - (lat.to_radians()).sin()) EARTH_RADIUS * (PI * 0.25 + 0.5 * lat.to_radians()).tan().ln() }; - - // let y = EARTH_RADIUS * (PI * 0.25 + 0.5 * lat.to_radians()).tan().ln(); - // let x = (lnglat.lng + 180.0) / 360.0; - // let sin_lat = lnglat.lat.to_radians().sin(); - // let y = 0.5 - (0.5 * (1.0 + sin_lat) / (1.0 - sin_lat)).ln() / PI; (x, y) } @@ -507,18 +252,6 @@ pub fn lnglat(x: f64, y: f64, truncate: Option) -> LngLat { } } -pub fn ll(x: u32, y: u32, z: u8) -> LngLat { - ul(x, y + 1, z) -} - -pub fn ur(x: u32, y: u32, z: u8) -> LngLat { - ul(x + 1, y, z) -} - -pub fn lr(x: u32, y: u32, z: u8) -> LngLat { - ul(x + 1, y + 1, z) -} - pub fn parent(x: u32, y: u32, z: u8, n: Option) -> Tile { let n = n.unwrap_or(0); if n == 0 { @@ -688,8 +421,15 @@ pub fn neighbors(x: u32, y: u32, z: u8) -> Vec { } } -// tile = reptiles.Tile(486, 332, 10) +// tile = ut.Tile(486, 332, 10) // expected = "0313102310" +/// Return the quadkey for a tile as a string. +/// # Examples +/// ``` +/// use utiles::xyz2quadkey; +/// let quadkey = xyz2quadkey(486, 332, 10); +/// assert_eq!(quadkey, "0313102310"); +/// ``` pub fn xyz2quadkey(x: u32, y: u32, z: u8) -> String { let mut quadkey = String::new(); for i in (0..z).rev() { @@ -702,7 +442,6 @@ pub fn xyz2quadkey(x: u32, y: u32, z: u8) -> String { digit += 2; } quadkey.push_str(&digit.to_string()); - // write!(quadkey, "{}", digit).unwrap(); } quadkey } @@ -741,288 +480,6 @@ pub fn quadkey2tile(quadkey: &str) -> Result> { Ok(Tile::new(x, y, z)) } -impl std::fmt::Display for Tile { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "x{}y{}z{}", self.x, self.y, self.z) - } -} - -impl std::fmt::Display for LngLat { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "({}, {})", self.xy.x, self.xy.y) - } -} - -impl PartialOrd for Tile { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } - - fn lt(&self, other: &Self) -> bool { - self.cmp(other) == Ordering::Less - } -} - -impl Ord for Tile { - fn cmp(&self, other: &Self) -> Ordering { - if self.z != other.z { - return self.z.cmp(&other.z); - } - if self.y != other.y { - return self.y.cmp(&other.y); - } - self.x.cmp(&other.x) - } -} - -impl Tile { - pub fn new(x: u32, y: u32, z: u8) -> Self { - Tile { x, y, z } - } - - #[allow(dead_code)] - pub fn valid(&self) -> bool { - valid(self.x, self.y, self.z) - } - - pub fn x(&self) -> u32 { - self.x - } - - pub fn y(&self) -> u32 { - self.y - } - - pub fn z(&self) -> u8 { - self.z - } - - pub fn zoom(&self) -> u8 { - self.z - } - - pub fn bounds(&self) -> (f64, f64, f64, f64) { - bounds(self.x, self.y, self.z) - } - - pub fn pmtileid(&self) -> u64 { - pmtiles::xyz2id(self.x, self.y, self.z) - } - - pub fn from_pmtileid(id: u64) -> Self { - let (x, y, z) = pmtiles::id2xyz(id); - Tile::new(x, y, z) - } - - pub fn fmt_zxy(&self, sep: Option<&str>) -> String { - match sep { - Some(sep) => format!("{}{}{}{}{}", self.z, sep, self.x, sep, self.y), - None => format!("{}/{}/{}", self.z, self.x, self.y), - } - } - - pub fn fmt_zxy_ext(&self, ext: &str, sep: Option<&str>) -> String { - match sep { - Some(sep) => { - format!("{}{}{}{}{}.{}", self.z, sep, self.x, sep, self.y, ext) - } - None => format!("{}/{}/{}.{}", self.z, self.x, self.y, ext), - } - } - - pub fn parent_id(&self) -> u64 { - pmtiles::parent_id(self.pmtileid()) - } - - pub fn from_quadkey(quadkey: &str) -> Result> { - quadkey2tile(quadkey) - } - - pub fn from_qk(qk: &str) -> Self { - let res = quadkey2tile(qk); - match res { - Ok(tile) => tile, - Err(e) => { - panic!("Invalid quadkey: {e}"); - } - } - } - - pub fn quadkey(&self) -> String { - xyz2quadkey(self.x, self.y, self.z) - } - - pub fn qk(&self) -> String { - xyz2quadkey(self.x, self.y, self.z) - } - - pub fn from_lnglat_zoom( - lng: f64, - lat: f64, - zoom: u8, - truncate: Option, - ) -> Self { - let xy = _xy(lng, lat, truncate); - let (x, y) = match xy { - Ok(xy) => xy, - Err(e) => { - panic!("Invalid lnglat: {e}"); - } - }; - let z2 = 2.0_f64.powi(i32::from(zoom)); - let z2f = z2; - let xtile = if x <= 0.0 { - 0 - } else if x >= 1.0 { - (z2f - 1.0) as u32 - } else { - let xt = (x + EPSILON) * z2f; - (xt.floor()) as u32 - }; - - let ytile = if y <= 0.0 { - 0 - } else if y >= 1.0 { - (z2f - 1.0) as u32 - } else { - let yt = (y + EPSILON) * z2f; - (yt.floor()) as u32 - }; - Self { - x: xtile, - y: ytile, - z: zoom, - } - } - - pub fn ul(&self) -> LngLat { - ul(self.x, self.y, self.z) - } - - pub fn ll(&self) -> LngLat { - ll(self.x, self.y, self.z) - } - - pub fn ur(&self) -> LngLat { - ur(self.x, self.y, self.z) - } - - pub fn lr(&self) -> LngLat { - lr(self.x, self.y, self.z) - } - - pub fn bbox(&self) -> (f64, f64, f64, f64) { - let ul = self.ul(); - let lr = self.lr(); - (ul.lng(), lr.lat(), lr.lng(), ul.lat()) - } - - pub fn center(&self) -> LngLat { - let ul = self.ul(); - let lr = self.lr(); - LngLat::new((ul.lng() + lr.lng()) / 2.0, (ul.lat() + lr.lat()) / 2.0) - } - - pub fn up(&self) -> Self { - Self { - x: self.x + 1, - y: self.y, - z: self.z, - } - } - - pub fn down(&self) -> Self { - Self { - x: self.x - 1, - y: self.y, - z: self.z, - } - } - - pub fn left(&self) -> Self { - Self { - x: self.x, - y: self.y - 1, - z: self.z, - } - } - - pub fn right(&self) -> Self { - Self { - x: self.x, - y: self.y + 1, - z: self.z, - } - } - - pub fn up_left(&self) -> Self { - Self { - x: self.x + 1, - y: self.y - 1, - z: self.z, - } - } - - pub fn up_right(&self) -> Self { - Self { - x: self.x + 1, - y: self.y + 1, - z: self.z, - } - } - - pub fn down_left(&self) -> Self { - Self { - x: self.x - 1, - y: self.y - 1, - z: self.z, - } - } - - pub fn down_right(&self) -> Self { - Self { - x: self.x - 1, - y: self.y + 1, - z: self.z, - } - } - - pub fn neighbors(&self) -> Vec { - neighbors(self.x, self.y, self.z) - } - - pub fn children(&self, zoom: Option) -> Vec { - children(self.x, self.y, self.z, zoom) - } - - pub fn parent(&self, zoom: Option) -> Self { - parent(self.x, self.y, self.z, zoom) - } - - pub fn siblings(&self) -> Vec { - siblings(self.x, self.y, self.z) - } - - pub fn sql_where(&self, flip: Option) -> String { - // classic mbtiles sqlite query: - // 'SELECT tile_data FROM tiles WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?', - - // flip y for tms (default for mbtiles) - match flip.unwrap_or(true) { - true => format!( - "(zoom_level = {} AND tile_column = {} AND tile_row = {})", - self.z, - self.x, - flipy(self.y, self.z) - ), - false => format!( - "(zoom_level = {} AND tile_column = {} AND tile_row = {})", - self.z, self.x, self.y - ), - } - } -} - impl From for (u32, u32, u8) { fn from(tile: Tile) -> Self { (tile.x, tile.y, tile.z) @@ -1043,12 +500,6 @@ pub fn xyz2bbox(x: u32, y: u32, z: u8) -> WebMercatorBbox { } } -impl From for WebMercatorBbox { - fn from(tile: Tile) -> Self { - xyz2bbox(tile.x, tile.y, tile.z) - } -} - pub fn as_zooms(zoom_or_zooms: ZoomOrZooms) -> Vec { match zoom_or_zooms { ZoomOrZooms::Zoom(zoom) => { @@ -1058,144 +509,6 @@ pub fn as_zooms(zoom_or_zooms: ZoomOrZooms) -> Vec { } } -pub struct TileRange { - curx: u32, - cury: u32, - minx: u32, - maxx: u32, - miny: u32, - maxy: u32, - zoom: u8, -} - -impl TileRange { - pub fn new(minx: u32, maxx: u32, miny: u32, maxy: u32, zoom: u8) -> Self { - Self { - curx: minx, - cury: miny, - minx, - maxx, - miny, - maxy, - zoom, - } - } - - pub fn minx(&self) -> u32 { - self.minx - } - pub fn maxx(&self) -> u32 { - self.maxx - } - pub fn miny(&self) -> u32 { - self.miny - } - pub fn maxy(&self) -> u32 { - self.maxy - } - pub fn zoom(&self) -> u8 { - self.zoom - } - - pub fn length(&self) -> u64 { - ((self.maxx - self.minx + 1) * (self.maxy - self.miny + 1)) as u64 - } - - pub fn sql_where(&self, flip: Option) -> String { - // classic mbtiles sqlite query: - // 'SELECT tile_data FROM tiles WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?', - - let miny = match flip.unwrap_or(true) { - true => flipy(self.miny, self.zoom), - false => self.miny, - }; - let maxy = match flip.unwrap_or(true) { - true => flipy(self.maxy, self.zoom), - false => self.maxy, - }; - format!( - "(zoom_level = {} AND tile_column >= {} AND tile_column <= {} AND tile_row >= {} AND tile_row <= {})", - self.zoom, self.minx, self.maxx, miny, maxy - ) - } -} - -impl Iterator for TileRange { - type Item = (u32, u32, u8); - - fn next(&mut self) -> Option { - if self.curx > self.maxx { - self.curx = self.minx; - self.cury += 1; - } - if self.cury > self.maxy { - return None; - } - let tile = (self.curx, self.cury, self.zoom); - self.curx += 1; - Some(tile) - } - - fn size_hint(&self) -> (usize, Option) { - let size = ((self.maxx - self.minx + 1) * (self.maxy - self.miny + 1)) as usize; - (size, Some(size)) - } -} - -pub struct TileRanges { - ranges: Vec, -} - -impl TileRanges { - pub fn new(minx: u32, maxx: u32, miny: u32, maxy: u32, zoom: u8) -> Self { - Self { - ranges: vec![TileRange::new(minx, maxx, miny, maxy, zoom)], - } - } - - pub fn length(&self) -> u64 { - self.ranges.iter().map(|r| r.length()).sum() - } - - pub fn sql_where(&self, flip: Option) -> String { - self.ranges - .iter() - .map(|r| r.sql_where(flip)) - .collect::>() - .join(" OR ") - } -} - -impl From for TileRanges { - fn from(range: TileRange) -> Self { - Self { - ranges: vec![range], - } - } -} - -impl From> for TileRanges { - fn from(ranges: Vec) -> Self { - Self { ranges } - } -} - -impl Iterator for TileRanges { - type Item = (u32, u32, u8); - - fn next(&mut self) -> Option { - if self.ranges.is_empty() { - return None; - } - let mut range = self.ranges.remove(0); - let tile = range.next(); - if let Some((_, _, _)) = tile { - self.ranges.push(range); - } - tile - } -} - fn tiles_range_zoom( minx: u32, maxx: u32, @@ -1217,7 +530,7 @@ pub fn bounding_tile(bbox: BBox, truncate: Option) -> Tile { let tmax = tile(east - LL_EPSILON, south + LL_EPSILON, 32, truncate); let cell = (tmin.x, tmin.y, tmax.x, tmax.y); - let z = get_bbox_zoom(cell); + let z = bbox2zoom(cell); if z == 0 { return Tile::new(0, 0, 0); } @@ -1427,18 +740,6 @@ pub fn simplify(tiles: HashSet) -> HashSet { root_set } -impl Iterator for LngLat { - type Item = (f64, f64); - - fn next(&mut self) -> Option { - let lng = self.xy.x; - let lat = self.xy.y; - self.xy.x += 1.0; - self.xy.y += 1.0; - Some((lng, lat)) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/utiles/src/lnglat.rs b/crates/utiles/src/lnglat.rs new file mode 100644 index 00000000..d04d0476 --- /dev/null +++ b/crates/utiles/src/lnglat.rs @@ -0,0 +1,64 @@ +use geo_types::{coord, Coord}; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct LngLat { + pub xy: Coord, +} + +impl From for LngLat { + fn from(xy: Coord) -> Self { + LngLat::new(xy.x, xy.y) + } +} + +impl From<(f64, f64)> for LngLat { + fn from(xy: (f64, f64)) -> Self { + LngLat::new(xy.0, xy.1) + } +} + +impl LngLat { + pub fn new(lng: f64, lat: f64) -> Self { + LngLat { + xy: coord! { x: lng, y: lat}, + } + } + + pub fn lng(&self) -> f64 { + self.xy.x + } + + pub fn lat(&self) -> f64 { + self.xy.y + } + + pub fn lon(&self) -> f64 { + self.xy.x + } + + pub fn x(&self) -> f64 { + self.xy.x + } + + pub fn y(&self) -> f64 { + self.xy.y + } +} + +impl std::fmt::Display for LngLat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "({}, {})", self.xy.x, self.xy.y) + } +} + +impl Iterator for LngLat { + type Item = (f64, f64); + + fn next(&mut self) -> Option { + let lng = self.xy.x; + let lat = self.xy.y; + self.xy.x += 1.0; + self.xy.y += 1.0; + Some((lng, lat)) + } +} diff --git a/crates/utiles/src/sibling_relationship.rs b/crates/utiles/src/sibling_relationship.rs index 0b37950e..503d48c3 100644 --- a/crates/utiles/src/sibling_relationship.rs +++ b/crates/utiles/src/sibling_relationship.rs @@ -1,4 +1,4 @@ -use crate::Tile; +use crate::tile::Tile; pub enum SiblingRelationship { UpperLeft = 0, diff --git a/crates/utiles/src/tile.rs b/crates/utiles/src/tile.rs new file mode 100644 index 00000000..cfc34f96 --- /dev/null +++ b/crates/utiles/src/tile.rs @@ -0,0 +1,330 @@ +use std::cmp::Ordering; +use std::error::Error; + +use serde::{Deserialize, Serialize}; + +use crate::bbox::BBox; +use crate::constants::EPSILON; +use crate::lnglat::LngLat; +use crate::{ll, lr, pmtiles, traits, ul, ur, XYZ}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Tile { + pub x: u32, + pub y: u32, + pub z: u8, +} + +impl traits::Utiles for Tile { + fn ul(&self) -> LngLat { + ul(self.x, self.y, self.z) + } + + fn ur(&self) -> LngLat { + ur(self.x, self.y, self.z) + } + + fn lr(&self) -> LngLat { + lr(self.x, self.y, self.z) + } + + fn ll(&self) -> LngLat { + ll(self.x, self.y, self.z) + } + + fn bbox(&self) -> BBox { + let (west, south, east, north) = crate::bounds(self.x, self.y, self.z); + BBox { + north, + south, + east, + west, + } + } +} + +impl From for Tile { + fn from(xyz: XYZ) -> Self { + Tile { + x: xyz.0, + y: xyz.1, + z: xyz.2, + } + } +} + +impl std::fmt::Display for Tile { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "x{}y{}z{}", self.x, self.y, self.z) + } +} + +impl PartialOrd for Tile { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } + + fn lt(&self, other: &Self) -> bool { + self.cmp(other) == Ordering::Less + } +} + +impl Ord for Tile { + fn cmp(&self, other: &Self) -> Ordering { + if self.z != other.z { + return self.z.cmp(&other.z); + } + if self.y != other.y { + return self.y.cmp(&other.y); + } + self.x.cmp(&other.x) + } +} + +impl Tile { + pub fn new(x: u32, y: u32, z: u8) -> Self { + Tile { x, y, z } + } + + #[allow(dead_code)] + pub fn valid(&self) -> bool { + crate::valid(self.x, self.y, self.z) + } + + pub fn x(&self) -> u32 { + self.x + } + + pub fn y(&self) -> u32 { + self.y + } + + pub fn z(&self) -> u8 { + self.z + } + + pub fn zoom(&self) -> u8 { + self.z + } + + pub fn bounds(&self) -> (f64, f64, f64, f64) { + crate::bounds(self.x, self.y, self.z) + } + + pub fn pmtileid(&self) -> u64 { + pmtiles::xyz2id(self.x, self.y, self.z) + } + + pub fn from_pmtileid(id: u64) -> Self { + let (x, y, z) = pmtiles::id2xyz(id); + Tile::new(x, y, z) + } + + pub fn fmt_zxy(&self, sep: Option<&str>) -> String { + match sep { + Some(sep) => format!("{}{}{}{}{}", self.z, sep, self.x, sep, self.y), + None => format!("{}/{}/{}", self.z, self.x, self.y), + } + } + + pub fn fmt_zxy_ext(&self, ext: &str, sep: Option<&str>) -> String { + match sep { + Some(sep) => { + format!("{}{}{}{}{}.{}", self.z, sep, self.x, sep, self.y, ext) + } + None => format!("{}/{}/{}.{}", self.z, self.x, self.y, ext), + } + } + + pub fn parent_id(&self) -> u64 { + pmtiles::parent_id(self.pmtileid()) + } + + pub fn from_quadkey(quadkey: &str) -> Result> { + crate::quadkey2tile(quadkey) + } + + pub fn from_qk(qk: &str) -> Self { + let res = crate::quadkey2tile(qk); + match res { + Ok(tile) => tile, + Err(e) => { + panic!("Invalid quadkey: {e}"); + } + } + } + + pub fn quadkey(&self) -> String { + crate::xyz2quadkey(self.x, self.y, self.z) + } + + pub fn qk(&self) -> String { + crate::xyz2quadkey(self.x, self.y, self.z) + } + + pub fn from_lnglat_zoom( + lng: f64, + lat: f64, + zoom: u8, + truncate: Option, + ) -> Self { + let xy = crate::_xy(lng, lat, truncate); + let (x, y) = match xy { + Ok(xy) => xy, + Err(e) => { + panic!("Invalid lnglat: {e}"); + } + }; + let z2 = 2.0_f64.powi(i32::from(zoom)); + let z2f = z2; + let xtile = if x <= 0.0 { + 0 + } else if x >= 1.0 { + (z2f - 1.0) as u32 + } else { + let xt = (x + EPSILON) * z2f; + (xt.floor()) as u32 + }; + + let ytile = if y <= 0.0 { + 0 + } else if y >= 1.0 { + (z2f - 1.0) as u32 + } else { + let yt = (y + EPSILON) * z2f; + (yt.floor()) as u32 + }; + Self { + x: xtile, + y: ytile, + z: zoom, + } + } + + pub fn ul(&self) -> LngLat { + ul(self.x, self.y, self.z) + } + + pub fn ll(&self) -> LngLat { + ll(self.x, self.y, self.z) + } + + pub fn ur(&self) -> LngLat { + ur(self.x, self.y, self.z) + } + + pub fn lr(&self) -> LngLat { + lr(self.x, self.y, self.z) + } + + pub fn bbox(&self) -> (f64, f64, f64, f64) { + let ul = self.ul(); + let lr = self.lr(); + (ul.lng(), lr.lat(), lr.lng(), ul.lat()) + } + + pub fn center(&self) -> LngLat { + let ul = self.ul(); + let lr = self.lr(); + LngLat::new((ul.lng() + lr.lng()) / 2.0, (ul.lat() + lr.lat()) / 2.0) + } + + pub fn up(&self) -> Self { + Self { + x: self.x + 1, + y: self.y, + z: self.z, + } + } + + pub fn down(&self) -> Self { + Self { + x: self.x - 1, + y: self.y, + z: self.z, + } + } + + pub fn left(&self) -> Self { + Self { + x: self.x, + y: self.y - 1, + z: self.z, + } + } + + pub fn right(&self) -> Self { + Self { + x: self.x, + y: self.y + 1, + z: self.z, + } + } + + pub fn up_left(&self) -> Self { + Self { + x: self.x + 1, + y: self.y - 1, + z: self.z, + } + } + + pub fn up_right(&self) -> Self { + Self { + x: self.x + 1, + y: self.y + 1, + z: self.z, + } + } + + pub fn down_left(&self) -> Self { + Self { + x: self.x - 1, + y: self.y - 1, + z: self.z, + } + } + + pub fn down_right(&self) -> Self { + Self { + x: self.x - 1, + y: self.y + 1, + z: self.z, + } + } + + pub fn neighbors(&self) -> Vec { + crate::neighbors(self.x, self.y, self.z) + } + + pub fn children(&self, zoom: Option) -> Vec { + crate::children(self.x, self.y, self.z, zoom) + } + + pub fn parent(&self, zoom: Option) -> Self { + crate::parent(self.x, self.y, self.z, zoom) + } + + pub fn siblings(&self) -> Vec { + crate::siblings(self.x, self.y, self.z) + } + + pub fn sql_where(&self, flip: Option) -> String { + // classic mbtiles sqlite query: + // 'SELECT tile_data FROM tiles WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?', + + // flip y for tms (default for mbtiles) + match flip.unwrap_or(true) { + true => format!( + "(zoom_level = {} AND tile_column = {} AND tile_row = {})", + self.z, + self.x, + crate::flipy(self.y, self.z) + ), + false => format!( + "(zoom_level = {} AND tile_column = {} AND tile_row = {})", + self.z, self.x, self.y + ), + } + } +} diff --git a/crates/utiles/src/tile_range.rs b/crates/utiles/src/tile_range.rs new file mode 100644 index 00000000..0118c0ed --- /dev/null +++ b/crates/utiles/src/tile_range.rs @@ -0,0 +1,137 @@ +pub struct TileRange { + curx: u32, + cury: u32, + minx: u32, + maxx: u32, + miny: u32, + maxy: u32, + zoom: u8, +} + +impl TileRange { + pub fn new(minx: u32, maxx: u32, miny: u32, maxy: u32, zoom: u8) -> Self { + Self { + curx: minx, + cury: miny, + minx, + maxx, + miny, + maxy, + zoom, + } + } + + pub fn minx(&self) -> u32 { + self.minx + } + pub fn maxx(&self) -> u32 { + self.maxx + } + pub fn miny(&self) -> u32 { + self.miny + } + pub fn maxy(&self) -> u32 { + self.maxy + } + pub fn zoom(&self) -> u8 { + self.zoom + } + + pub fn length(&self) -> u64 { + ((self.maxx - self.minx + 1) * (self.maxy - self.miny + 1)) as u64 + } + + pub fn sql_where(&self, flip: Option) -> String { + // classic mbtiles sqlite query: + // 'SELECT tile_data FROM tiles WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?', + + let miny = match flip.unwrap_or(true) { + true => crate::flipy(self.miny, self.zoom), + false => self.miny, + }; + let maxy = match flip.unwrap_or(true) { + true => crate::flipy(self.maxy, self.zoom), + false => self.maxy, + }; + format!( + "(zoom_level = {} AND tile_column >= {} AND tile_column <= {} AND tile_row >= {} AND tile_row <= {})", + self.zoom, self.minx, self.maxx, miny, maxy + ) + } +} + +impl Iterator for TileRange { + type Item = (u32, u32, u8); + + fn next(&mut self) -> Option { + if self.curx > self.maxx { + self.curx = self.minx; + self.cury += 1; + } + if self.cury > self.maxy { + return None; + } + let tile = (self.curx, self.cury, self.zoom); + self.curx += 1; + Some(tile) + } + + fn size_hint(&self) -> (usize, Option) { + let size = ((self.maxx - self.minx + 1) * (self.maxy - self.miny + 1)) as usize; + (size, Some(size)) + } +} + +pub struct TileRanges { + ranges: Vec, +} + +impl TileRanges { + pub fn new(minx: u32, maxx: u32, miny: u32, maxy: u32, zoom: u8) -> Self { + Self { + ranges: vec![TileRange::new(minx, maxx, miny, maxy, zoom)], + } + } + + pub fn length(&self) -> u64 { + self.ranges.iter().map(|r| r.length()).sum() + } + + pub fn sql_where(&self, flip: Option) -> String { + self.ranges + .iter() + .map(|r| r.sql_where(flip)) + .collect::>() + .join(" OR ") + } +} + +impl From for TileRanges { + fn from(range: TileRange) -> Self { + Self { + ranges: vec![range], + } + } +} + +impl From> for TileRanges { + fn from(ranges: Vec) -> Self { + Self { ranges } + } +} + +impl Iterator for TileRanges { + type Item = (u32, u32, u8); + + fn next(&mut self) -> Option { + if self.ranges.is_empty() { + return None; + } + let mut range = self.ranges.remove(0); + let tile = range.next(); + if let Some((_, _, _)) = tile { + self.ranges.push(range); + } + tile + } +} diff --git a/src/lib.rs b/src/lib.rs index 40306657..3838303c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ use std::collections::{HashMap, HashSet}; -use utiles::BBox; +use utiles::bbox::BBox; use pyo3::exceptions::{self, PyValueError}; diff --git a/src/pyutiles/pybbox.rs b/src/pyutiles/pybbox.rs index 3f35ac56..dccc35b8 100644 --- a/src/pyutiles/pybbox.rs +++ b/src/pyutiles/pybbox.rs @@ -6,7 +6,7 @@ use pyo3::{ Python, }; use utiles; -use utiles::BBox; +use utiles::bbox::BBox; #[pyclass(name = "Bbox")] #[derive(Clone)] diff --git a/src/pyutiles/pylnglatbbox.rs b/src/pyutiles/pylnglatbbox.rs index e53506ec..d1a72dad 100644 --- a/src/pyutiles/pylnglatbbox.rs +++ b/src/pyutiles/pylnglatbbox.rs @@ -7,7 +7,7 @@ use pyo3::{ PyResult, Python, }; use utiles; -use utiles::BBox; +use utiles::bbox::BBox; #[pyclass(name = "LngLatBbox")] #[derive(Clone)] diff --git a/src/pyutiles/pytile.rs b/src/pyutiles/pytile.rs index ce4286cc..fb8588ff 100644 --- a/src/pyutiles/pytile.rs +++ b/src/pyutiles/pytile.rs @@ -4,7 +4,7 @@ use std::collections::HashMap; use crate::pyutiles::pyiters::IntIterator; use crate::pyutiles::tuple_slice; use crate::TileTuple; -use utiles::{BBox, Tile}; +use utiles::tile::Tile; use utiles; @@ -13,7 +13,7 @@ use pyo3::types::PyType; use pyo3::exceptions::PyValueError; use pyo3::{ - exceptions, pyclass, pymethods, IntoPy, Py, PyAny, PyErr, PyObject, PyRef, + exceptions, IntoPy, Py, PyAny, pyclass, PyErr, pymethods, PyObject, PyRef, PyResult, Python, }; use serde::{Deserialize, Serialize}; @@ -22,6 +22,7 @@ use std::collections::hash_map::DefaultHasher; use crate::pyutiles::pylnglat::PyLngLat; use crate::pyutiles::pylnglatbbox::PyLngLatBbox; use std::hash::{Hash, Hasher}; +use utiles::bbox::BBox; /// `PyTile` macro to create a new tile. /// - do you need this? probably not From 04d3d79aa0465de5e868cd76acc6ea12b1f4880a Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 2 Nov 2023 07:11:40 -0700 Subject: [PATCH 02/80] jesus --- Cargo.lock | 486 +++++++++++++++++++++++++++++++++++++- Cargo.toml | 3 + python/utiles/__init__.py | 3 +- src/lib.rs | 8 +- 4 files changed, 497 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c8fe759d..a6d376cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,87 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "anstream" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys", +] + [[package]] name = "approx" version = "0.5.1" @@ -17,18 +98,140 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap-stdin" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff8959de58e45555040f38e374db860b2ffbfecacef4f353c6ef6e5347dff01" +dependencies = [ + "thiserror", +] + +[[package]] +name = "clap_builder" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fast_hilbert" version = "2.0.0" @@ -49,12 +252,43 @@ dependencies = [ "serde", ] +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "hashbrown" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown", +] + [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + [[package]] name = "indoc" version = "2.0.4" @@ -79,6 +313,17 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "libsqlite3-sys" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "lock_api" version = "0.4.11" @@ -89,6 +334,12 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + [[package]] name = "memoffset" version = "0.9.0" @@ -98,6 +349,26 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + [[package]] name = "num-traits" version = "0.2.17" @@ -108,6 +379,25 @@ dependencies = [ "libm", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.18.0" @@ -137,6 +427,18 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + [[package]] name = "proc-macro2" version = "1.0.69" @@ -215,9 +517,12 @@ dependencies = [ "geo-types", "pyo3", "pyo3-build-config", + "rusqlite", "serde", "serde_json", "utiles", + "utiles-cli", + "utilesqlite", ] [[package]] @@ -235,9 +540,29 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] +[[package]] +name = "rusqlite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" +dependencies = [ + "bitflags 2.4.1", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "ryu" version = "1.0.15" @@ -281,12 +606,37 @@ dependencies = [ "serde", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "smallvec" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "syn" version = "2.0.38" @@ -304,6 +654,67 @@ version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rusqlite" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aa66395f5ff117faee90c9458232c936405f9227ad902038000b74b3bc1feac" +dependencies = [ + "crossbeam-channel", + "rusqlite", + "tokio", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -316,6 +727,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "utiles" version = "0.0.1" @@ -326,6 +743,53 @@ dependencies = [ "serde_json", ] +[[package]] +name = "utiles-cli" +version = "0.1.0" +dependencies = [ + "clap", + "clap-stdin", + "rusqlite", + "thiserror", + "tokio", +] + +[[package]] +name = "utilesqlite" +version = "0.1.0" +dependencies = [ + "rusqlite", + "tokio", + "tokio-rusqlite", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -382,3 +846,23 @@ name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "zerocopy" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd66a62464e3ffd4e37bd09950c2b9dd6c4f8767380fabba0d523f9a775bc85a" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "255c4596d41e6916ced49cfafea18727b24d67878fa180ddfd69b9df34fd1726" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index bf451e88..f9b44747 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,9 +16,12 @@ crate-type = ["cdylib"] fast_hilbert = "2.0.0" geo-types = "0.7.9" pyo3 = "0.20.0" +rusqlite = "0.29.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.96" utiles = { path = "crates/utiles" } +utilesqlite = { path = "crates/utilesqlite" } +utiles-cli = { path = "crates/utiles-cli" } [profile.dev] opt-level = 0 diff --git a/python/utiles/__init__.py b/python/utiles/__init__.py index 7a1869f1..bf7b2a1c 100644 --- a/python/utiles/__init__.py +++ b/python/utiles/__init__.py @@ -3,7 +3,7 @@ import math from typing import List, Sequence, Tuple, Union - +from utiles import libutiles from utiles.libutiles import ( TILETYPE_GIF, TILETYPE_JPG, @@ -82,6 +82,7 @@ "from_pmtileid", "from_tuple", "geojson_bounds", + "libutiles", "lnglat", "minmax", "neighbors", diff --git a/src/lib.rs b/src/lib.rs index 3838303c..b8f2d8c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ use std::collections::{HashMap, HashSet}; use utiles::bbox::BBox; - use pyo3::exceptions::{self, PyValueError}; use pyo3::prelude::*; @@ -16,7 +15,10 @@ use utiles::zoom::ZoomOrZooms; use utiles::libtiletype; +use crate::pyutilesqlite::{PyMbtiles, query_db}; + mod pyutiles; +mod pyutilesqlite; // mod utiles; #[derive(FromPyObject)] @@ -817,5 +819,9 @@ fn libutiles(_py: Python<'_>, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + + // mbtiles... + m.add_class::()?; + m.add_function(wrap_pyfunction!(query_db, m)?)?; Ok(()) } From 9c674bb3a72c085b2b04036ba6aabfafbf6a4d2d Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 2 Nov 2023 08:08:21 -0700 Subject: [PATCH 03/80] cli initial trial run... --- Cargo.lock | 1 + crates/utiles-cli/Cargo.lock | 695 ++++++++++++++++++++++++++++++++ crates/utiles-cli/Cargo.toml | 18 + crates/utiles-cli/src/bin.rs | 34 ++ crates/utiles-cli/src/cli.rs | 331 +++++++++++++++ crates/utiles-cli/src/lib.rs | 1 + crates/utiles/src/bbox.rs | 39 ++ crates/utiles/src/tile.rs | 12 + crates/utiles/src/tile_range.rs | 2 + 9 files changed, 1133 insertions(+) create mode 100644 crates/utiles-cli/Cargo.lock create mode 100644 crates/utiles-cli/Cargo.toml create mode 100644 crates/utiles-cli/src/bin.rs create mode 100644 crates/utiles-cli/src/cli.rs create mode 100644 crates/utiles-cli/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index a6d376cd..d4db3b4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -752,6 +752,7 @@ dependencies = [ "rusqlite", "thiserror", "tokio", + "utiles", ] [[package]] diff --git a/crates/utiles-cli/Cargo.lock b/crates/utiles-cli/Cargo.lock new file mode 100644 index 00000000..c9257ced --- /dev/null +++ b/crates/utiles-cli/Cargo.lock @@ -0,0 +1,695 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "anstream" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fast_hilbert" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ec2bbe15af87954c739e236021f4411766c0f2b9c4a5f0b9317bcf6048ebf8" +dependencies = [ + "num-traits", +] + +[[package]] +name = "geo-types" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9705398c5c7b26132e74513f4ee7c1d7dafd786004991b375c172be2be0eecaa" +dependencies = [ + "approx", + "num-traits", + "serde", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "hashbrown" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "libc" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libsqlite3-sys" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "rusqlite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" +dependencies = [ + "bitflags 2.4.1", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.190" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.190" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tokio" +version = "1.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "utiles" +version = "0.0.1" +dependencies = [ + "fast_hilbert", + "geo-types", + "serde", + "serde_json", +] + +[[package]] +name = "utiles-cli" +version = "0.1.0" +dependencies = [ + "clap", + "rusqlite", + "tokio", + "utiles", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "zerocopy" +version = "0.7.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686b7e407015242119c33dab17b8f61ba6843534de936d94368856528eae4dcc" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020f3dfe25dfc38dfea49ce62d5d45ecdd7f0d8a724fa63eb36b6eba4ec76806" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/crates/utiles-cli/Cargo.toml b/crates/utiles-cli/Cargo.toml new file mode 100644 index 00000000..eac5a333 --- /dev/null +++ b/crates/utiles-cli/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "utiles-cli" +version = "0.1.0" +edition = "2021" + +[lib] +name = "utiles_cli" +path = "src/lib.rs" + +[[bin]] +name = "ut" +path = "src/bin.rs" + +[dependencies] +clap = { version = "4.4.7", features = ["derive"] } +rusqlite = { version = "0.29.0", features = ["bundled", "vtab", "blob"] } +tokio = { version = "1.33.0", features = ["full"] } +utiles = { path = "../utiles" } \ No newline at end of file diff --git a/crates/utiles-cli/src/bin.rs b/crates/utiles-cli/src/bin.rs new file mode 100644 index 00000000..a1e68840 --- /dev/null +++ b/crates/utiles-cli/src/bin.rs @@ -0,0 +1,34 @@ +use std::io::BufRead; + +use clap::{Args, Parser, Subcommand, ValueEnum}; + +mod cli; + +// use crate::maybe_stdin::MaybeStdin; +// mod maybe_stdinput; + +// use clap_stdin::MaybeStdin; + +// use clap_stdin::MaybeStdin; + +// use clap::Parser; +// use std::io::{self, BufRead, BufReader}; +// use std::fs::File; +// use std::iter; + +// #[derive(Parser, Debug)] +// struct Cli { +// Input file, reads from stdin if not present +// #[clap(value_parser)] +// input: Option, +// } +// impl From for Error { +// fn from(e: tokio_rusqlite::Error) -> Error { +// Error::RusqliteError(e) +// } +// } + +#[tokio::main] +async fn main() { + cli::cli_main() +} diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs new file mode 100644 index 00000000..9feec396 --- /dev/null +++ b/crates/utiles-cli/src/cli.rs @@ -0,0 +1,331 @@ +use clap::{Parser, Subcommand, ValueEnum}; +use std::io; +use std::io::BufRead; +use utiles::bbox::BBox; +use utiles::tiles; +use utiles::zoom::ZoomOrZooms; + +pub enum LineSource { + Single(String), + Multiple(Box), +} + +pub struct StdInterator { + source: LineSource, +} + +impl StdInterator { + fn new(input: Option) -> io::Result { + let source = match input { + Some(file_content) => LineSource::Single(file_content), + None => { + let reader = Box::new(io::BufReader::new(io::stdin())); + LineSource::Multiple(reader) + } + }; + Ok(Self { source }) + } +} + +impl Iterator for StdInterator { + type Item = io::Result; + + fn next(&mut self) -> Option { + match &mut self.source { + LineSource::Single(content) => { + if content.is_empty() { + None + } else { + Some(Ok(std::mem::take(content))) + } + } + LineSource::Multiple(reader) => { + let mut line = String::new(); + match reader.read_line(&mut line) { + Ok(0) => None, // EOF + Ok(_) => Some(Ok(line.trim_end().to_string())), + Err(e) => Some(Err(e)), + } + } + } + } +} + +pub struct Stdinput { + pub arg: Option, +} + +// struct LineIterator { +// reader: Box, +// } +// +// impl LineIterator { +// fn new(input: Option) -> io::Result { +// let reader: Box = match input { +// Some(input) => { +// // fake iterator +// Box::new(io::BufReader::new(input.as_bytes())) +// }, +// None => Box::new(io::BufReader::new(io::stdin())) +// }; +// Ok(Self { reader }) +// } +// } +// +// impl Iterator for LineIterator { +// type Item = io::Result; +// +// fn next(&mut self) -> Option { +// let mut line = String::new(); +// match self.reader.read_line(&mut line) { +// Ok(0) => None, // EOF +// Ok(_) => Some(Ok(line.trim_end().to_string())), +// Err(e) => Some(Err(e)), +// } +// } +// } +impl Stdinput { + fn new(arg: Option) -> Self { + Self { arg } + } +} + +impl Iterator for Stdinput { + type Item = String; + + fn next(&mut self) -> Option { + let mut line = String::new(); + let stdin = io::stdin(); + let mut handle = stdin.lock(); + handle.read_line(&mut line).ok().map(|_| line) + } +} + +/// A fictional versioning CLI +#[derive(Debug, Parser)] // requires `derive` feature +#[command(name = "ut")] +#[command(about = "utiles cli (rust)", long_about = None)] +pub struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Debug, Parser)] // requires `derive` feature +pub struct QuadkeyArgs { + /// The remote to clone + // #[arg(required = false, default_value = "-")] + // quadkey: MaybeStdin, + #[arg(required = false)] + quadkey: Option, +} + +#[derive(Debug, Subcommand)] +pub enum Commands { + /// quadkey + // Quadkey { + // #[arg(required = true)] + // quadkey: String, + // }, + Quadkey(QuadkeyArgs), + + /// tiles + Tiles { + #[arg(required = true)] + zoom: u8, + + #[arg(required = false)] + input: Option, + }, + // /// Clones repos + // #[command(arg_required_else_help = true)] + // Clone { + // /// The remote to clone + // remote: String, + // }, + // /// Compare two commits + // Diff { + // #[arg(value_name = "COMMIT")] + // base: Option, + // #[arg(value_name = "COMMIT")] + // head: Option, + // #[arg(last = true)] + // path: Option, + // #[arg( + // long, + // require_equals = true, + // value_name = "WHEN", + // num_args = 0..=1, + // default_value_t = ColorWhen::Auto, + // default_missing_value = "always", + // value_enum + // )] + // color: ColorWhen, + // }, + // /// pushes things + // #[command(arg_required_else_help = true)] + // Push { + // /// The remote to target + // remote: String, + // }, + // /// adds things + // #[command(arg_required_else_help = true)] + // Add { + // /// Stuff to add + // #[arg(required = true)] + // path: Vec, + // }, + + // Stash(StashArgs), + // #[command(external_subcommand)] + // External(Vec), +} + +#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq)] +pub enum ColorWhen { + Always, + Auto, + Never, +} + +impl std::fmt::Display for ColorWhen { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.to_possible_value() + .expect("no values are skipped") + .get_name() + .fmt(f) + } +} + +pub fn cli_main() { + let args = Cli::parse(); + + match args.command { + Commands::Quadkey(quadkey) => { + let thingy = StdInterator::new(quadkey.quadkey).unwrap(); + for line in thingy { + println!("Line from stdin: {}", line.unwrap()); + } + } + Commands::Tiles { zoom, input } => { + let thingy = StdInterator::new(input).unwrap(); + for line in thingy { + let lstr = line.unwrap(); + // println!("Line from stdin: {}", lstr); + let thingy = BBox::from(lstr); + + for tile in tiles( + (thingy.west, thingy.south, thingy.east, thingy.north), + ZoomOrZooms::Zoom(zoom), + ) { + println!("{}", tile.json_arr()); + } + // ) + // let tr = tile_ranges(( + // thingy.west, + // thingy.south, + // thingy.east, + // thingy.north, + // ), ZoomOrZooms::Zoom(zoom)); + // + // println!("tr: {:?}", tr); + // for tile in tr { + // println!("tile: {:?}", tile); + // println!(tile.to_json()); + // } + } + } // Commands::External(args) => { + // println!("Calling out to {:?} with {:?}", &args[0], &args[1..]); + } + + // let c_res = Connection::open( + // "D:\\maps\\reptiles\\mbtiles\\osm\\planet_z0z14_2022_10_13.mbtiles" + // ).await; + + let filepath = "D:\\maps\\reptiles\\mbtiles\\osm\\planet_z0z14_2022_10_13.mbtiles"; + // "D:\\maps\\reptiles\\mbtiles\\osm\\planet_z0z14_2022_10_13.mbtiles", + // "D:\\maps\\reptiles\\mbtiles\\globallandcover.mbtiles", + // let mbt = MbtilesAsync::open( + // "D:\\maps\\reptiles\\mbtiles\\osm\\planet_z0z14_2022_10_13.mbtiles", + // ).await?; + // + // let mdata = mbt.metadata().await?; + // + // let mut metadataMap: HashMap> = HashMap::new(); + // + // for thing in mdata { + // println!("{}: {}", thing.name, thing.value); + // + // // if it does not exist, create empty vector + // // if it does exist, append to vector + // let mut v = metadataMap.entry(thing.name).or_insert(Vec::new()); + // v.push(thing.value); + // } + // + // println!("metadataMap: {:?}", metadataMap); + // + // println!("metadata_has_unique_index_name: {}", mbt.metadata_has_unique_index_name().await?); + // + // let mut mbtiles_manager = MbtilesManager::new(); + // + // // Open the database connection + // mbtiles_manager.open( + // filepath + // ).unwrap(); + // + // let mapfn = |row: &rusqlite::Row| -> rusqlite::Result { + // Ok(row.get(0)?) + // }; + // + // let metadata = mbtiles_manager.metadata(); + // // Execute a query + // let result= mbtiles_manager.query("SELECT name, value FROM metadata", + // mapfn + // ); + // match result { + // Ok(rows) => { + // for row in rows { + // println!("{}", row); + // } + // } + // Err(err) => eprintln!("Query failed: {}", err), + // } + // + // println!("metadata: {:?}", metadata); + // // Close the database connection + // mbtiles_manager.close().unwrap(); + // + // // match c_res { + // // Ok(c) => println!("Connection opened"), + // // Err(e) => println!("Error opening connection: {}", e), + // // } + // let conn = match c_res { + // Ok(c) => c, + // Err(e) => return Err(e), + // }; + // + // let mdata = conn + // .call(|conn| { + // let mut stmt = conn.prepare("SELECT name, value FROM metadata")?; + // let mdata = stmt + // .query_map([], |row| { + // Ok( + // MetadataRow { + // name: row.get(0)?, + // value: row.get(1)?, + // } + // ) + // })? + // .collect::, rusqlite::Error>>()?; + // + // Ok::<_, rusqlite::Error>(mdata) + // }) + // .await?; + // + // + // + // for thing in mdata { + // println!("{}: {}", thing.name, thing.value); + // } + + // let mbt = Connection +} diff --git a/crates/utiles-cli/src/lib.rs b/crates/utiles-cli/src/lib.rs new file mode 100644 index 00000000..4f773726 --- /dev/null +++ b/crates/utiles-cli/src/lib.rs @@ -0,0 +1 @@ +pub mod cli; diff --git a/crates/utiles/src/bbox.rs b/crates/utiles/src/bbox.rs index 81c2a592..9df7aa32 100644 --- a/crates/utiles/src/bbox.rs +++ b/crates/utiles/src/bbox.rs @@ -1,5 +1,13 @@ +use serde::{Deserialize, Serialize}; + +use crate::tiles; use crate::lnglat::LngLat; use crate::tile::Tile; +use crate::zoom::ZoomOrZooms; + +#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)] +pub struct BBoxTuple(f64, f64, f64, f64); + #[derive(Debug, Clone, Copy, PartialEq)] pub struct BBox { pub north: f64, @@ -179,6 +187,37 @@ impl BBox { } } +impl From for BBoxTuple { + fn from(bbox: BBox) -> Self { + BBoxTuple(bbox.west, bbox.south, bbox.east, bbox.north) + } +} + +impl From for BBox { + fn from(tuple: BBoxTuple) -> Self { + BBox { + north: tuple.3, + south: tuple.1, + east: tuple.2, + west: tuple.0, + } + } +} + +impl From for BBox { + fn from(s: String) -> Self { + let tuple: BBoxTuple = serde_json::from_str(&s).unwrap(); + self::BBox::from(tuple) + } +} + +impl From<&String> for BBox { + fn from(s: &String) -> Self { + let tuple: BBoxTuple = serde_json::from_str(&s).unwrap(); + self::BBox::from(tuple) + } +} + impl From for WebMercatorBbox { fn from(tile: Tile) -> Self { crate::xyz2bbox(tile.x, tile.y, tile.z) diff --git a/crates/utiles/src/tile.rs b/crates/utiles/src/tile.rs index cfc34f96..77e4ece6 100644 --- a/crates/utiles/src/tile.rs +++ b/crates/utiles/src/tile.rs @@ -327,4 +327,16 @@ impl Tile { ), } } + + pub fn json_arr_min(&self) -> String { + format!("[{},{},{}]", self.x, self.y, self.z) + } + + pub fn json_arr(&self) -> String { + format!("[{}, {}, {}]", self.x, self.y, self.z) + } + + pub fn json_obj(&self) -> String { + serde_json::to_string(self).unwrap() + } } diff --git a/crates/utiles/src/tile_range.rs b/crates/utiles/src/tile_range.rs index 0118c0ed..6949426f 100644 --- a/crates/utiles/src/tile_range.rs +++ b/crates/utiles/src/tile_range.rs @@ -1,3 +1,4 @@ +#[derive(Debug)] pub struct TileRange { curx: u32, cury: u32, @@ -82,6 +83,7 @@ impl Iterator for TileRange { } } +#[derive(Debug)] pub struct TileRanges { ranges: Vec, } From 6ec4f4f1c6e8806b1daf45d46ec5dcc7579d5213 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 2 Nov 2023 11:09:38 -0700 Subject: [PATCH 04/80] sweet --- Cargo.lock | 159 +++++++++++++++++++---- crates/utiles-cli/Cargo.lock | 149 +++++++++++++++++++++- crates/utiles-cli/Cargo.toml | 7 +- crates/utiles-cli/src/bin.rs | 28 +---- crates/utiles-cli/src/cli.rs | 236 ++++++++++------------------------- crates/utiles/src/bbox.rs | 14 ++- pyproject.toml | 1 + src/lib.rs | 38 ++++-- 8 files changed, 389 insertions(+), 243 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d4db3b4c..7d1db1df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -156,15 +156,6 @@ dependencies = [ "clap_derive", ] -[[package]] -name = "clap-stdin" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff8959de58e45555040f38e374db860b2ffbfecacef4f353c6ef6e5347dff01" -dependencies = [ - "thiserror", -] - [[package]] name = "clap_builder" version = "4.4.7" @@ -301,6 +292,12 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.149" @@ -334,6 +331,12 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + [[package]] name = "memchr" version = "2.6.4" @@ -369,6 +372,16 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-traits" version = "0.2.17" @@ -404,6 +417,12 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parking_lot" version = "0.12.1" @@ -606,6 +625,15 @@ dependencies = [ "serde", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -655,23 +683,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" [[package]] -name = "thiserror" -version = "1.0.50" +name = "thread_local" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "cfg-if", + "once_cell", ] [[package]] @@ -715,6 +733,65 @@ dependencies = [ "tokio", ] +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "nu-ansi-term", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -745,13 +822,13 @@ dependencies = [ [[package]] name = "utiles-cli" -version = "0.1.0" +version = "0.0.1" dependencies = [ "clap", - "clap-stdin", "rusqlite", - "thiserror", "tokio", + "tracing", + "tracing-subscriber", "utiles", ] @@ -764,6 +841,12 @@ dependencies = [ "tokio-rusqlite", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" @@ -782,6 +865,28 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/crates/utiles-cli/Cargo.lock b/crates/utiles-cli/Cargo.lock index c9257ced..bf41587b 100644 --- a/crates/utiles-cli/Cargo.lock +++ b/crates/utiles-cli/Cargo.lock @@ -156,6 +156,16 @@ dependencies = [ "clap_derive", ] +[[package]] +name = "clap-verbosity-flag" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5fdbb015d790cfb378aca82caf9cc52a38be96a7eecdb92f31b4366a8afc019" +dependencies = [ + "clap", + "log", +] + [[package]] name = "clap_builder" version = "4.4.7" @@ -267,6 +277,12 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.149" @@ -300,6 +316,12 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + [[package]] name = "memchr" version = "2.6.4" @@ -326,6 +348,16 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-traits" version = "0.2.17" @@ -361,6 +393,12 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parking_lot" version = "0.12.1" @@ -486,6 +524,15 @@ dependencies = [ "serde", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -528,6 +575,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "tokio" version = "1.33.0" @@ -558,6 +615,65 @@ dependencies = [ "syn", ] +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "nu-ansi-term", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -582,14 +698,23 @@ dependencies = [ [[package]] name = "utiles-cli" -version = "0.1.0" +version = "0.0.1" dependencies = [ "clap", + "clap-verbosity-flag", "rusqlite", "tokio", + "tracing", + "tracing-subscriber", "utiles", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" @@ -608,6 +733,28 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/crates/utiles-cli/Cargo.toml b/crates/utiles-cli/Cargo.toml index eac5a333..85bc904f 100644 --- a/crates/utiles-cli/Cargo.toml +++ b/crates/utiles-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "utiles-cli" -version = "0.1.0" +version = "0.0.1" edition = "2021" [lib] @@ -13,6 +13,9 @@ path = "src/bin.rs" [dependencies] clap = { version = "4.4.7", features = ["derive"] } +clap-verbosity-flag = "2.1.0" rusqlite = { version = "0.29.0", features = ["bundled", "vtab", "blob"] } tokio = { version = "1.33.0", features = ["full"] } -utiles = { path = "../utiles" } \ No newline at end of file +tracing = { version = "0.1.40", features = [] } +tracing-subscriber = { version = "0.3.17", features = ["serde", "serde_json"] } +utiles = { path = "../utiles" } diff --git a/crates/utiles-cli/src/bin.rs b/crates/utiles-cli/src/bin.rs index a1e68840..c9eab5f2 100644 --- a/crates/utiles-cli/src/bin.rs +++ b/crates/utiles-cli/src/bin.rs @@ -2,33 +2,11 @@ use std::io::BufRead; use clap::{Args, Parser, Subcommand, ValueEnum}; -mod cli; - -// use crate::maybe_stdin::MaybeStdin; -// mod maybe_stdinput; - -// use clap_stdin::MaybeStdin; +use tracing_subscriber; -// use clap_stdin::MaybeStdin; - -// use clap::Parser; -// use std::io::{self, BufRead, BufReader}; -// use std::fs::File; -// use std::iter; - -// #[derive(Parser, Debug)] -// struct Cli { -// Input file, reads from stdin if not present -// #[clap(value_parser)] -// input: Option, -// } -// impl From for Error { -// fn from(e: tokio_rusqlite::Error) -> Error { -// Error::RusqliteError(e) -// } -// } +mod cli; #[tokio::main] async fn main() { - cli::cli_main() + cli::cli_main(Option::None) } diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index 9feec396..66cc0b10 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -1,26 +1,39 @@ use clap::{Parser, Subcommand, ValueEnum}; use std::io; use std::io::BufRead; +use tracing::{debug, info}; +use tracing_subscriber; +use tracing_subscriber::util::SubscriberInitExt; use utiles::bbox::BBox; use utiles::tiles; use utiles::zoom::ZoomOrZooms; -pub enum LineSource { +pub enum StdInteratorSource { Single(String), Multiple(Box), } pub struct StdInterator { - source: LineSource, + source: StdInteratorSource, } impl StdInterator { fn new(input: Option) -> io::Result { let source = match input { - Some(file_content) => LineSource::Single(file_content), + Some(file_content) => { + if file_content == "-" { + debug!("reading from stdin - got '-'"); + let reader = Box::new(io::BufReader::new(io::stdin())); + StdInteratorSource::Multiple(reader) + } else { + debug!("reading from args: {:?}", file_content); + StdInteratorSource::Single(file_content) + } + } None => { let reader = Box::new(io::BufReader::new(io::stdin())); - LineSource::Multiple(reader) + debug!("reading from stdin - no args"); + StdInteratorSource::Multiple(reader) } }; Ok(Self { source }) @@ -29,17 +42,16 @@ impl StdInterator { impl Iterator for StdInterator { type Item = io::Result; - fn next(&mut self) -> Option { match &mut self.source { - LineSource::Single(content) => { + StdInteratorSource::Single(content) => { if content.is_empty() { None } else { Some(Ok(std::mem::take(content))) } } - LineSource::Multiple(reader) => { + StdInteratorSource::Multiple(reader) => { let mut line = String::new(); match reader.read_line(&mut line) { Ok(0) => None, // EOF @@ -51,56 +63,6 @@ impl Iterator for StdInterator { } } -pub struct Stdinput { - pub arg: Option, -} - -// struct LineIterator { -// reader: Box, -// } -// -// impl LineIterator { -// fn new(input: Option) -> io::Result { -// let reader: Box = match input { -// Some(input) => { -// // fake iterator -// Box::new(io::BufReader::new(input.as_bytes())) -// }, -// None => Box::new(io::BufReader::new(io::stdin())) -// }; -// Ok(Self { reader }) -// } -// } -// -// impl Iterator for LineIterator { -// type Item = io::Result; -// -// fn next(&mut self) -> Option { -// let mut line = String::new(); -// match self.reader.read_line(&mut line) { -// Ok(0) => None, // EOF -// Ok(_) => Some(Ok(line.trim_end().to_string())), -// Err(e) => Some(Err(e)), -// } -// } -// } -impl Stdinput { - fn new(arg: Option) -> Self { - Self { arg } - } -} - -impl Iterator for Stdinput { - type Item = String; - - fn next(&mut self) -> Option { - let mut line = String::new(); - let stdin = io::stdin(); - let mut handle = stdin.lock(); - handle.read_line(&mut line).ok().map(|_| line) - } -} - /// A fictional versioning CLI #[derive(Debug, Parser)] // requires `derive` feature #[command(name = "ut")] @@ -108,6 +70,18 @@ impl Iterator for Stdinput { pub struct Cli { #[command(subcommand)] command: Commands, + + // debug flag + #[arg( + long, + short, + global = true, + default_value = "false", + help = "debug mode" + )] + debug: bool, + // #[command(flatten , help="verbosity level (-v, -vv, -vvv, -vvvv)" )] + // verbose: Verbosity, } #[derive(Debug, Parser)] // requires `derive` feature @@ -126,6 +100,7 @@ pub enum Commands { // #[arg(required = true)] // quadkey: String, // }, + #[command(name = "quadkey", visible_alias= "qk", about = "convert xyz <-> quadkey", long_about = None)] Quadkey(QuadkeyArgs), /// tiles @@ -196,21 +171,50 @@ impl std::fmt::Display for ColorWhen { } } -pub fn cli_main() { - let args = Cli::parse(); +pub fn cli_main(argv: Option>) { + // print args + let argv = match argv { + Some(argv) => argv, + None => std::env::args().collect::>(), + }; + + let args = Cli::parse_from(&argv); + + // level is info by default and debug if --debug is passed + let level = if args.debug { + tracing::Level::DEBUG + } else { + tracing::Level::WARN + }; + + // install global collector configured based on RUST_LOG env var. + // tracing_subscriber::fmt::init(); + let subscriber = tracing_subscriber::fmt() + .with_max_level(level) + .with_writer(std::io::stderr) + .finish() + .init(); + debug!("args: {:?}", std::env::args().collect::>()); + debug!("argv: {:?}", argv); + + debug!("args: {:?}", args); match args.command { Commands::Quadkey(quadkey) => { let thingy = StdInterator::new(quadkey.quadkey).unwrap(); for line in thingy { - println!("Line from stdin: {}", line.unwrap()); + println!("Line from stdin: `{}`", line.unwrap()); } } Commands::Tiles { zoom, input } => { let thingy = StdInterator::new(input).unwrap(); - for line in thingy { + println!("zoom: {}", zoom); + for line in thingy + .filter(|l| !l.is_err()) + .filter(|l| !l.as_ref().unwrap().is_empty()) + { let lstr = line.unwrap(); - // println!("Line from stdin: {}", lstr); + println!("Line from stdin: `{}`", lstr); let thingy = BBox::from(lstr); for tile in tiles( @@ -219,113 +223,7 @@ pub fn cli_main() { ) { println!("{}", tile.json_arr()); } - // ) - // let tr = tile_ranges(( - // thingy.west, - // thingy.south, - // thingy.east, - // thingy.north, - // ), ZoomOrZooms::Zoom(zoom)); - // - // println!("tr: {:?}", tr); - // for tile in tr { - // println!("tile: {:?}", tile); - // println!(tile.to_json()); - // } } - } // Commands::External(args) => { - // println!("Calling out to {:?} with {:?}", &args[0], &args[1..]); + } } - - // let c_res = Connection::open( - // "D:\\maps\\reptiles\\mbtiles\\osm\\planet_z0z14_2022_10_13.mbtiles" - // ).await; - - let filepath = "D:\\maps\\reptiles\\mbtiles\\osm\\planet_z0z14_2022_10_13.mbtiles"; - // "D:\\maps\\reptiles\\mbtiles\\osm\\planet_z0z14_2022_10_13.mbtiles", - // "D:\\maps\\reptiles\\mbtiles\\globallandcover.mbtiles", - // let mbt = MbtilesAsync::open( - // "D:\\maps\\reptiles\\mbtiles\\osm\\planet_z0z14_2022_10_13.mbtiles", - // ).await?; - // - // let mdata = mbt.metadata().await?; - // - // let mut metadataMap: HashMap> = HashMap::new(); - // - // for thing in mdata { - // println!("{}: {}", thing.name, thing.value); - // - // // if it does not exist, create empty vector - // // if it does exist, append to vector - // let mut v = metadataMap.entry(thing.name).or_insert(Vec::new()); - // v.push(thing.value); - // } - // - // println!("metadataMap: {:?}", metadataMap); - // - // println!("metadata_has_unique_index_name: {}", mbt.metadata_has_unique_index_name().await?); - // - // let mut mbtiles_manager = MbtilesManager::new(); - // - // // Open the database connection - // mbtiles_manager.open( - // filepath - // ).unwrap(); - // - // let mapfn = |row: &rusqlite::Row| -> rusqlite::Result { - // Ok(row.get(0)?) - // }; - // - // let metadata = mbtiles_manager.metadata(); - // // Execute a query - // let result= mbtiles_manager.query("SELECT name, value FROM metadata", - // mapfn - // ); - // match result { - // Ok(rows) => { - // for row in rows { - // println!("{}", row); - // } - // } - // Err(err) => eprintln!("Query failed: {}", err), - // } - // - // println!("metadata: {:?}", metadata); - // // Close the database connection - // mbtiles_manager.close().unwrap(); - // - // // match c_res { - // // Ok(c) => println!("Connection opened"), - // // Err(e) => println!("Error opening connection: {}", e), - // // } - // let conn = match c_res { - // Ok(c) => c, - // Err(e) => return Err(e), - // }; - // - // let mdata = conn - // .call(|conn| { - // let mut stmt = conn.prepare("SELECT name, value FROM metadata")?; - // let mdata = stmt - // .query_map([], |row| { - // Ok( - // MetadataRow { - // name: row.get(0)?, - // value: row.get(1)?, - // } - // ) - // })? - // .collect::, rusqlite::Error>>()?; - // - // Ok::<_, rusqlite::Error>(mdata) - // }) - // .await?; - // - // - // - // for thing in mdata { - // println!("{}: {}", thing.name, thing.value); - // } - - // let mbt = Connection } diff --git a/crates/utiles/src/bbox.rs b/crates/utiles/src/bbox.rs index 9df7aa32..392f42d2 100644 --- a/crates/utiles/src/bbox.rs +++ b/crates/utiles/src/bbox.rs @@ -204,19 +204,21 @@ impl From for BBox { } } -impl From for BBox { - fn from(s: String) -> Self { - let tuple: BBoxTuple = serde_json::from_str(&s).unwrap(); - self::BBox::from(tuple) - } -} impl From<&String> for BBox { fn from(s: &String) -> Self { + // remove leading and trailing quotes + let s = s.trim_matches('"'); let tuple: BBoxTuple = serde_json::from_str(&s).unwrap(); + self::BBox::from(tuple) } } +impl From for BBox { + fn from(s: String) -> Self { + self::BBox::from(&s) + } +} impl From for WebMercatorBbox { fn from(tile: Tile) -> Self { diff --git a/pyproject.toml b/pyproject.toml index aa41103a..251a948e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,7 @@ dependencies = [ [project.scripts] utiles = "utiles.cli:cli" +ut = "utiles.ut:cli" [project.entry-points."rasterio.rio_plugins"] utiles = "utiles.rio_plugin:rio_utiles" diff --git a/src/lib.rs b/src/lib.rs index b8f2d8c6..6ea15603 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,22 +1,19 @@ use std::collections::{HashMap, HashSet}; -use utiles::bbox::BBox; use pyo3::exceptions::{self, PyValueError}; - use pyo3::prelude::*; use pyo3::types::{PyDict, PyTuple}; +use utiles::bbox::BBox; +use utiles::libtiletype; +use utiles::zoom::ZoomOrZooms; +use utiles_cli::cli::cli_main; + use pyutiles::pybbox::PyBbox; use pyutiles::pyiters::CoordinateIterator; use pyutiles::pylnglat::PyLngLat; use pyutiles::pylnglatbbox::PyLngLatBbox; use pyutiles::pytile::PyTile; -use utiles::zoom::ZoomOrZooms; - -use utiles::libtiletype; - -use crate::pyutilesqlite::{PyMbtiles, query_db}; - mod pyutiles; mod pyutilesqlite; // mod utiles; @@ -430,7 +427,7 @@ impl From for ZoomOrZooms { #[pyclass] struct TilesGenerator { - iter: Box + Send>, + iter: Box + Send>, length: u64, } @@ -486,7 +483,7 @@ fn tiles( (west, south, east, north), ZoomOrZooms::from(zooms_vec_iter), ) - .map(PyTile::from); + .map(PyTile::from); TilesGenerator { iter: Box::new(xyzs), length: ntiles, @@ -760,6 +757,17 @@ fn feature( Ok(f) } +#[pyfunction] +fn utcli(py: Python, args: Option>) { + let argv = match args { + Some(args) => args, + None => std::env::args().collect(), + }; + cli_main( + Option::Some(argv) + ) +} + /// Utiles python module #[pymodule] fn libutiles(_py: Python<'_>, m: &PyModule) -> PyResult<()> { @@ -821,7 +829,11 @@ fn libutiles(_py: Python<'_>, m: &PyModule) -> PyResult<()> { m.add_class::()?; // mbtiles... - m.add_class::()?; - m.add_function(wrap_pyfunction!(query_db, m)?)?; + // m.add_class::()?; + // m.add_function(wrap_pyfunction!(query_db, m)?)?; + + // rust-cli + m.add_function(wrap_pyfunction!(utcli, m)?)?; + Ok(()) -} +} \ No newline at end of file From f97dbfe6fe868512a783f99c0844f0706e601db5 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 2 Nov 2023 11:10:36 -0700 Subject: [PATCH 05/80] removed thingy --- crates/utiles-cli/src/cli.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index 66cc0b10..aab22d98 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -189,7 +189,7 @@ pub fn cli_main(argv: Option>) { // install global collector configured based on RUST_LOG env var. // tracing_subscriber::fmt::init(); - let subscriber = tracing_subscriber::fmt() + tracing_subscriber::fmt() .with_max_level(level) .with_writer(std::io::stderr) .finish() From 8d07e73bb7a250165d01e4e0af5265fe10d8c3ab Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 2 Nov 2023 11:11:50 -0700 Subject: [PATCH 06/80] alrighty --- crates/utiles-cli/src/bin.rs | 6 ------ crates/utiles-cli/src/cli.rs | 4 ++-- crates/utiles/src/bbox.rs | 3 +-- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/crates/utiles-cli/src/bin.rs b/crates/utiles-cli/src/bin.rs index c9eab5f2..011ca547 100644 --- a/crates/utiles-cli/src/bin.rs +++ b/crates/utiles-cli/src/bin.rs @@ -1,9 +1,3 @@ -use std::io::BufRead; - -use clap::{Args, Parser, Subcommand, ValueEnum}; - -use tracing_subscriber; - mod cli; #[tokio::main] diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index aab22d98..6eea2767 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -1,8 +1,8 @@ use clap::{Parser, Subcommand, ValueEnum}; use std::io; use std::io::BufRead; -use tracing::{debug, info}; -use tracing_subscriber; +use tracing::debug; + use tracing_subscriber::util::SubscriberInitExt; use utiles::bbox::BBox; use utiles::tiles; diff --git a/crates/utiles/src/bbox.rs b/crates/utiles/src/bbox.rs index 392f42d2..c0e75484 100644 --- a/crates/utiles/src/bbox.rs +++ b/crates/utiles/src/bbox.rs @@ -1,9 +1,7 @@ use serde::{Deserialize, Serialize}; -use crate::tiles; use crate::lnglat::LngLat; use crate::tile::Tile; -use crate::zoom::ZoomOrZooms; #[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)] pub struct BBoxTuple(f64, f64, f64, f64); @@ -214,6 +212,7 @@ impl From<&String> for BBox { self::BBox::from(tuple) } } + impl From for BBox { fn from(s: String) -> Self { self::BBox::from(&s) From 5f4420c1f9c69879c3b5ca794dae6291d2077803 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Fri, 3 Nov 2023 20:53:17 -0700 Subject: [PATCH 07/80] forgot to commit these --- Cargo.lock | 11 + crates/utilesqlite/Cargo.lock | 514 ++++++++++++++++++++++++++++++ crates/utilesqlite/Cargo.toml | 17 + crates/utilesqlite/src/bin.rs | 114 +++++++ crates/utilesqlite/src/lib.rs | 17 + crates/utilesqlite/src/mbtiles.rs | 229 +++++++++++++ 6 files changed, 902 insertions(+) create mode 100644 crates/utilesqlite/Cargo.lock create mode 100644 crates/utilesqlite/Cargo.toml create mode 100644 crates/utilesqlite/src/bin.rs create mode 100644 crates/utilesqlite/src/lib.rs create mode 100644 crates/utilesqlite/src/mbtiles.rs diff --git a/Cargo.lock b/Cargo.lock index 7d1db1df..7bc704db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -156,6 +156,16 @@ dependencies = [ "clap_derive", ] +[[package]] +name = "clap-verbosity-flag" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5fdbb015d790cfb378aca82caf9cc52a38be96a7eecdb92f31b4366a8afc019" +dependencies = [ + "clap", + "log", +] + [[package]] name = "clap_builder" version = "4.4.7" @@ -825,6 +835,7 @@ name = "utiles-cli" version = "0.0.1" dependencies = [ "clap", + "clap-verbosity-flag", "rusqlite", "tokio", "tracing", diff --git a/crates/utilesqlite/Cargo.lock b/crates/utilesqlite/Cargo.lock new file mode 100644 index 00000000..601f1dce --- /dev/null +++ b/crates/utilesqlite/Cargo.lock @@ -0,0 +1,514 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "hashbrown" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "libc" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + +[[package]] +name = "libsqlite3-sys" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "rusqlite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" +dependencies = [ + "bitflags 2.4.1", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tokio" +version = "1.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rusqlite" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aa66395f5ff117faee90c9458232c936405f9227ad902038000b74b3bc1feac" +dependencies = [ + "crossbeam-channel", + "rusqlite", + "tokio", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utilesqlite" +version = "0.1.0" +dependencies = [ + "rusqlite", + "tokio", + "tokio-rusqlite", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "zerocopy" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd66a62464e3ffd4e37bd09950c2b9dd6c4f8767380fabba0d523f9a775bc85a" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "255c4596d41e6916ced49cfafea18727b24d67878fa180ddfd69b9df34fd1726" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/crates/utilesqlite/Cargo.toml b/crates/utilesqlite/Cargo.toml new file mode 100644 index 00000000..7455932d --- /dev/null +++ b/crates/utilesqlite/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "utilesqlite" +version = "0.1.0" +edition = "2021" + +[lib] +name = "utilesqlite" +path = "src/lib.rs" + +[[bin]] +name = "utilesqlite" +path = "src/bin.rs" + +[dependencies] +rusqlite = { version = "0.29.0", features = ["bundled", "vtab", "blob"] } +tokio = { version = "1.33.0", features = ["full"] } +tokio-rusqlite = "0.4.0" diff --git a/crates/utilesqlite/src/bin.rs b/crates/utilesqlite/src/bin.rs new file mode 100644 index 00000000..e22aa722 --- /dev/null +++ b/crates/utilesqlite/src/bin.rs @@ -0,0 +1,114 @@ +use std::collections::HashMap; +use std::hash::Hash; + +use rusqlite; +use mbtiles::MbtilesManager; + +mod mbtiles; + +// impl From for Error { +// fn from(e: tokio_rusqlite::Error) -> Error { +// Error::RusqliteError(e) +// } +// } + + +#[tokio::main] +async fn main() -> tokio_rusqlite::Result<()> { + // let c_res = Connection::open( + // "D:\\maps\\reptiles\\mbtiles\\osm\\planet_z0z14_2022_10_13.mbtiles" + // ).await; + + let filepath= "D:\\maps\\reptiles\\mbtiles\\osm\\planet_z0z14_2022_10_13.mbtiles"; + // "D:\\maps\\reptiles\\mbtiles\\osm\\planet_z0z14_2022_10_13.mbtiles", + // "D:\\maps\\reptiles\\mbtiles\\globallandcover.mbtiles", + // let mbt = MbtilesAsync::open( + // "D:\\maps\\reptiles\\mbtiles\\osm\\planet_z0z14_2022_10_13.mbtiles", + // ).await?; + // + // let mdata = mbt.metadata().await?; + // + // let mut metadataMap: HashMap> = HashMap::new(); + // + // for thing in mdata { + // println!("{}: {}", thing.name, thing.value); + // + // // if it does not exist, create empty vector + // // if it does exist, append to vector + // let mut v = metadataMap.entry(thing.name).or_insert(Vec::new()); + // v.push(thing.value); + // } + // + // println!("metadataMap: {:?}", metadataMap); + // + // println!("metadata_has_unique_index_name: {}", mbt.metadata_has_unique_index_name().await?); + // + let mut mbtiles_manager = MbtilesManager::new(); + + // Open the database connection + mbtiles_manager.open( + filepath + ).unwrap(); + + let mapfn = |row: &rusqlite::Row| -> rusqlite::Result { + Ok(row.get(0)?) + }; + + let metadata = mbtiles_manager.metadata(); + // Execute a query + let result= mbtiles_manager.query("SELECT name, value FROM metadata", + mapfn + ); + match result { + Ok(rows) => { + for row in rows { + println!("{}", row); + } + } + Err(err) => eprintln!("Query failed: {}", err), + } + + println!("metadata: {:?}", metadata); + // Close the database connection + mbtiles_manager.close().unwrap(); + // + // // match c_res { + // // Ok(c) => println!("Connection opened"), + // // Err(e) => println!("Error opening connection: {}", e), + // // } + // let conn = match c_res { + // Ok(c) => c, + // Err(e) => return Err(e), + // }; + // + // let mdata = conn + // .call(|conn| { + // let mut stmt = conn.prepare("SELECT name, value FROM metadata")?; + // let mdata = stmt + // .query_map([], |row| { + // Ok( + // MetadataRow { + // name: row.get(0)?, + // value: row.get(1)?, + // } + // ) + // })? + // .collect::, rusqlite::Error>>()?; + // + // Ok::<_, rusqlite::Error>(mdata) + // }) + // .await?; + // + // + // + // for thing in mdata { + // println!("{}: {}", thing.name, thing.value); + // } + + Ok( + () + ) + + + // let mbt = Connection +} diff --git a/crates/utilesqlite/src/lib.rs b/crates/utilesqlite/src/lib.rs new file mode 100644 index 00000000..157f5b61 --- /dev/null +++ b/crates/utilesqlite/src/lib.rs @@ -0,0 +1,17 @@ +// pub use crate::mbtiles::{Mbtiles, MetadataRow, all_metadata}; +// pub mod mbtiles; + +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/utilesqlite/src/mbtiles.rs b/crates/utilesqlite/src/mbtiles.rs new file mode 100644 index 00000000..e2be34a7 --- /dev/null +++ b/crates/utilesqlite/src/mbtiles.rs @@ -0,0 +1,229 @@ +// #[derive(Debug)] +// pub struct Mbtiles<'a> { +// conn: &'a mut rusqlite::Connection, +// } +use rusqlite::{Connection, Result}; + +pub struct MbtilesManager { + conn: Option, +} + +#[derive(Debug)] +pub struct MbtilesMetadataRow { + pub name: String, + pub value: String, +} + + +pub fn mbtiles_metadata(conn: &rusqlite::Connection) -> Result> { + let mut stmt = conn.prepare("SELECT name, value FROM metadata")?; + let mdata = stmt + .query_map([], |row| { + Ok( + MbtilesMetadataRow { + name: row.get(0)?, + value: row.get(1)?, + } + ) + })? + .collect::, rusqlite::Error>>()?; + return Ok(mdata); +} + +impl MbtilesManager { + // Create a new instance of the MbtilesManager + pub fn new() -> MbtilesManager { + MbtilesManager { conn: None } + } + + // Open a connection to the MBTiles SQLite database + pub fn open(&mut self, path: &str) -> Result<()> { + self.conn = Some(Connection::open(path)?); + Ok(()) + } + + // Execute a query on the MBTiles database + pub fn query(&self, sql: &str, mut map_fn: F) -> Result> + where + F: FnMut(&rusqlite::Row<'_>) -> Result, + { + match &self.conn { + Some(conn) => { + let mut stmt = conn.prepare(sql)?; + let rows = stmt.query_map([], |row| map_fn(row))?; + rows.collect() + } + None => Err(rusqlite::Error::InvalidQuery), + } + } + + pub fn metadata(&self) -> Result> { + return mbtiles_metadata(self.conn.as_ref().unwrap()); + // let mut stmt = self.conn.as_ref().unwrap().prepare("SELECT name, value FROM metadata")?; + // let rows = stmt.query_map([], |row| { + // Ok( + // MbtilesMetadataRow { + // name: row.get(0)?, + // value: row.get(1)?, + // } + // ) + // })?; + // rows.collect() + } + + // Close the connection to the MBTiles database + pub fn close(&mut self) -> Result<()> { + if let Some(conn) = self.conn.take() { + conn.close().map_err(|(_, e)| e) + } else { + Ok(()) + } + } +} + +// fn main() { +// let mut mbtiles_manager = MbtilesManager::new(); +// +// // Open the database connection +// mbtiles_manager.open("path/to/your/mbtiles/database.mbtiles").unwrap(); +// +// // Execute a query +// let result: Result> = mbtiles_manager.query("SELECT name FROM some_table", |row| { +// Ok(row.get(0)?) +// }); +// match result { +// Ok(rows) => { +// for row in rows { +// println!("{}", row); +// } +// } +// Err(err) => eprintln!("Query failed: {}", err), +// } +// +// // Close the database connection +// mbtiles_manager.close().unwrap(); +// } + +// #[derive(Debug)] +// pub struct Mbtiles<'a> { +// pub conn: &'a mut rusqlite::Connection, +// } +// #[derive(Debug)] +// pub struct MetadataRow { +// pub name: String, +// pub value: String, +// } +// +// impl Mbtiles<'_> { +// // impl Mbtiles { +// pub fn metadata<'a>(&'a self) -> rusqlite::Result> { +// // return all_metadata(self.conn); +// +// let mut stmt = self.conn.prepare("SELECT name, value FROM metadata")?; +// let mdata = stmt +// .query_map([], |row| { +// Ok( +// MetadataRow { +// name: row.get(0)?, +// value: row.get(1)?, +// } +// ) +// })? +// .collect::>>(); +// return Ok(mdata?); +// } +// +// pub fn open<'a>(fspath: &str) -> rusqlite::Result { +// let mut conn = rusqlite::Connection::open(fspath)?; +// let mbt = Mbtiles { +// conn: &mut conn, +// }; +// +// return Ok(mbt); +// +// } +// +// pub fn from_conn<'a>(conn: &mut rusqlite::Connection) -> Mbtiles { +// Mbtiles { +// conn: conn, +// } +// } +// } +// +// +// pub fn all_metadata (conn: &rusqlite::Connection) -> rusqlite::Result> { +// let mut stmt = conn.prepare("SELECT name, value FROM metadata")?; +// let mdata = stmt +// .query_map([], |row| { +// Ok( +// MetadataRow { +// name: row.get(0)?, +// value: row.get(1)?, +// } +// ) +// })? +// .collect::>>(); +// return Ok(mdata?); +// } + +// #[derive(Debug)] +// pub struct Mbtiles<'a> { +// pub conn: &'a mut rusqlite::Connection, +// } +// #[derive(Debug)] +// pub struct MetadataRow { +// pub name: String, +// pub value: String, +// } +// +// impl Mbtiles<'_> { +// // impl Mbtiles { +// pub fn metadata<'a>(&'a self) -> rusqlite::Result> { +// // return all_metadata(self.conn); +// +// let mut stmt = self.conn.prepare("SELECT name, value FROM metadata")?; +// let mdata = stmt +// .query_map([], |row| { +// Ok( +// MetadataRow { +// name: row.get(0)?, +// value: row.get(1)?, +// } +// ) +// })? +// .collect::>>(); +// return Ok(mdata?); +// } +// +// pub fn open<'a>(fspath: &str) -> rusqlite::Result { +// let mut conn = rusqlite::Connection::open(fspath)?; +// let mbt = Mbtiles { +// conn: &mut conn, +// }; +// +// return Ok(mbt); +// +// } +// +// pub fn from_conn<'a>(conn: &mut rusqlite::Connection) -> Mbtiles { +// Mbtiles { +// conn: conn, +// } +// } +// } +// +// +// pub fn all_metadata (conn: &rusqlite::Connection) -> rusqlite::Result> { +// let mut stmt = conn.prepare("SELECT name, value FROM metadata")?; +// let mdata = stmt +// .query_map([], |row| { +// Ok( +// MetadataRow { +// name: row.get(0)?, +// value: row.get(1)?, +// } +// ) +// })? +// .collect::>>(); +// return Ok(mdata?); +// } From b7235eb2f4d840d0af84848b77fc821980afd84c Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Mon, 6 Nov 2023 12:47:26 -0800 Subject: [PATCH 08/80] libutilesqlite not fully done so not using rn --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6ea15603..0ee75d86 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,7 @@ use pyutiles::pylnglatbbox::PyLngLatBbox; use pyutiles::pytile::PyTile; mod pyutiles; -mod pyutilesqlite; +// mod pyutilesqlite; // mod utiles; #[derive(FromPyObject)] @@ -836,4 +836,4 @@ fn libutiles(_py: Python<'_>, m: &PyModule) -> PyResult<()> { m.add_function(wrap_pyfunction!(utcli, m)?)?; Ok(()) -} \ No newline at end of file +} From 518fde4ecb3253a49dfe7a997a73cc7fd056beec Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Mon, 6 Nov 2023 18:57:58 -0800 Subject: [PATCH 09/80] cli! --- crates/utiles-cli/src/cli.rs | 6 +- crates/utiles/src/bbox.rs | 131 +++++++++++++++++++++++++++++++---- src/lib.rs | 10 ++- 3 files changed, 130 insertions(+), 17 deletions(-) diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index 6eea2767..50b3c01b 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -110,6 +110,9 @@ pub enum Commands { #[arg(required = false)] input: Option, + + #[arg(required = false, default_value = "false", long)] + seq: Option, }, // /// Clones repos // #[command(arg_required_else_help = true)] @@ -206,7 +209,7 @@ pub fn cli_main(argv: Option>) { println!("Line from stdin: `{}`", line.unwrap()); } } - Commands::Tiles { zoom, input } => { + Commands::Tiles { zoom, input , seq} => { let thingy = StdInterator::new(input).unwrap(); println!("zoom: {}", zoom); for line in thingy @@ -215,6 +218,7 @@ pub fn cli_main(argv: Option>) { { let lstr = line.unwrap(); println!("Line from stdin: `{}`", lstr); + // let json: serde_json::Value = serde_json::from_str(the_file)l; let thingy = BBox::from(lstr); for tile in tiles( diff --git a/crates/utiles/src/bbox.rs b/crates/utiles/src/bbox.rs index c0e75484..17b69238 100644 --- a/crates/utiles/src/bbox.rs +++ b/crates/utiles/src/bbox.rs @@ -1,4 +1,6 @@ +use serde::de::value; use serde::{Deserialize, Serialize}; +use serde_json::{Result, Value}; use crate::lnglat::LngLat; use crate::tile::Tile; @@ -6,6 +8,15 @@ use crate::tile::Tile; #[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)] pub struct BBoxTuple(f64, f64, f64, f64); +#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)] +pub struct CoordTuple(f64, f64); + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +pub enum BBoxParseAble { + BBoxTuple((f64, f64, f64, f64)), + CoordTuple((f64, f64)), +} + #[derive(Debug, Clone, Copy, PartialEq)] pub struct BBox { pub north: f64, @@ -30,12 +41,7 @@ pub enum BBoxContainable { impl From<(f64, f64, f64, f64)> for BBox { fn from(bbox: (f64, f64, f64, f64)) -> Self { - BBox { - north: bbox.0, - south: bbox.1, - east: bbox.2, - west: bbox.3, - } + BBox::new(bbox.0, bbox.1, bbox.2, bbox.3) } } @@ -58,6 +64,15 @@ impl From<(i32, i32, i32, i32)> for BBox { } impl BBox { + pub fn new(west: f64, south: f64, east: f64, north: f64) -> Self { + BBox { + west: west, + south: south, + east: east, + north: north, + } + } + pub fn crosses_antimeridian(&self) -> bool { self.west > self.east } @@ -193,26 +208,26 @@ impl From for BBoxTuple { impl From for BBox { fn from(tuple: BBoxTuple) -> Self { - BBox { - north: tuple.3, - south: tuple.1, - east: tuple.2, - west: tuple.0, - } + BBox::new(tuple.0, tuple.1, tuple.2, tuple.3) } } - impl From<&String> for BBox { fn from(s: &String) -> Self { // remove leading and trailing quotes let s = s.trim_matches('"'); + // let value: Value = serde_json::from_str(&s).unwrap(); let tuple: BBoxTuple = serde_json::from_str(&s).unwrap(); - self::BBox::from(tuple) } } +impl From<&str> for BBox { + fn from(s: &str) -> Self { + self::BBox::from(&s.to_string()) + } +} + impl From for BBox { fn from(s: String) -> Self { self::BBox::from(&s) @@ -224,3 +239,91 @@ impl From for WebMercatorBbox { crate::xyz2bbox(tile.x, tile.y, tile.z) } } + +// +// pub fn parse_bbox(s: &str) -> Result { +// let parsed : Result= serde_json::from_str(&s); +// if parsed.is_err() { +// // println!("parsed error: {:?}", parsed.err().unwrap()); +// return Err(parsed.err().unwrap()) +// } +// let parsed = parsed.unwrap(); +// let bbox = match parsed { +// BBoxParseAble::CoordTuple(coord) => { +// let bbox = BBox::new(coord.0, coord.1, coord.0, coord.1); +// bbox +// }, +// BBoxParseAble::BBoxTuple(bbox) => { +// let bbox = BBox::from(bbox); +// bbox +// }, +// // BBoxParseAble::Array(array) => { +// // let bbox = BBox::new(array[0], array[1], array[2], array[3]); +// // bbox +// // }, +// }; +// return Ok(bbox); +// +// } + +// pub fn parse_bbox(s: &str) -> Result { +// let parsed: Result = serde_json::from_str(s); +// let bbox = match parsed? { +// BBoxParseAble::CoordTuple(coord) => BBox::new(coord.0, coord.1, coord.0, coord.1), +// BBoxParseAble::BBoxTuple(bbox) => BBox::from(bbox), +// // Uncomment and handle BBoxParseAble::Array(array) if needed +// // BBoxParseAble::Array(array) => BBox::new(array[0], array[1], array[2], array[3]), +// }; +// Ok(bbox) +// } +pub fn parse_bbox(s: &str) -> Result { + let v: Value = serde_json::from_str(s)?; + + // Assume a single pair of coordinates represents a CoordTuple + // and a four-element array represents a BBoxTuple + match v.as_array().map(|arr| arr.len()) { + Some(2) => { + let coord: (f64, f64) = serde_json::from_value(v)?; + Ok(BBox::new(coord.0, coord.1, coord.0, coord.1)) + } + Some(4) => { + let bbox: (f64, f64, f64, f64) = serde_json::from_value(v)?; + Ok(BBox::from(bbox)) + } + _ => Err(panic!( + "Expected a two-element array or a four-element array" + )), + } +} +#[cfg(test)] +mod tests { + use super::*; + + // + // #[test] + // fn parse_bbox_simple(){ + // let string = "[-180.0, -85, 180.0, 85]"; + // let bbox_result = parse_bbox(string); + // assert!(bbox_result.is_ok()); + // let bbox = bbox_result.unwrap(); + // assert_eq!(bbox, BBox::new( -180.0, -85.0, 180.0, 85.0)); + // + // } + + #[test] + fn parse_bbox_simple() { + let string = r#"[-180.0, -85.0, 180.0, 85.0]"#; + let bbox_result = parse_bbox(string); + // assert!(bbox_result.is_ok()); + let bbox = bbox_result.unwrap(); + assert_eq!(bbox, BBox::new(-180.0, -85.0, 180.0, 85.0)); + } + + // + // #[test] + // fn parse_bbox_from_coords(){ + // let string = "[1, 2]"; + // let bbox = parse_bbox(string).unwrap(); + // assert_eq!(bbox, BBox::new(1.0, 2.0, 1.0, 2.0)); + // } +} diff --git a/src/lib.rs b/src/lib.rs index 0ee75d86..745c6df9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -768,11 +768,17 @@ fn utcli(py: Python, args: Option>) { ) } +fn lib_constants(_py: Python<'_>, m: &PyModule) -> PyResult<()> { + m.add("__version_lib__", env!("CARGO_PKG_VERSION"))?; + m.add("__build_profile__", env!("PROFILE"))?; + Ok(()) +} + /// Utiles python module #[pymodule] fn libutiles(_py: Python<'_>, m: &PyModule) -> PyResult<()> { - m.add("__version_lib__", env!("CARGO_PKG_VERSION"))?; - m.add("__build_profile__", env!("PROFILE"))?; + // lib constants + lib_constants(_py, m)?; // mercantile functions m.add_function(wrap_pyfunction!(parse_tile_arg, m)?)?; From 74828f7c24b7c043910db8cdc0c05be99bed975d Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Mon, 6 Nov 2023 19:06:59 -0800 Subject: [PATCH 10/80] parsing module --- crates/utiles/src/bbox.rs | 93 +--------------------------------- crates/utiles/src/lib.rs | 1 + crates/utiles/src/parsing.rs | 96 ++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 92 deletions(-) create mode 100644 crates/utiles/src/parsing.rs diff --git a/crates/utiles/src/bbox.rs b/crates/utiles/src/bbox.rs index 17b69238..9cf948fe 100644 --- a/crates/utiles/src/bbox.rs +++ b/crates/utiles/src/bbox.rs @@ -1,9 +1,6 @@ -use serde::de::value; -use serde::{Deserialize, Serialize}; -use serde_json::{Result, Value}; - use crate::lnglat::LngLat; use crate::tile::Tile; +use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)] pub struct BBoxTuple(f64, f64, f64, f64); @@ -239,91 +236,3 @@ impl From for WebMercatorBbox { crate::xyz2bbox(tile.x, tile.y, tile.z) } } - -// -// pub fn parse_bbox(s: &str) -> Result { -// let parsed : Result= serde_json::from_str(&s); -// if parsed.is_err() { -// // println!("parsed error: {:?}", parsed.err().unwrap()); -// return Err(parsed.err().unwrap()) -// } -// let parsed = parsed.unwrap(); -// let bbox = match parsed { -// BBoxParseAble::CoordTuple(coord) => { -// let bbox = BBox::new(coord.0, coord.1, coord.0, coord.1); -// bbox -// }, -// BBoxParseAble::BBoxTuple(bbox) => { -// let bbox = BBox::from(bbox); -// bbox -// }, -// // BBoxParseAble::Array(array) => { -// // let bbox = BBox::new(array[0], array[1], array[2], array[3]); -// // bbox -// // }, -// }; -// return Ok(bbox); -// -// } - -// pub fn parse_bbox(s: &str) -> Result { -// let parsed: Result = serde_json::from_str(s); -// let bbox = match parsed? { -// BBoxParseAble::CoordTuple(coord) => BBox::new(coord.0, coord.1, coord.0, coord.1), -// BBoxParseAble::BBoxTuple(bbox) => BBox::from(bbox), -// // Uncomment and handle BBoxParseAble::Array(array) if needed -// // BBoxParseAble::Array(array) => BBox::new(array[0], array[1], array[2], array[3]), -// }; -// Ok(bbox) -// } -pub fn parse_bbox(s: &str) -> Result { - let v: Value = serde_json::from_str(s)?; - - // Assume a single pair of coordinates represents a CoordTuple - // and a four-element array represents a BBoxTuple - match v.as_array().map(|arr| arr.len()) { - Some(2) => { - let coord: (f64, f64) = serde_json::from_value(v)?; - Ok(BBox::new(coord.0, coord.1, coord.0, coord.1)) - } - Some(4) => { - let bbox: (f64, f64, f64, f64) = serde_json::from_value(v)?; - Ok(BBox::from(bbox)) - } - _ => Err(panic!( - "Expected a two-element array or a four-element array" - )), - } -} -#[cfg(test)] -mod tests { - use super::*; - - // - // #[test] - // fn parse_bbox_simple(){ - // let string = "[-180.0, -85, 180.0, 85]"; - // let bbox_result = parse_bbox(string); - // assert!(bbox_result.is_ok()); - // let bbox = bbox_result.unwrap(); - // assert_eq!(bbox, BBox::new( -180.0, -85.0, 180.0, 85.0)); - // - // } - - #[test] - fn parse_bbox_simple() { - let string = r#"[-180.0, -85.0, 180.0, 85.0]"#; - let bbox_result = parse_bbox(string); - // assert!(bbox_result.is_ok()); - let bbox = bbox_result.unwrap(); - assert_eq!(bbox, BBox::new(-180.0, -85.0, 180.0, 85.0)); - } - - // - // #[test] - // fn parse_bbox_from_coords(){ - // let string = "[1, 2]"; - // let bbox = parse_bbox(string).unwrap(); - // assert_eq!(bbox, BBox::new(1.0, 2.0, 1.0, 2.0)); - // } -} diff --git a/crates/utiles/src/lib.rs b/crates/utiles/src/lib.rs index 1381eb3b..94eeef38 100644 --- a/crates/utiles/src/lib.rs +++ b/crates/utiles/src/lib.rs @@ -16,6 +16,7 @@ pub mod bbox; pub mod constants; pub mod libtiletype; pub mod lnglat; +mod parsing; pub mod pmtiles; pub mod sibling_relationship; pub mod tile; diff --git a/crates/utiles/src/parsing.rs b/crates/utiles/src/parsing.rs new file mode 100644 index 00000000..eb69c68d --- /dev/null +++ b/crates/utiles/src/parsing.rs @@ -0,0 +1,96 @@ +use crate::bbox::BBox; +use serde_json::Value; + +// pub fn parse_bbox(s: &str) -> Result { +// let parsed: Result = serde_json::from_str(s); +// let bbox = match parsed? { +// BBoxParseAble::CoordTuple(coord) => BBox::new(coord.0, coord.1, coord.0, coord.1), +// BBoxParseAble::BBoxTuple(bbox) => BBox::from(bbox), +// // Uncomment and handle BBoxParseAble::Array(array) if needed +// // BBoxParseAble::Array(array) => BBox::new(array[0], array[1], array[2], array[3]), +// }; +// Ok(bbox) +// } +pub fn parse_bbox(s: &str) -> serde_json::Result { + let v: Value = serde_json::from_str(s)?; + + // Assume a single pair of coordinates represents a CoordTuple + // and a four-element array represents a BBoxTuple + match v.as_array().map(|arr| arr.len()) { + Some(2) => { + let coord: (f64, f64) = serde_json::from_value(v)?; + Ok(BBox::new(coord.0, coord.1, coord.0, coord.1)) + } + Some(4) => { + let bbox: (f64, f64, f64, f64) = serde_json::from_value(v)?; + Ok(BBox::from(bbox)) + } + _ => Err(panic!( + "Expected a two-element array or a four-element array" + )), + } +} + +// +// pub fn parse_bbox(s: &str) -> Result { +// let parsed : Result= serde_json::from_str(&s); +// if parsed.is_err() { +// // println!("parsed error: {:?}", parsed.err().unwrap()); +// return Err(parsed.err().unwrap()) +// } +// let parsed = parsed.unwrap(); +// let bbox = match parsed { +// BBoxParseAble::CoordTuple(coord) => { +// let bbox = BBox::new(coord.0, coord.1, coord.0, coord.1); +// bbox +// }, +// BBoxParseAble::BBoxTuple(bbox) => { +// let bbox = BBox::from(bbox); +// bbox +// }, +// // BBoxParseAble::Array(array) => { +// // let bbox = BBox::new(array[0], array[1], array[2], array[3]); +// // bbox +// // }, +// }; +// return Ok(bbox); +// +// } +#[cfg(test)] +mod tests { + use crate::bbox::*; + use crate::parsing::parse_bbox; + + // + // #[test] + // fn parse_bbox_simple(){ + // let string = "[-180.0, -85, 180.0, 85]"; + // let bbox_result = parse_bbox(string); + // assert!(bbox_result.is_ok()); + // let bbox = bbox_result.unwrap(); + // assert_eq!(bbox, BBox::new( -180.0, -85.0, 180.0, 85.0)); + // + // } + + #[test] + fn parse_bbox_simple() { + let string = r#"[-180.0, -85.0, 180.0, 85.0]"#; + let bbox_result = parse_bbox(string); + // assert!(bbox_result.is_ok()); + let bbox = bbox_result.unwrap(); + assert_eq!(bbox, BBox::new(-180.0, -85.0, 180.0, 85.0)); + } + + #[test] + fn parse_bbox_from_coords() { + let string = "[-180.0, -85.0]"; + let bbox_result = parse_bbox(string); + // assert!(bbox_result.is_ok()); + let bbox = bbox_result.unwrap(); + assert_eq!(bbox, BBox::new(-180.0, -85.0, -180.0, -85.0)); + } + // let string = "[1, 2]"; + // let bbox = parse_bbox(string).unwrap(); + // assert_eq!(bbox, BBox::new(1.0, 2.0, 1.0, 2.0)); + // } +} From f5c6185932cc923da99907acca247c679eab1404 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Mon, 6 Nov 2023 19:19:05 -0800 Subject: [PATCH 11/80] funky --- crates/utiles/src/bbox.rs | 3 ++- crates/utiles/src/parsing.rs | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/utiles/src/bbox.rs b/crates/utiles/src/bbox.rs index 9cf948fe..78159b73 100644 --- a/crates/utiles/src/bbox.rs +++ b/crates/utiles/src/bbox.rs @@ -1,4 +1,5 @@ use crate::lnglat::LngLat; +use crate::parsing::parse_bbox; use crate::tile::Tile; use serde::{Deserialize, Serialize}; @@ -221,7 +222,7 @@ impl From<&String> for BBox { impl From<&str> for BBox { fn from(s: &str) -> Self { - self::BBox::from(&s.to_string()) + parse_bbox(s).unwrap() } } diff --git a/crates/utiles/src/parsing.rs b/crates/utiles/src/parsing.rs index eb69c68d..2c1206cf 100644 --- a/crates/utiles/src/parsing.rs +++ b/crates/utiles/src/parsing.rs @@ -25,9 +25,7 @@ pub fn parse_bbox(s: &str) -> serde_json::Result { let bbox: (f64, f64, f64, f64) = serde_json::from_value(v)?; Ok(BBox::from(bbox)) } - _ => Err(panic!( - "Expected a two-element array or a four-element array" - )), + _ => (panic!("Expected a two-element array or a four-element array")), } } From 3f4861a123f2f527591853a573e6d2b46998d592 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Mon, 6 Nov 2023 19:19:47 -0800 Subject: [PATCH 12/80] cargo fmt --- crates/utiles/src/bbox.rs | 10 +++---- crates/utiles/src/parsing.rs | 52 +----------------------------------- 2 files changed, 6 insertions(+), 56 deletions(-) diff --git a/crates/utiles/src/bbox.rs b/crates/utiles/src/bbox.rs index 78159b73..ebe983dc 100644 --- a/crates/utiles/src/bbox.rs +++ b/crates/utiles/src/bbox.rs @@ -64,10 +64,10 @@ impl From<(i32, i32, i32, i32)> for BBox { impl BBox { pub fn new(west: f64, south: f64, east: f64, north: f64) -> Self { BBox { - west: west, - south: south, - east: east, - north: north, + west, + south, + east, + north, } } @@ -215,7 +215,7 @@ impl From<&String> for BBox { // remove leading and trailing quotes let s = s.trim_matches('"'); // let value: Value = serde_json::from_str(&s).unwrap(); - let tuple: BBoxTuple = serde_json::from_str(&s).unwrap(); + let tuple: BBoxTuple = serde_json::from_str(s).unwrap(); self::BBox::from(tuple) } } diff --git a/crates/utiles/src/parsing.rs b/crates/utiles/src/parsing.rs index 2c1206cf..32036c99 100644 --- a/crates/utiles/src/parsing.rs +++ b/crates/utiles/src/parsing.rs @@ -1,16 +1,6 @@ use crate::bbox::BBox; use serde_json::Value; -// pub fn parse_bbox(s: &str) -> Result { -// let parsed: Result = serde_json::from_str(s); -// let bbox = match parsed? { -// BBoxParseAble::CoordTuple(coord) => BBox::new(coord.0, coord.1, coord.0, coord.1), -// BBoxParseAble::BBoxTuple(bbox) => BBox::from(bbox), -// // Uncomment and handle BBoxParseAble::Array(array) if needed -// // BBoxParseAble::Array(array) => BBox::new(array[0], array[1], array[2], array[3]), -// }; -// Ok(bbox) -// } pub fn parse_bbox(s: &str) -> serde_json::Result { let v: Value = serde_json::from_str(s)?; @@ -25,51 +15,15 @@ pub fn parse_bbox(s: &str) -> serde_json::Result { let bbox: (f64, f64, f64, f64) = serde_json::from_value(v)?; Ok(BBox::from(bbox)) } - _ => (panic!("Expected a two-element array or a four-element array")), + _ => panic!("Expected a two-element array or a four-element array"), } } -// -// pub fn parse_bbox(s: &str) -> Result { -// let parsed : Result= serde_json::from_str(&s); -// if parsed.is_err() { -// // println!("parsed error: {:?}", parsed.err().unwrap()); -// return Err(parsed.err().unwrap()) -// } -// let parsed = parsed.unwrap(); -// let bbox = match parsed { -// BBoxParseAble::CoordTuple(coord) => { -// let bbox = BBox::new(coord.0, coord.1, coord.0, coord.1); -// bbox -// }, -// BBoxParseAble::BBoxTuple(bbox) => { -// let bbox = BBox::from(bbox); -// bbox -// }, -// // BBoxParseAble::Array(array) => { -// // let bbox = BBox::new(array[0], array[1], array[2], array[3]); -// // bbox -// // }, -// }; -// return Ok(bbox); -// -// } #[cfg(test)] mod tests { use crate::bbox::*; use crate::parsing::parse_bbox; - // - // #[test] - // fn parse_bbox_simple(){ - // let string = "[-180.0, -85, 180.0, 85]"; - // let bbox_result = parse_bbox(string); - // assert!(bbox_result.is_ok()); - // let bbox = bbox_result.unwrap(); - // assert_eq!(bbox, BBox::new( -180.0, -85.0, 180.0, 85.0)); - // - // } - #[test] fn parse_bbox_simple() { let string = r#"[-180.0, -85.0, 180.0, 85.0]"#; @@ -87,8 +41,4 @@ mod tests { let bbox = bbox_result.unwrap(); assert_eq!(bbox, BBox::new(-180.0, -85.0, -180.0, -85.0)); } - // let string = "[1, 2]"; - // let bbox = parse_bbox(string).unwrap(); - // assert_eq!(bbox, BBox::new(1.0, 2.0, 1.0, 2.0)); - // } } From 435c28c26d89e4863984396c4a0d9ef7286f2ca6 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Tue, 7 Nov 2023 07:48:38 -0800 Subject: [PATCH 13/80] exception handling.... --- crates/utiles-cli/src/cli.rs | 36 ++++++++++++++++++++++++------------ crates/utiles/src/parsing.rs | 8 ++++++++ pyproject.toml | 3 ++- src/cli.rs | 16 ++++++++++++++++ src/lib.rs | 16 ++-------------- 5 files changed, 52 insertions(+), 27 deletions(-) create mode 100644 src/cli.rs diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index 50b3c01b..cefe9dd7 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -73,11 +73,11 @@ pub struct Cli { // debug flag #[arg( - long, - short, - global = true, - default_value = "false", - help = "debug mode" + long, + short, + global = true, + default_value = "false", + help = "debug mode" )] debug: bool, // #[command(flatten , help="verbosity level (-v, -vv, -vvv, -vvvv)" )] @@ -100,7 +100,7 @@ pub enum Commands { // #[arg(required = true)] // quadkey: String, // }, - #[command(name = "quadkey", visible_alias= "qk", about = "convert xyz <-> quadkey", long_about = None)] + #[command(name = "quadkey", visible_alias = "qk", about = "convert xyz <-> quadkey", long_about = None)] Quadkey(QuadkeyArgs), /// tiles @@ -174,7 +174,7 @@ impl std::fmt::Display for ColorWhen { } } -pub fn cli_main(argv: Option>) { +pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { // print args let argv = match argv { Some(argv) => argv, @@ -209,15 +209,16 @@ pub fn cli_main(argv: Option>) { println!("Line from stdin: `{}`", line.unwrap()); } } - Commands::Tiles { zoom, input , seq} => { - let thingy = StdInterator::new(input).unwrap(); - println!("zoom: {}", zoom); - for line in thingy + Commands::Tiles { zoom, input, seq } => { + let input_lines = StdInterator::new(input).unwrap(); + // println!("zoom: {}", zoom); + let mut niter = 0; + for line in input_lines .filter(|l| !l.is_err()) .filter(|l| !l.as_ref().unwrap().is_empty()) { let lstr = line.unwrap(); - println!("Line from stdin: `{}`", lstr); + // println!("Line from stdin: `{}`", lstr); // let json: serde_json::Value = serde_json::from_str(the_file)l; let thingy = BBox::from(lstr); @@ -226,6 +227,17 @@ pub fn cli_main(argv: Option>) { ZoomOrZooms::Zoom(zoom), ) { println!("{}", tile.json_arr()); + + // call loop_fn if it's defined + niter += 1; + + // call fn every 1000 iterations + if niter % 1000 == 0{ + + if let Some(f) = loop_fn { + f(); + } + } } } } diff --git a/crates/utiles/src/parsing.rs b/crates/utiles/src/parsing.rs index 32036c99..26beccd8 100644 --- a/crates/utiles/src/parsing.rs +++ b/crates/utiles/src/parsing.rs @@ -1,6 +1,7 @@ use crate::bbox::BBox; use serde_json::Value; +// pub fn parse_bbox(s: &str) -> serde_json::Result { pub fn parse_bbox(s: &str) -> serde_json::Result { let v: Value = serde_json::from_str(s)?; @@ -41,4 +42,11 @@ mod tests { let bbox = bbox_result.unwrap(); assert_eq!(bbox, BBox::new(-180.0, -85.0, -180.0, -85.0)); } + + #[test] + fn parse_bbox_bad() { + let string = r#"[-180.0,]"#; + let bbox_result = parse_bbox(string); + assert!(bbox_result.is_err()); + } } diff --git a/pyproject.toml b/pyproject.toml index 251a948e..a3315f2d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,8 @@ testpaths = [ # "python" ] addopts = [ - "--doctest-modules" + "--doctest-modules", + "-v" ] markers = [ "slow: marks tests as slow (deselect with '-m \"not slow\"')", diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 00000000..5267b65b --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,16 @@ +use pyo3::{pyfunction, PyResult, Python}; +use utiles_cli::cli::cli_main; + +#[pyfunction] +pub fn utcli(py: Python, args: Option>) { + let argv = match args { + Some(args) => args, + None => std::env::args().collect(), + }; + cli_main( + Option::Some(argv), + Option::Some(&|| { + py.check_signals().unwrap(); + }), + ) +} diff --git a/src/lib.rs b/src/lib.rs index 745c6df9..5ac1f7ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,8 +6,6 @@ use pyo3::types::{PyDict, PyTuple}; use utiles::bbox::BBox; use utiles::libtiletype; use utiles::zoom::ZoomOrZooms; -use utiles_cli::cli::cli_main; - use pyutiles::pybbox::PyBbox; use pyutiles::pyiters::CoordinateIterator; use pyutiles::pylnglat::PyLngLat; @@ -15,6 +13,7 @@ use pyutiles::pylnglatbbox::PyLngLatBbox; use pyutiles::pytile::PyTile; mod pyutiles; +mod cli; // mod pyutilesqlite; // mod utiles; @@ -757,17 +756,6 @@ fn feature( Ok(f) } -#[pyfunction] -fn utcli(py: Python, args: Option>) { - let argv = match args { - Some(args) => args, - None => std::env::args().collect(), - }; - cli_main( - Option::Some(argv) - ) -} - fn lib_constants(_py: Python<'_>, m: &PyModule) -> PyResult<()> { m.add("__version_lib__", env!("CARGO_PKG_VERSION"))?; m.add("__build_profile__", env!("PROFILE"))?; @@ -839,7 +827,7 @@ fn libutiles(_py: Python<'_>, m: &PyModule) -> PyResult<()> { // m.add_function(wrap_pyfunction!(query_db, m)?)?; // rust-cli - m.add_function(wrap_pyfunction!(utcli, m)?)?; + m.add_function(wrap_pyfunction!(cli::utcli, m)?)?; Ok(()) } From 0db78787005e277c5394d0e00ee9760945181866 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Tue, 7 Nov 2023 12:51:38 -0800 Subject: [PATCH 14/80] aha! --- Cargo.lock | 66 ++++++ crates/utiles-cli/Cargo.lock | 166 ++++++++++++++- crates/utiles-cli/Cargo.toml | 3 +- crates/utiles-cli/src/bin.rs | 3 +- crates/utiles-cli/src/cli.rs | 147 +++++++------ crates/utiles-cli/src/lib.rs | 1 + crates/utiles-cli/src/stdinterator.rs | 58 +++++ crates/utiles/Cargo.toml | 2 + crates/utiles/src/lib.rs | 4 +- .../utiles/src/mbtiles/metadata2tilejson.rs | 70 ++++++ crates/utiles/src/mbtiles/metadata_row.rs | 6 + crates/utiles/src/mbtiles/mod.rs | 3 + crates/utiles/src/tilejson.rs | 13 ++ crates/utilesqlite/Cargo.lock | 199 +++++++++++++++++- crates/utilesqlite/Cargo.toml | 7 +- crates/utilesqlite/src/bin.rs | 74 ++++--- crates/utilesqlite/src/lib.rs | 4 + crates/utilesqlite/src/mbtiles.rs | 84 ++++++-- python/utiles/__init__.py | 8 +- python/utiles/libutiles.pyi | 11 + src/cli.rs | 2 +- src/lib.rs | 14 +- src/pyutiles/pytile.rs | 2 +- 23 files changed, 808 insertions(+), 139 deletions(-) create mode 100644 crates/utiles-cli/src/stdinterator.rs create mode 100644 crates/utiles/src/mbtiles/metadata2tilejson.rs create mode 100644 crates/utiles/src/mbtiles/metadata_row.rs create mode 100644 crates/utiles/src/mbtiles/mod.rs create mode 100644 crates/utiles/src/tilejson.rs diff --git a/Cargo.lock b/Cargo.lock index 7bc704db..0ab380ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,6 +29,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.16" @@ -347,6 +356,15 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "memchr" version = "2.6.4" @@ -572,6 +590,50 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "rusqlite" version = "0.29.0" @@ -792,12 +854,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ + "matchers", "nu-ansi-term", + "once_cell", + "regex", "serde", "serde_json", "sharded-slab", "smallvec", "thread_local", + "tracing", "tracing-core", "tracing-log", ] diff --git a/crates/utiles-cli/Cargo.lock b/crates/utiles-cli/Cargo.lock index bf41587b..7a9f10af 100644 --- a/crates/utiles-cli/Cargo.lock +++ b/crates/utiles-cli/Cargo.lock @@ -29,6 +29,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.16" @@ -187,7 +196,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -202,6 +211,25 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -322,6 +350,15 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "memchr" version = "2.6.4" @@ -461,6 +498,50 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "rusqlite" version = "0.29.0" @@ -510,7 +591,7 @@ checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -524,6 +605,27 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_tuple" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f025b91216f15a2a32aa39669329a475733590a015835d1783549a56d09427" +dependencies = [ + "serde", + "serde_tuple_macros", +] + +[[package]] +name = "serde_tuple_macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4076151d1a2b688e25aaf236997933c66e18b870d0369f8b248b8ab2be630d7e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -564,6 +666,17 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.38" @@ -585,6 +698,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tilejson" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a16212e5ea60f2b406835981338a1df9b206fab312e3470502d60f69e590a9c9" +dependencies = [ + "serde", + "serde_json", + "serde_tuple", +] + [[package]] name = "tokio" version = "1.33.0" @@ -612,7 +736,18 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", +] + +[[package]] +name = "tokio-rusqlite" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aa66395f5ff117faee90c9458232c936405f9227ad902038000b74b3bc1feac" +dependencies = [ + "crossbeam-channel", + "rusqlite", + "tokio", ] [[package]] @@ -634,7 +769,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -664,12 +799,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ + "matchers", "nu-ansi-term", + "once_cell", + "regex", "serde", "serde_json", "sharded-slab", "smallvec", "thread_local", + "tracing", "tracing-core", "tracing-log", ] @@ -694,6 +833,8 @@ dependencies = [ "geo-types", "serde", "serde_json", + "tilejson", + "tracing", ] [[package]] @@ -707,6 +848,21 @@ dependencies = [ "tracing", "tracing-subscriber", "utiles", + "utilesqlite", +] + +[[package]] +name = "utilesqlite" +version = "0.1.0" +dependencies = [ + "rusqlite", + "serde", + "serde_json", + "tilejson", + "tokio", + "tokio-rusqlite", + "tracing", + "utiles", ] [[package]] @@ -838,5 +994,5 @@ checksum = "020f3dfe25dfc38dfea49ce62d5d45ecdd7f0d8a724fa63eb36b6eba4ec76806" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] diff --git a/crates/utiles-cli/Cargo.toml b/crates/utiles-cli/Cargo.toml index 85bc904f..54910cfb 100644 --- a/crates/utiles-cli/Cargo.toml +++ b/crates/utiles-cli/Cargo.toml @@ -17,5 +17,6 @@ clap-verbosity-flag = "2.1.0" rusqlite = { version = "0.29.0", features = ["bundled", "vtab", "blob"] } tokio = { version = "1.33.0", features = ["full"] } tracing = { version = "0.1.40", features = [] } -tracing-subscriber = { version = "0.3.17", features = ["serde", "serde_json"] } +tracing-subscriber = { version = "0.3.17", features = ["serde", "serde_json", "env-filter"] } utiles = { path = "../utiles" } +utilesqlite = { path = "../utilesqlite" } diff --git a/crates/utiles-cli/src/bin.rs b/crates/utiles-cli/src/bin.rs index 011ca547..22709723 100644 --- a/crates/utiles-cli/src/bin.rs +++ b/crates/utiles-cli/src/bin.rs @@ -1,6 +1,7 @@ mod cli; +mod stdinterator; #[tokio::main] async fn main() { - cli::cli_main(Option::None) + cli::cli_main(Option::None, Option::None) } diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index cefe9dd7..09405a6f 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -1,67 +1,16 @@ use clap::{Parser, Subcommand, ValueEnum}; -use std::io; -use std::io::BufRead; use tracing::debug; use tracing_subscriber::util::SubscriberInitExt; -use utiles::bbox::BBox; +use tracing_subscriber::EnvFilter; +use utiles::parsing::parse_bbox; +use utiles::tilejson::tilejson_stringify; use utiles::tiles; use utiles::zoom::ZoomOrZooms; +use crate::stdinterator::StdInterator; +use utilesqlite::mbtiles::Mbtiles; -pub enum StdInteratorSource { - Single(String), - Multiple(Box), -} - -pub struct StdInterator { - source: StdInteratorSource, -} - -impl StdInterator { - fn new(input: Option) -> io::Result { - let source = match input { - Some(file_content) => { - if file_content == "-" { - debug!("reading from stdin - got '-'"); - let reader = Box::new(io::BufReader::new(io::stdin())); - StdInteratorSource::Multiple(reader) - } else { - debug!("reading from args: {:?}", file_content); - StdInteratorSource::Single(file_content) - } - } - None => { - let reader = Box::new(io::BufReader::new(io::stdin())); - debug!("reading from stdin - no args"); - StdInteratorSource::Multiple(reader) - } - }; - Ok(Self { source }) - } -} -impl Iterator for StdInterator { - type Item = io::Result; - fn next(&mut self) -> Option { - match &mut self.source { - StdInteratorSource::Single(content) => { - if content.is_empty() { - None - } else { - Some(Ok(std::mem::take(content))) - } - } - StdInteratorSource::Multiple(reader) => { - let mut line = String::new(); - match reader.read_line(&mut line) { - Ok(0) => None, // EOF - Ok(_) => Some(Ok(line.trim_end().to_string())), - Err(e) => Some(Err(e)), - } - } - } - } -} /// A fictional versioning CLI #[derive(Debug, Parser)] // requires `derive` feature @@ -111,9 +60,26 @@ pub enum Commands { #[arg(required = false)] input: Option, - #[arg(required = false, default_value = "false", long)] - seq: Option, + #[arg(required = false, long, action = clap::ArgAction::SetTrue)] + seq: bool, + }, + + + #[command(name = "lint", about = "lint mbtiles file", long_about = None)] + Lint { + #[arg(required = true)] + filepath: String, + + #[arg(required = false, long, action = clap::ArgAction::SetTrue)] + fix: bool, }, + + #[command(name = "tilejson", about = "output tilejson", long_about = None)] + Tilejson { + #[arg(required = true)] + filepath: String, + }, + // /// Clones repos // #[command(arg_required_else_help = true)] // Clone { @@ -184,24 +150,38 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { let args = Cli::parse_from(&argv); // level is info by default and debug if --debug is passed - let level = if args.debug { - tracing::Level::DEBUG + // let level = if args.debug { + // tracing::Level::DEBUG + // } else { + // tracing::Level::WARN + // }; + + // install global collector configured based on RUST_LOG env var. + // tracing_subscriber::fmt() + // .with_max_level(level) + // .with_writer(std::io::stderr) + // .finish() + // .init(); + // Configure the filter + + + let filter = if args.debug { + EnvFilter::new("DEBUG") } else { - tracing::Level::WARN + EnvFilter::from_default_env() }; - // install global collector configured based on RUST_LOG env var. - // tracing_subscriber::fmt::init(); + // Install the global collector configured based on the filter. tracing_subscriber::fmt() - .with_max_level(level) + .with_env_filter(filter) .with_writer(std::io::stderr) - .finish() .init(); debug!("args: {:?}", std::env::args().collect::>()); debug!("argv: {:?}", argv); debug!("args: {:?}", args); + match args.command { Commands::Quadkey(quadkey) => { let thingy = StdInterator::new(quadkey.quadkey).unwrap(); @@ -220,7 +200,19 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { let lstr = line.unwrap(); // println!("Line from stdin: `{}`", lstr); // let json: serde_json::Value = serde_json::from_str(the_file)l; - let thingy = BBox::from(lstr); + + let thingy = parse_bbox( + &lstr, + ).unwrap(); + + // match thingy { + // Ok(thingy) => thingy, + // Err(e) => { + // println!("Error parsing bbox: {}", e); + // continue; + // } + // } + // let thingy = BBox::from(lstr); for tile in tiles( (thingy.west, thingy.south, thingy.east, thingy.north), @@ -241,5 +233,28 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { } } } + + Commands::Lint { filepath, fix } => { + println!("linting: {}", filepath); + println!("NOT IMPLEMENTED YET"); + } + + Commands::Tilejson { filepath } => { + println!("tilejson: {}", filepath); + println!("NOT IMPLEMENTED YET"); + let mbtiles = Mbtiles::from_filepath( + &filepath + ).unwrap(); + let tj = mbtiles.tilejson().unwrap(); + + let s =tilejson_stringify(&tj, None); + + println!("{}", s); + + // println!( + // "{}", + // serde_json::to_string_pretty(&tj).unwrap() + // ); + } } } diff --git a/crates/utiles-cli/src/lib.rs b/crates/utiles-cli/src/lib.rs index 4f773726..ab0653ee 100644 --- a/crates/utiles-cli/src/lib.rs +++ b/crates/utiles-cli/src/lib.rs @@ -1 +1,2 @@ pub mod cli; +pub mod stdinterator; \ No newline at end of file diff --git a/crates/utiles-cli/src/stdinterator.rs b/crates/utiles-cli/src/stdinterator.rs new file mode 100644 index 00000000..f5d7fc66 --- /dev/null +++ b/crates/utiles-cli/src/stdinterator.rs @@ -0,0 +1,58 @@ +use std::io; +use std::io::BufRead; +use tracing::debug; + +pub enum StdInteratorSource { + Single(String), + Multiple(Box), +} + +pub struct StdInterator { + source: StdInteratorSource, +} + +impl StdInterator { + pub fn new(input: Option) -> io::Result { + let source = match input { + Some(file_content) => { + if file_content == "-" { + debug!("reading from stdin - got '-'"); + let reader = Box::new(io::BufReader::new(io::stdin())); + StdInteratorSource::Multiple(reader) + } else { + debug!("reading from args: {:?}", file_content); + StdInteratorSource::Single(file_content) + } + } + None => { + let reader = Box::new(io::BufReader::new(io::stdin())); + debug!("reading from stdin - no args"); + StdInteratorSource::Multiple(reader) + } + }; + Ok(Self { source }) + } +} + +impl Iterator for StdInterator { + type Item = io::Result; + fn next(&mut self) -> Option { + match &mut self.source { + StdInteratorSource::Single(content) => { + if content.is_empty() { + None + } else { + Some(Ok(std::mem::take(content))) + } + } + StdInteratorSource::Multiple(reader) => { + let mut line = String::new(); + match reader.read_line(&mut line) { + Ok(0) => None, // EOF + Ok(_) => Some(Ok(line.trim_end().to_string())), + Err(e) => Some(Err(e)), + } + } + } + } +} diff --git a/crates/utiles/Cargo.toml b/crates/utiles/Cargo.toml index c7e476bf..1cfe93de 100644 --- a/crates/utiles/Cargo.toml +++ b/crates/utiles/Cargo.toml @@ -18,3 +18,5 @@ fast_hilbert = "2.0.0" geo-types = "0.7.9" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.96" +tilejson = "0.3.2" +tracing = { version = "0.1.40", features = [] } \ No newline at end of file diff --git a/crates/utiles/src/lib.rs b/crates/utiles/src/lib.rs index 94eeef38..2e6cf62d 100644 --- a/crates/utiles/src/lib.rs +++ b/crates/utiles/src/lib.rs @@ -16,13 +16,15 @@ pub mod bbox; pub mod constants; pub mod libtiletype; pub mod lnglat; -mod parsing; pub mod pmtiles; pub mod sibling_relationship; pub mod tile; pub mod tile_range; pub mod traits; pub mod zoom; +pub mod parsing; +pub mod mbtiles; +pub mod tilejson; /// Tile macro to create a new tile. /// - do you need this? probably not diff --git a/crates/utiles/src/mbtiles/metadata2tilejson.rs b/crates/utiles/src/mbtiles/metadata2tilejson.rs new file mode 100644 index 00000000..2bf86656 --- /dev/null +++ b/crates/utiles/src/mbtiles/metadata2tilejson.rs @@ -0,0 +1,70 @@ +use std::error::Error; +use std::fmt::Display; +use std::str::FromStr; + +use serde_json::{Value as JSONValue, Value}; + +use tilejson::{Bounds, Center, tilejson, TileJSON}; +use tracing::{debug, error, info, Level, span, warn}; +use crate::mbtiles::metadata_row::MbtilesMetadataRow; + +fn to_val(val: Result, title: &str) -> Option { + match val { + Ok(v) => Some(v), + Err(err) => { + // let name = &self.filename; + warn!("Unable to parse metadata {title}"); + None + } + } +} + +pub fn metadata2tilejson(metadata: Vec) -> Result> { + let mut tj = tilejson! {tiles : vec![]}; + let mut layer_type: Option = None; + let mut json: Option = None; + + + for row in metadata { + let name = row.name; + let value = row.value; + match name.as_ref() { + "name" => tj.name = Some(value), + "version" => tj.version = Some(value), + "bounds" => tj.bounds = to_val(Bounds::from_str(value.as_str()), &name), + "center" => tj.center = to_val(Center::from_str(value.as_str()), &name), + "minzoom" => tj.minzoom = to_val(value.parse(), &name), + "maxzoom" => tj.maxzoom = to_val(value.parse(), &name), + "description" => tj.description = Some(value), + "attribution" => tj.attribution = Some(value), + "type" => layer_type = Some(value), + "legend" => tj.legend = Some(value), + "template" => tj.template = Some(value), + "json" => json = to_val(serde_json::from_str(&value), &name), + "format" | "generator" => { + tj.other.insert(name, Value::String(value)); + } + _ => { + // let file = &filename; + // info!("{file} has an unrecognized metadata value {name}={value}"); + info!("unrecognized metadata value {name}={value}"); + tj.other.insert(name, Value::String(value)); + } + } + } + + if let Some(JSONValue::Object(obj)) = &mut json { + if let Some(value) = obj.remove("vector_layers") { + if let Ok(v) = serde_json::from_value(value) { + tj.vector_layers = Some(v); + } else { + warn!( + "Unable to parse metadata vector_layers value", + // self.filename + + ); + } + } + } + Ok(tj) +} diff --git a/crates/utiles/src/mbtiles/metadata_row.rs b/crates/utiles/src/mbtiles/metadata_row.rs new file mode 100644 index 00000000..425d3965 --- /dev/null +++ b/crates/utiles/src/mbtiles/metadata_row.rs @@ -0,0 +1,6 @@ + +#[derive(Debug)] +pub struct MbtilesMetadataRow { + pub name: String, + pub value: String, +} diff --git a/crates/utiles/src/mbtiles/mod.rs b/crates/utiles/src/mbtiles/mod.rs new file mode 100644 index 00000000..d0fe7f1a --- /dev/null +++ b/crates/utiles/src/mbtiles/mod.rs @@ -0,0 +1,3 @@ +pub mod metadata_row; +mod metadata2tilejson; +pub use crate::mbtiles::metadata2tilejson::{metadata2tilejson}; \ No newline at end of file diff --git a/crates/utiles/src/tilejson.rs b/crates/utiles/src/tilejson.rs new file mode 100644 index 00000000..aa623999 --- /dev/null +++ b/crates/utiles/src/tilejson.rs @@ -0,0 +1,13 @@ +use serde_json; +use tilejson::{TileJSON}; + +pub fn tilejson_stringify(tj: &TileJSON, fmt: Option) -> String { + match fmt { + Some(true) => serde_json::to_string(&tj).unwrap(), + _ => serde_json::to_string_pretty(&tj).unwrap(), + } +} + +pub fn tilejson_parse(s: &str) -> Result { + serde_json::from_str(s) +} \ No newline at end of file diff --git a/crates/utilesqlite/Cargo.lock b/crates/utilesqlite/Cargo.lock index 601f1dce..4de59eb3 100644 --- a/crates/utilesqlite/Cargo.lock +++ b/crates/utilesqlite/Cargo.lock @@ -35,6 +35,15 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -120,6 +129,26 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fast_hilbert" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ec2bbe15af87954c739e236021f4411766c0f2b9c4a5f0b9317bcf6048ebf8" +dependencies = [ + "num-traits", +] + +[[package]] +name = "geo-types" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9705398c5c7b26132e74513f4ee7c1d7dafd786004991b375c172be2be0eecaa" +dependencies = [ + "approx", + "num-traits", + "serde", +] + [[package]] name = "gimli" version = "0.28.0" @@ -151,11 +180,23 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + [[package]] name = "libc" -version = "0.2.149" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "libm" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libsqlite3-sys" @@ -204,6 +245,16 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", + "libm", +] + [[package]] name = "num_cpus" version = "1.16.0" @@ -311,12 +362,70 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "serde" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_tuple" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f025b91216f15a2a32aa39669329a475733590a015835d1783549a56d09427" +dependencies = [ + "serde", + "serde_tuple_macros", +] + +[[package]] +name = "serde_tuple_macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4076151d1a2b688e25aaf236997933c66e18b870d0369f8b248b8ab2be630d7e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -344,15 +453,37 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.38" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "tilejson" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a16212e5ea60f2b406835981338a1df9b206fab312e3470502d60f69e590a9c9" +dependencies = [ + "serde", + "serde_json", + "serde_tuple", +] + [[package]] name = "tokio" version = "1.33.0" @@ -380,7 +511,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -394,19 +525,67 @@ dependencies = [ "tokio", ] +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "utiles" +version = "0.0.1" +dependencies = [ + "fast_hilbert", + "geo-types", + "serde", + "serde_json", + "tilejson", + "tracing", +] + [[package]] name = "utilesqlite" version = "0.1.0" dependencies = [ "rusqlite", + "serde", + "serde_json", + "tilejson", "tokio", "tokio-rusqlite", + "tracing", + "utiles", ] [[package]] @@ -495,20 +674,20 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "zerocopy" -version = "0.7.20" +version = "0.7.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd66a62464e3ffd4e37bd09950c2b9dd6c4f8767380fabba0d523f9a775bc85a" +checksum = "8cd369a67c0edfef15010f980c3cbe45d7f651deac2cd67ce097cd801de16557" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.20" +version = "0.7.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "255c4596d41e6916ced49cfafea18727b24d67878fa180ddfd69b9df34fd1726" +checksum = "c2f140bda219a26ccc0cdb03dba58af72590c53b22642577d88a927bc5c87d6b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] diff --git a/crates/utilesqlite/Cargo.toml b/crates/utilesqlite/Cargo.toml index 7455932d..39cbd9d0 100644 --- a/crates/utilesqlite/Cargo.toml +++ b/crates/utilesqlite/Cargo.toml @@ -8,10 +8,15 @@ name = "utilesqlite" path = "src/lib.rs" [[bin]] -name = "utilesqlite" +name = "utilesql" path = "src/bin.rs" [dependencies] rusqlite = { version = "0.29.0", features = ["bundled", "vtab", "blob"] } +tilejson = "0.3.2" +tracing = { version = "0.1.40", features = [] } tokio = { version = "1.33.0", features = ["full"] } tokio-rusqlite = "0.4.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.96" +utiles = { path = "../utiles" } \ No newline at end of file diff --git a/crates/utilesqlite/src/bin.rs b/crates/utilesqlite/src/bin.rs index e22aa722..1ca935b5 100644 --- a/crates/utilesqlite/src/bin.rs +++ b/crates/utilesqlite/src/bin.rs @@ -1,10 +1,12 @@ use std::collections::HashMap; use std::hash::Hash; - +use serde_json; use rusqlite; -use mbtiles::MbtilesManager; +use utilesqlite::mbtiles::Mbtiles; +// use mbtiles::MbtilesManager; +// use crate::mbtiles::Mbtiles; -mod mbtiles; +// mod mbtiles; // impl From for Error { // fn from(e: tokio_rusqlite::Error) -> Error { @@ -43,34 +45,58 @@ async fn main() -> tokio_rusqlite::Result<()> { // // println!("metadata_has_unique_index_name: {}", mbt.metadata_has_unique_index_name().await?); // - let mut mbtiles_manager = MbtilesManager::new(); - // Open the database connection - mbtiles_manager.open( + + let conn = rusqlite::Connection::open( filepath ).unwrap(); - let mapfn = |row: &rusqlite::Row| -> rusqlite::Result { - Ok(row.get(0)?) - }; + let mbtiles = Mbtiles::from_conn(conn); - let metadata = mbtiles_manager.metadata(); - // Execute a query - let result= mbtiles_manager.query("SELECT name, value FROM metadata", - mapfn - ); - match result { - Ok(rows) => { - for row in rows { - println!("{}", row); - } - } - Err(err) => eprintln!("Query failed: {}", err), + let mdata_arr = mbtiles.metadata().unwrap(); + + // print it + for thing in mdata_arr { + println!("{}: {}", thing.name, thing.value); } - println!("metadata: {:?}", metadata); - // Close the database connection - mbtiles_manager.close().unwrap(); + + let tj = mbtiles.tilejson().unwrap(); + + let tj_str = serde_json::to_string_pretty(&tj).unwrap(); + // serialized + println!( "{}", tj_str + ); + + // + // let mut mbtiles_manager = MbtilesManager::new(); + // + // // Open the database connection + // mbtiles_manager.open( + // filepath + // ).unwrap(); + // + // let mapfn = |row: &rusqlite::Row| -> rusqlite::Result { + // Ok(row.get(0)?) + // }; + // + // let metadata = mbtiles_manager.metadata(); + // // Execute a query + // let result= mbtiles_manager.query("SELECT name, value FROM metadata", + // mapfn + // ); + // match result { + // Ok(rows) => { + // for row in rows { + // println!("{}", row); + // } + // } + // Err(err) => eprintln!("Query failed: {}", err), + // } + // + // println!("metadata: {:?}", metadata); + // // Close the database connection + // mbtiles_manager.close().unwrap(); // // // match c_res { // // Ok(c) => println!("Connection opened"), diff --git a/crates/utilesqlite/src/lib.rs b/crates/utilesqlite/src/lib.rs index 157f5b61..95434d2e 100644 --- a/crates/utilesqlite/src/lib.rs +++ b/crates/utilesqlite/src/lib.rs @@ -1,5 +1,9 @@ // pub use crate::mbtiles::{Mbtiles, MetadataRow, all_metadata}; // pub mod mbtiles; +// mod mbtiles; +// mod metadata_row; +// mod metadata2tilejson; +pub mod mbtiles; pub fn add(left: usize, right: usize) -> usize { left + right diff --git a/crates/utilesqlite/src/mbtiles.rs b/crates/utilesqlite/src/mbtiles.rs index e2be34a7..ba8cd1c1 100644 --- a/crates/utilesqlite/src/mbtiles.rs +++ b/crates/utilesqlite/src/mbtiles.rs @@ -1,21 +1,69 @@ -// #[derive(Debug)] -// pub struct Mbtiles<'a> { -// conn: &'a mut rusqlite::Connection, -// } -use rusqlite::{Connection, Result}; +use serde_json::{Value as JSONValue, Value}; +use rusqlite::{Connection, Result as RusqliteResult}; +use tilejson::{Bounds, Center, tilejson, TileJSON}; +use std::error::Error; +use utiles::mbtiles::metadata_row::MbtilesMetadataRow; +// use crate::metadata_row::MbtilesMetadataRow; +use utiles::mbtiles::{metadata2tilejson}; -pub struct MbtilesManager { - conn: Option, +pub struct Mbtiles { + conn: rusqlite::Connection, } -#[derive(Debug)] -pub struct MbtilesMetadataRow { - pub name: String, - pub value: String, +use tracing::{debug, error, info, span, warn, Level}; + +impl Mbtiles { + pub fn from_conn(conn: rusqlite::Connection) -> Mbtiles { + Mbtiles { + conn: conn, + } + } + + pub fn metadata(&self) -> RusqliteResult> { + return mbtiles_metadata(&self.conn); + // let mut stmt = self.conn.as_ref().unwrap().prepare("SELECT name, value FROM metadata")?; + // let rows = stmt.query_map([], |row| { + // Ok( + // MbtilesMetadataRow { + // name: row.get(0)?, + // value: row.get(1)?, + // } + // ) + // })?; + // rows.collect() + } + + pub fn tilejson(&self) -> Result> { + let metadata = self.metadata()?; + let tj = metadata2tilejson(metadata); + match tj { + Ok(t) => return Ok(t), + Err(e) => { + error!("Error parsing metadata to TileJSON: {}", e); + return Err(e.into()); + } + } + // return Ok(tj); + } + + + pub fn from_filepath(fspath: &str) -> RusqliteResult { + let conn = rusqlite::Connection::open(fspath)?; + let mbt = Mbtiles { + conn: conn, + }; + + return Ok(mbt); + } +} + + +pub struct MbtilesManager { + conn: Option, } -pub fn mbtiles_metadata(conn: &rusqlite::Connection) -> Result> { +pub fn mbtiles_metadata(conn: &rusqlite::Connection) -> RusqliteResult> { let mut stmt = conn.prepare("SELECT name, value FROM metadata")?; let mdata = stmt .query_map([], |row| { @@ -26,7 +74,7 @@ pub fn mbtiles_metadata(conn: &rusqlite::Connection) -> Result, rusqlite::Error>>()?; + .collect::, rusqlite::Error>>()?; return Ok(mdata); } @@ -37,15 +85,15 @@ impl MbtilesManager { } // Open a connection to the MBTiles SQLite database - pub fn open(&mut self, path: &str) -> Result<()> { + pub fn open(&mut self, path: &str) -> RusqliteResult<()> { self.conn = Some(Connection::open(path)?); Ok(()) } // Execute a query on the MBTiles database - pub fn query(&self, sql: &str, mut map_fn: F) -> Result> + pub fn query(&self, sql: &str, mut map_fn: F) -> RusqliteResult> where - F: FnMut(&rusqlite::Row<'_>) -> Result, + F: FnMut(&rusqlite::Row<'_>) -> RusqliteResult, { match &self.conn { Some(conn) => { @@ -57,7 +105,7 @@ impl MbtilesManager { } } - pub fn metadata(&self) -> Result> { + pub fn metadata(&self) -> RusqliteResult> { return mbtiles_metadata(self.conn.as_ref().unwrap()); // let mut stmt = self.conn.as_ref().unwrap().prepare("SELECT name, value FROM metadata")?; // let rows = stmt.query_map([], |row| { @@ -72,7 +120,7 @@ impl MbtilesManager { } // Close the connection to the MBTiles database - pub fn close(&mut self) -> Result<()> { + pub fn close(&mut self) -> RusqliteResult<()> { if let Some(conn) = self.conn.take() { conn.close().map_err(|(_, e)| e) } else { diff --git a/python/utiles/__init__.py b/python/utiles/__init__.py index bf7b2a1c..d28c0aae 100644 --- a/python/utiles/__init__.py +++ b/python/utiles/__init__.py @@ -5,15 +5,15 @@ from typing import List, Sequence, Tuple, Union from utiles import libutiles from utiles.libutiles import ( + Bbox, + LngLat, + LngLatBbox, TILETYPE_GIF, TILETYPE_JPG, TILETYPE_PBF, TILETYPE_PNG, TILETYPE_UNKNOWN, TILETYPE_WEBP, - Bbox, - LngLat, - LngLatBbox, Tile, __build_profile__, __version_lib__, @@ -47,6 +47,7 @@ tiletype_str, truncate_lnglat, ul, + ut_cli, xy, xy_bounds, xyz, @@ -103,6 +104,7 @@ "tiletype_str", "truncate_lnglat", "ul", + "ut_cli", "xy", "xy_bounds", "xyz", diff --git a/python/utiles/libutiles.pyi b/python/utiles/libutiles.pyi index 755676b1..0c4dc0eb 100644 --- a/python/utiles/libutiles.pyi +++ b/python/utiles/libutiles.pyi @@ -206,6 +206,14 @@ def tiles_list( zooms: list[int] | tuple[int, ...] | int, truncate: bool = ..., ) -> list[Tile]: ... +def tiles_count( + west: float, + south: float, + east: float, + north: float, + zooms: list[int] | tuple[int, ...] | int, + truncate: bool = ..., +) -> int: ... def tiletype(buf: bytes) -> int: ... def tiletype2headers(tiletype_int: int) -> list[tuple[str, str]]: ... def tiletype_str(buf: bytes) -> str: ... @@ -224,3 +232,6 @@ def _coords(obj: Any) -> Iterable[Tuple[float, float]]: ... def geojson_bounds(obj: Any) -> LngLatBbox: ... def pmtileid(*tile: TileLike) -> int: ... def from_pmtileid(pmtileid: int) -> Tile: ... + +# CLI +def ut_cli(args: list[str]) -> None: ... diff --git a/src/cli.rs b/src/cli.rs index 5267b65b..202e34ee 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -2,7 +2,7 @@ use pyo3::{pyfunction, PyResult, Python}; use utiles_cli::cli::cli_main; #[pyfunction] -pub fn utcli(py: Python, args: Option>) { +pub fn ut_cli(py: Python, args: Option>) { let argv = match args { Some(args) => args, None => std::env::args().collect(), diff --git a/src/lib.rs b/src/lib.rs index 5ac1f7ce..cfd32189 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,17 +3,17 @@ use std::collections::{HashMap, HashSet}; use pyo3::exceptions::{self, PyValueError}; use pyo3::prelude::*; use pyo3::types::{PyDict, PyTuple}; -use utiles::bbox::BBox; -use utiles::libtiletype; -use utiles::zoom::ZoomOrZooms; use pyutiles::pybbox::PyBbox; use pyutiles::pyiters::CoordinateIterator; use pyutiles::pylnglat::PyLngLat; use pyutiles::pylnglatbbox::PyLngLatBbox; use pyutiles::pytile::PyTile; +use utiles::bbox::BBox; +use utiles::libtiletype; +use utiles::zoom::ZoomOrZooms; -mod pyutiles; mod cli; +mod pyutiles; // mod pyutilesqlite; // mod utiles; @@ -426,7 +426,7 @@ impl From for ZoomOrZooms { #[pyclass] struct TilesGenerator { - iter: Box + Send>, + iter: Box + Send>, length: u64, } @@ -482,7 +482,7 @@ fn tiles( (west, south, east, north), ZoomOrZooms::from(zooms_vec_iter), ) - .map(PyTile::from); + .map(PyTile::from); TilesGenerator { iter: Box::new(xyzs), length: ntiles, @@ -827,7 +827,7 @@ fn libutiles(_py: Python<'_>, m: &PyModule) -> PyResult<()> { // m.add_function(wrap_pyfunction!(query_db, m)?)?; // rust-cli - m.add_function(wrap_pyfunction!(cli::utcli, m)?)?; + m.add_function(wrap_pyfunction!(cli::ut_cli, m)?)?; Ok(()) } diff --git a/src/pyutiles/pytile.rs b/src/pyutiles/pytile.rs index fb8588ff..f2548858 100644 --- a/src/pyutiles/pytile.rs +++ b/src/pyutiles/pytile.rs @@ -13,7 +13,7 @@ use pyo3::types::PyType; use pyo3::exceptions::PyValueError; use pyo3::{ - exceptions, IntoPy, Py, PyAny, pyclass, PyErr, pymethods, PyObject, PyRef, + exceptions, pyclass, pymethods, IntoPy, Py, PyAny, PyErr, PyObject, PyRef, PyResult, Python, }; use serde::{Deserialize, Serialize}; From 76367fdf6991d2fb9a06d4cec6b0754b404c6587 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Tue, 7 Nov 2023 13:12:41 -0800 Subject: [PATCH 15/80] AHA! --- Cargo.lock | 65 +++++++++++++++++++++++++--- crates/utiles-cli/src/cli.rs | 84 +++++++++++++++++++++++++++++------- 2 files changed, 126 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0ab380ff..97be64d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -196,7 +196,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -541,7 +541,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -553,7 +553,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -683,7 +683,7 @@ checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -697,6 +697,27 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_tuple" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f025b91216f15a2a32aa39669329a475733590a015835d1783549a56d09427" +dependencies = [ + "serde", + "serde_tuple_macros", +] + +[[package]] +name = "serde_tuple_macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4076151d1a2b688e25aaf236997933c66e18b870d0369f8b248b8ab2be630d7e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -737,6 +758,17 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.38" @@ -764,6 +796,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tilejson" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a16212e5ea60f2b406835981338a1df9b206fab312e3470502d60f69e590a9c9" +dependencies = [ + "serde", + "serde_json", + "serde_tuple", +] + [[package]] name = "tokio" version = "1.33.0" @@ -791,7 +834,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -824,7 +867,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -894,6 +937,8 @@ dependencies = [ "geo-types", "serde", "serde_json", + "tilejson", + "tracing", ] [[package]] @@ -907,6 +952,7 @@ dependencies = [ "tracing", "tracing-subscriber", "utiles", + "utilesqlite", ] [[package]] @@ -914,8 +960,13 @@ name = "utilesqlite" version = "0.1.0" dependencies = [ "rusqlite", + "serde", + "serde_json", + "tilejson", "tokio", "tokio-rusqlite", + "tracing", + "utiles", ] [[package]] @@ -1047,5 +1098,5 @@ checksum = "255c4596d41e6916ced49cfafea18727b24d67878fa180ddfd69b9df34fd1726" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index 09405a6f..47820b9c 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -11,7 +11,6 @@ use crate::stdinterator::StdInterator; use utilesqlite::mbtiles::Mbtiles; - /// A fictional versioning CLI #[derive(Debug, Parser)] // requires `derive` feature #[command(name = "ut")] @@ -49,10 +48,22 @@ pub enum Commands { // #[arg(required = true)] // quadkey: String, // }, - #[command(name = "quadkey", visible_alias = "qk", about = "convert xyz <-> quadkey", long_about = None)] - Quadkey(QuadkeyArgs), - /// tiles + #[command(name = "lint", about = "lint mbtiles file", long_about = None)] + Lint { + #[arg(required = true)] + filepath: String, + + #[arg(required = false, long, action = clap::ArgAction::SetTrue)] + fix: bool, + }, + #[command(name = "tilejson", visible_alias = "tj", about = "output tilejson", long_about = None)] + Tilejson { + #[arg(required = true)] + filepath: String, + }, + + // MERCANTILE CLIKE (cli+like) Tiles { #[arg(required = true)] zoom: u8, @@ -65,21 +76,59 @@ pub enum Commands { }, - #[command(name = "lint", about = "lint mbtiles file", long_about = None)] - Lint { + // =================== + // NOT IMPLEMENTED YET + // =================== + #[command(name = "quadkey", visible_alias = "qk", about = "convert xyz <-> quadkey", long_about = None)] + Quadkey(QuadkeyArgs), + + #[command(name = "bounding-tile", about = "output tilejson", long_about = None)] + BoundingTile { #[arg(required = true)] - filepath: String, + zoom: u8, - #[arg(required = false, long, action = clap::ArgAction::SetTrue)] - fix: bool, + #[arg(required = true)] + input: String, + + seq: bool, }, - #[command(name = "tilejson", about = "output tilejson", long_about = None)] - Tilejson { + #[command(name = "children", about = "print children of tile(s)", long_about = None)] + Children { #[arg(required = true)] - filepath: String, + depth: u8, + + #[arg(required = true)] + input: String, + + seq: bool, }, + #[command(name="neighbors", about="print neighbors of tile(s)", long_about=None)] + Neighbors { + #[arg(required = true)] + input: String, + + seq: bool, + }, + + #[command(name="parent", about="print parent of tile(s)", long_about=None)] + Parent { + #[arg(required = true)] + input: String, + + seq: bool, + }, + + #[command(name="shapes", about="print shapes of tiles as geojson", long_about=None)] + Shapes { + #[arg(required = true)] + input: String, + + seq: bool, + }, + + // /// Clones repos // #[command(arg_required_else_help = true)] // Clone { @@ -220,12 +269,11 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { ) { println!("{}", tile.json_arr()); - // call loop_fn if it's defined + // call loop_fn if it's defined niter += 1; // call fn every 1000 iterations - if niter % 1000 == 0{ - + if niter % 1000 == 0 { if let Some(f) = loop_fn { f(); } @@ -247,7 +295,7 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { ).unwrap(); let tj = mbtiles.tilejson().unwrap(); - let s =tilejson_stringify(&tj, None); + let s = tilejson_stringify(&tj, None); println!("{}", s); @@ -256,5 +304,9 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { // serde_json::to_string_pretty(&tj).unwrap() // ); } + + _ => { + println!("NOT IMPLEMENTED YET"); + } } } From e1af4079f610f70f68ac035b01626926f66fe484 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Tue, 7 Nov 2023 14:20:28 -0800 Subject: [PATCH 16/80] christ I think it works aight --- Cargo.lock | 34 +++++++ crates/utiles-cli/src/cli.rs | 6 +- crates/utiles/Cargo.lock | 178 ++++++++++++++++++++++++++++++----- crates/utiles/Cargo.toml | 3 +- crates/utiles/src/bbox.rs | 49 ++++++++++ crates/utiles/src/parsing.rs | 102 ++++++++++++++++++++ 6 files changed, 345 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 97be64d4..c41def2e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -262,6 +262,19 @@ dependencies = [ "serde", ] +[[package]] +name = "geojson" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d728c1df1fbf328d74151efe6cb0586f79ee813346ea981add69bd22c9241b" +dependencies = [ + "geo-types", + "log", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "gimli" version = "0.28.0" @@ -786,6 +799,26 @@ version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "thread_local" version = "1.1.7" @@ -935,6 +968,7 @@ version = "0.0.1" dependencies = [ "fast_hilbert", "geo-types", + "geojson", "serde", "serde_json", "tilejson", diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index 47820b9c..3a7a2f46 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -267,7 +267,11 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { (thingy.west, thingy.south, thingy.east, thingy.north), ZoomOrZooms::Zoom(zoom), ) { - println!("{}", tile.json_arr()); + let tstr = tile.json_arr(); + // RS char if seq else "" + let rs = if seq { "\x1e\n" } else { "" }; + println!("{}{}", rs, tstr); + // println!("{}", tile.json_arr()); // call loop_fn if it's defined niter += 1; diff --git a/crates/utiles/Cargo.lock b/crates/utiles/Cargo.lock index 33aa4ac1..63b5e953 100644 --- a/crates/utiles/Cargo.lock +++ b/crates/utiles/Cargo.lock @@ -28,108 +28,233 @@ dependencies = [ [[package]] name = "geo-types" -version = "0.7.9" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5f0b3068e1537a4b861ec3734f4aa9c317d537cf0845bf6fb6221973499d26c" +checksum = "9705398c5c7b26132e74513f4ee7c1d7dafd786004991b375c172be2be0eecaa" dependencies = [ "approx", "num-traits", "serde", ] +[[package]] +name = "geojson" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d728c1df1fbf328d74151efe6cb0586f79ee813346ea981add69bd22c9241b" +dependencies = [ + "geo-types", + "log", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "libm" -version = "0.2.7" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "log" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", "libm", ] +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + [[package]] name = "proc-macro2" -version = "1.0.60" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.28" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "serde" -version = "1.0.164" +version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.164" +version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] name = "serde_json" -version = "1.0.97" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", "serde", ] +[[package]] +name = "serde_tuple" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f025b91216f15a2a32aa39669329a475733590a015835d1783549a56d09427" +dependencies = [ + "serde", + "serde_tuple_macros", +] + +[[package]] +name = "serde_tuple_macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4076151d1a2b688e25aaf236997933c66e18b870d0369f8b248b8ab2be630d7e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "syn" -version = "2.0.18" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "tilejson" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a16212e5ea60f2b406835981338a1df9b206fab312e3470502d60f69e590a9c9" +dependencies = [ + "serde", + "serde_json", + "serde_tuple", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + [[package]] name = "unicode-ident" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "utiles" @@ -137,6 +262,9 @@ version = "0.0.1" dependencies = [ "fast_hilbert", "geo-types", + "geojson", "serde", "serde_json", + "tilejson", + "tracing", ] diff --git a/crates/utiles/Cargo.toml b/crates/utiles/Cargo.toml index 1cfe93de..07ea967d 100644 --- a/crates/utiles/Cargo.toml +++ b/crates/utiles/Cargo.toml @@ -16,7 +16,8 @@ repository = "https://github.com/jessekrubin/utiles" [dependencies] fast_hilbert = "2.0.0" geo-types = "0.7.9" +geojson = "0.24.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.96" tilejson = "0.3.2" -tracing = { version = "0.1.40", features = [] } \ No newline at end of file +tracing = { version = "0.1.40", features = [] } diff --git a/crates/utiles/src/bbox.rs b/crates/utiles/src/bbox.rs index ebe983dc..9e84df82 100644 --- a/crates/utiles/src/bbox.rs +++ b/crates/utiles/src/bbox.rs @@ -1,3 +1,4 @@ +use geo_types::Coord; use crate::lnglat::LngLat; use crate::parsing::parse_bbox; use crate::tile::Tile; @@ -71,6 +72,25 @@ impl BBox { } } + pub fn world_planet() -> Self { + BBox { + west: -180.0, + south: -90.0, + east: 180.0, + north: 90.0, + } + } + + pub fn world_web() -> Self { + BBox { + west: -180.0, + south: -85.051_129, + east: 180.0, + north: 85.051_129, + } + } + + pub fn crosses_antimeridian(&self) -> bool { self.west > self.east } @@ -196,6 +216,7 @@ impl BBox { pub fn ll(&self) -> LngLat { LngLat::new(self.west, self.south) } + } impl From for BBoxTuple { @@ -204,6 +225,8 @@ impl From for BBoxTuple { } } + + impl From for BBox { fn from(tuple: BBoxTuple) -> Self { BBox::new(tuple.0, tuple.1, tuple.2, tuple.3) @@ -237,3 +260,29 @@ impl From for WebMercatorBbox { crate::xyz2bbox(tile.x, tile.y, tile.z) } } + +impl From> for BBox{ + fn from(coords: Vec) -> Self { + let mut min_x = 180.0; + let mut min_y = 90.0; + let mut max_x = -180.0; + let mut max_y = -90.0; + for coord in coords { + let x = coord.x; + let y = coord.y; + if x < min_x { + min_x = x; + } + if y < min_y { + min_y = y; + } + if x > max_x { + max_x = x; + } + if y > max_y { + max_y = y; + } + } + BBox::new(min_x, min_y, max_x, max_y) + } +} \ No newline at end of file diff --git a/crates/utiles/src/parsing.rs b/crates/utiles/src/parsing.rs index 26beccd8..f94de50e 100644 --- a/crates/utiles/src/parsing.rs +++ b/crates/utiles/src/parsing.rs @@ -1,8 +1,18 @@ +use geo_types::{Coord}; use crate::bbox::BBox; use serde_json::Value; +use geojson::{GeoJson, Geometry, Value as GeoJsonValue, Feature}; + +use geo_types::coord; // pub fn parse_bbox(s: &str) -> serde_json::Result { pub fn parse_bbox(s: &str) -> serde_json::Result { + // if the first char is "{" assume it is geojson-like + if s.chars().next().unwrap() == '{' { + let coords = geojson_coords(s); + return Ok(BBox::from(coords)); + } + let v: Value = serde_json::from_str(s)?; // Assume a single pair of coordinates represents a CoordTuple @@ -20,6 +30,97 @@ pub fn parse_bbox(s: &str) -> serde_json::Result { } } + +fn vec2coord(v: Vec) -> Coord { + coord! { x: v[0], y: v[1]} +} + +fn geojson_geometry_points(g: Geometry) -> Vec> { + let value = g.value; + let coord_vecs = match value { + GeoJsonValue::Point(c) => { + vec![c] + } + GeoJsonValue::MultiPoint(c) => { + c.into_iter().collect() + } + GeoJsonValue::LineString(c) => { + c.into_iter().collect() + } + GeoJsonValue::MultiLineString(c) => { + c.into_iter().flatten().collect() + } + GeoJsonValue::Polygon(c) => { + c.into_iter().flatten().collect() + } + GeoJsonValue::MultiPolygon(c) => { + c.into_iter().flatten().flatten().collect() + } + GeoJsonValue::GeometryCollection(c) => { + let t = c.into_iter().map(|g| geojson_geometry_points(g)).flatten().collect(); + t + + + } + _ => { + vec![] + } + + + }; + coord_vecs + + + // convert from Vec to Vec + // coord_vecs.into_iter().map(|v| vec2coord(v)).collect() + +} +fn geojson_geometry_coords(g: Geometry) -> Vec { + let coord_vecs = geojson_geometry_points(g); + // convert from Vec to Vec + coord_vecs.into_iter().map(|v| vec2coord(v)).collect() + +} + +fn geojson_feature_coords(feature: Feature) -> Vec { + let geometry = feature.geometry.unwrap(); + geojson_geometry_coords(geometry) +} + +pub fn geojson_coords(geojson_str: &str) -> Vec { + let gj = geojson_str.parse::().unwrap(); + match gj { + GeoJson::FeatureCollection(fc) => { + let mut coords = Vec::new(); + for feature in fc.features { + let feature_coords = geojson_feature_coords(feature); + coords.extend(feature_coords); + } + coords + // let mut bbox = BBox::new(180.0, 90.0, -180.0, -90.0); + // for feature in fc.features { + // let feature_bbox = geojson_feature_bounds(feature); + // bbox = bbox.union(feature_bbox); + // } + // bbox + } + GeoJson::Feature(feature) => { + // if it has a bbox + let geometry = feature.geometry.unwrap(); + geojson_geometry_coords(geometry) + } + GeoJson::Geometry(geometry) => { + geojson_geometry_coords(geometry) + } + } +} + +pub fn geojson_bounds(geojson_str: &str) -> BBox { + let coords = geojson_coords(geojson_str); + // BBox::from(coords) + BBox::world_web() +} + #[cfg(test)] mod tests { use crate::bbox::*; @@ -50,3 +151,4 @@ mod tests { assert!(bbox_result.is_err()); } } + From 79a42b81989700c174315d78cf6c093d8058e832 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Tue, 7 Nov 2023 14:49:26 -0800 Subject: [PATCH 17/80] changed pmtile id name --- crates/utiles-cli/Cargo.lock | 34 ++++++++++++++++ crates/utiles/src/pmtiles.rs | 76 ++++++++++++++++++------------------ crates/utiles/src/tile.rs | 4 +- 3 files changed, 74 insertions(+), 40 deletions(-) diff --git a/crates/utiles-cli/Cargo.lock b/crates/utiles-cli/Cargo.lock index 7a9f10af..4c04bc27 100644 --- a/crates/utiles-cli/Cargo.lock +++ b/crates/utiles-cli/Cargo.lock @@ -262,6 +262,19 @@ dependencies = [ "serde", ] +[[package]] +name = "geojson" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d728c1df1fbf328d74151efe6cb0586f79ee813346ea981add69bd22c9241b" +dependencies = [ + "geo-types", + "log", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "gimli" version = "0.28.0" @@ -688,6 +701,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "thread_local" version = "1.1.7" @@ -831,6 +864,7 @@ version = "0.0.1" dependencies = [ "fast_hilbert", "geo-types", + "geojson", "serde", "serde_json", "tilejson", diff --git a/crates/utiles/src/pmtiles.rs b/crates/utiles/src/pmtiles.rs index f047cd8b..b27290c4 100644 --- a/crates/utiles/src/pmtiles.rs +++ b/crates/utiles/src/pmtiles.rs @@ -1,4 +1,4 @@ -pub fn xyz2id(x: u32, y: u32, z: u8) -> u64 { +pub fn xyz2pmid(x: u32, y: u32, z: u8) -> u64 { if z == 0 { return 0; } @@ -8,8 +8,8 @@ pub fn xyz2id(x: u32, y: u32, z: u8) -> u64 { } #[allow(dead_code)] -pub fn zxy2id(z: u8, x: u32, y: u32) -> u64 { - xyz2id(x, y, z) +pub fn zxy2pmid(z: u8, x: u32, y: u32) -> u64 { + xyz2pmid(x, y, z) } pub fn calculate_h_o(i: u64) -> (u64, u8) { @@ -28,7 +28,7 @@ pub fn calculate_h_o(i: u64) -> (u64, u8) { } } -pub fn id2xyz(i: u64) -> (u32, u32, u8) { +pub fn pmid2xyz(i: u64) -> (u32, u32, u8) { if i == 0 { return (0, 0, 0); } @@ -38,8 +38,8 @@ pub fn id2xyz(i: u64) -> (u32, u32, u8) { } #[allow(dead_code)] -pub fn id2zxy(i: u64) -> (u8, u32, u32) { - let (x, y, z) = id2xyz(i); +pub fn pmid2zxy(i: u64) -> (u8, u32, u32) { + let (x, y, z) = pmid2xyz(i); (z, x, y) } @@ -66,25 +66,25 @@ mod tests { #[test] fn test_zxy_to_id() { - assert_eq!(0, zxy2id(0, 0, 0)); - assert_eq!(1, zxy2id(1, 0, 0)); - assert_eq!(2, zxy2id(1, 0, 1)); - assert_eq!(3, zxy2id(1, 1, 1)); - assert_eq!(4, zxy2id(1, 1, 0)); - assert_eq!(5, zxy2id(2, 0, 0)); + assert_eq!(0, zxy2pmid(0, 0, 0)); + assert_eq!(1, zxy2pmid(1, 0, 0)); + assert_eq!(2, zxy2pmid(1, 0, 1)); + assert_eq!(3, zxy2pmid(1, 1, 1)); + assert_eq!(4, zxy2pmid(1, 1, 0)); + assert_eq!(5, zxy2pmid(2, 0, 0)); } #[test] fn test_id_to_zxy() { - let (z, x, y) = id2zxy(0); + let (z, x, y) = pmid2zxy(0); assert_eq!(0, z); assert_eq!(0, x); assert_eq!(0, y); - let (z, x, y) = id2zxy(1); + let (z, x, y) = pmid2zxy(1); assert_eq!(1, z); assert_eq!(0, x); assert_eq!(0, y); - let (z, x, y) = id2zxy(19078479); + let (z, x, y) = pmid2zxy(19078479); assert_eq!(12, z); assert_eq!(3423, x); assert_eq!(1763, y); @@ -95,8 +95,8 @@ mod tests { for z in 0..10 { for x in 0..(1 << z) { for y in 0..(1 << z) { - let id = zxy2id(z, x, y); - let (rz, rx, ry) = id2zxy(id); + let id = zxy2pmid(z, x, y); + let (rz, rx, ry) = pmid2zxy(id); assert_eq!(z, rz); assert_eq!(x, rx); assert_eq!(y, ry); @@ -109,19 +109,19 @@ mod tests { fn test_extremes() { for tz in 0..32 { let dim = (1 << tz) - 1; - let (z, x, y) = id2zxy(zxy2id(tz, 0, 0)); + let (z, x, y) = pmid2zxy(zxy2pmid(tz, 0, 0)); assert_eq!(tz, z); assert_eq!(0, x); assert_eq!(0, y); - let (z, x, y) = id2zxy(zxy2id(z, dim, 0)); + let (z, x, y) = pmid2zxy(zxy2pmid(z, dim, 0)); assert_eq!(tz, z); assert_eq!(dim, x); assert_eq!(0, y); - let (z, x, y) = id2zxy(zxy2id(z, 0, dim)); + let (z, x, y) = pmid2zxy(zxy2pmid(z, 0, dim)); assert_eq!(tz, z); assert_eq!(0, x); assert_eq!(dim, y); - let (z, x, y) = id2zxy(zxy2id(z, dim, dim)); + let (z, x, y) = pmid2zxy(zxy2pmid(z, dim, dim)); assert_eq!(tz, z); assert_eq!(dim, x); assert_eq!(dim, y); @@ -130,26 +130,26 @@ mod tests { #[test] fn test_parent() { - assert_eq!(zxy2id(0, 0, 0), parent_id(zxy2id(1, 0, 0))); + assert_eq!(zxy2pmid(0, 0, 0), parent_id(zxy2pmid(1, 0, 0))); - assert_eq!(zxy2id(1, 0, 0), parent_id(zxy2id(2, 0, 0))); - assert_eq!(zxy2id(1, 0, 0), parent_id(zxy2id(2, 0, 1))); - assert_eq!(zxy2id(1, 0, 0), parent_id(zxy2id(2, 1, 0))); - assert_eq!(zxy2id(1, 0, 0), parent_id(zxy2id(2, 1, 1))); + assert_eq!(zxy2pmid(1, 0, 0), parent_id(zxy2pmid(2, 0, 0))); + assert_eq!(zxy2pmid(1, 0, 0), parent_id(zxy2pmid(2, 0, 1))); + assert_eq!(zxy2pmid(1, 0, 0), parent_id(zxy2pmid(2, 1, 0))); + assert_eq!(zxy2pmid(1, 0, 0), parent_id(zxy2pmid(2, 1, 1))); - assert_eq!(zxy2id(1, 0, 1), parent_id(zxy2id(2, 0, 2))); - assert_eq!(zxy2id(1, 0, 1), parent_id(zxy2id(2, 0, 3))); - assert_eq!(zxy2id(1, 0, 1), parent_id(zxy2id(2, 1, 2))); - assert_eq!(zxy2id(1, 0, 1), parent_id(zxy2id(2, 1, 3))); + assert_eq!(zxy2pmid(1, 0, 1), parent_id(zxy2pmid(2, 0, 2))); + assert_eq!(zxy2pmid(1, 0, 1), parent_id(zxy2pmid(2, 0, 3))); + assert_eq!(zxy2pmid(1, 0, 1), parent_id(zxy2pmid(2, 1, 2))); + assert_eq!(zxy2pmid(1, 0, 1), parent_id(zxy2pmid(2, 1, 3))); - assert_eq!(zxy2id(1, 1, 0), parent_id(zxy2id(2, 2, 0))); - assert_eq!(zxy2id(1, 1, 0), parent_id(zxy2id(2, 2, 1))); - assert_eq!(zxy2id(1, 1, 0), parent_id(zxy2id(2, 3, 0))); - assert_eq!(zxy2id(1, 1, 0), parent_id(zxy2id(2, 3, 1))); + assert_eq!(zxy2pmid(1, 1, 0), parent_id(zxy2pmid(2, 2, 0))); + assert_eq!(zxy2pmid(1, 1, 0), parent_id(zxy2pmid(2, 2, 1))); + assert_eq!(zxy2pmid(1, 1, 0), parent_id(zxy2pmid(2, 3, 0))); + assert_eq!(zxy2pmid(1, 1, 0), parent_id(zxy2pmid(2, 3, 1))); - assert_eq!(zxy2id(1, 1, 1), parent_id(zxy2id(2, 2, 2))); - assert_eq!(zxy2id(1, 1, 1), parent_id(zxy2id(2, 2, 3))); - assert_eq!(zxy2id(1, 1, 1), parent_id(zxy2id(2, 3, 2))); - assert_eq!(zxy2id(1, 1, 1), parent_id(zxy2id(2, 3, 3))); + assert_eq!(zxy2pmid(1, 1, 1), parent_id(zxy2pmid(2, 2, 2))); + assert_eq!(zxy2pmid(1, 1, 1), parent_id(zxy2pmid(2, 2, 3))); + assert_eq!(zxy2pmid(1, 1, 1), parent_id(zxy2pmid(2, 3, 2))); + assert_eq!(zxy2pmid(1, 1, 1), parent_id(zxy2pmid(2, 3, 3))); } } diff --git a/crates/utiles/src/tile.rs b/crates/utiles/src/tile.rs index 77e4ece6..8292a1ad 100644 --- a/crates/utiles/src/tile.rs +++ b/crates/utiles/src/tile.rs @@ -112,11 +112,11 @@ impl Tile { } pub fn pmtileid(&self) -> u64 { - pmtiles::xyz2id(self.x, self.y, self.z) + pmtiles::xyz2pmid(self.x, self.y, self.z) } pub fn from_pmtileid(id: u64) -> Self { - let (x, y, z) = pmtiles::id2xyz(id); + let (x, y, z) = pmtiles::pmid2xyz(id); Tile::new(x, y, z) } From bfd9ef8c76c86f86517a686544c45742d609d4cb Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Tue, 7 Nov 2023 16:03:06 -0800 Subject: [PATCH 18/80] mbtiles! --- crates/utiles/src/tile.rs | 24 ++++++++++++------------ crates/utilesqlite/src/mbtiles.rs | 13 ++++++------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/crates/utiles/src/tile.rs b/crates/utiles/src/tile.rs index 8292a1ad..8ebb0713 100644 --- a/crates/utiles/src/tile.rs +++ b/crates/utiles/src/tile.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use crate::bbox::BBox; use crate::constants::EPSILON; use crate::lnglat::LngLat; -use crate::{ll, lr, pmtiles, traits, ul, ur, XYZ}; +use crate::{bounds, children, flipy, ll, lr, neighbors, parent, pmtiles, quadkey2tile, siblings, traits, ul, ur, XYZ, xyz2quadkey}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Tile { @@ -33,7 +33,7 @@ impl traits::Utiles for Tile { } fn bbox(&self) -> BBox { - let (west, south, east, north) = crate::bounds(self.x, self.y, self.z); + let (west, south, east, north) = bounds(self.x, self.y, self.z); BBox { north, south, @@ -108,7 +108,7 @@ impl Tile { } pub fn bounds(&self) -> (f64, f64, f64, f64) { - crate::bounds(self.x, self.y, self.z) + bounds(self.x, self.y, self.z) } pub fn pmtileid(&self) -> u64 { @@ -141,11 +141,11 @@ impl Tile { } pub fn from_quadkey(quadkey: &str) -> Result> { - crate::quadkey2tile(quadkey) + quadkey2tile(quadkey) } pub fn from_qk(qk: &str) -> Self { - let res = crate::quadkey2tile(qk); + let res = quadkey2tile(qk); match res { Ok(tile) => tile, Err(e) => { @@ -155,11 +155,11 @@ impl Tile { } pub fn quadkey(&self) -> String { - crate::xyz2quadkey(self.x, self.y, self.z) + xyz2quadkey(self.x, self.y, self.z) } pub fn qk(&self) -> String { - crate::xyz2quadkey(self.x, self.y, self.z) + xyz2quadkey(self.x, self.y, self.z) } pub fn from_lnglat_zoom( @@ -294,19 +294,19 @@ impl Tile { } pub fn neighbors(&self) -> Vec { - crate::neighbors(self.x, self.y, self.z) + neighbors(self.x, self.y, self.z) } pub fn children(&self, zoom: Option) -> Vec { - crate::children(self.x, self.y, self.z, zoom) + children(self.x, self.y, self.z, zoom) } pub fn parent(&self, zoom: Option) -> Self { - crate::parent(self.x, self.y, self.z, zoom) + parent(self.x, self.y, self.z, zoom) } pub fn siblings(&self) -> Vec { - crate::siblings(self.x, self.y, self.z) + siblings(self.x, self.y, self.z) } pub fn sql_where(&self, flip: Option) -> String { @@ -319,7 +319,7 @@ impl Tile { "(zoom_level = {} AND tile_column = {} AND tile_row = {})", self.z, self.x, - crate::flipy(self.y, self.z) + flipy(self.y, self.z) ), false => format!( "(zoom_level = {} AND tile_column = {} AND tile_row = {})", diff --git a/crates/utilesqlite/src/mbtiles.rs b/crates/utilesqlite/src/mbtiles.rs index ba8cd1c1..61d2b0da 100644 --- a/crates/utilesqlite/src/mbtiles.rs +++ b/crates/utilesqlite/src/mbtiles.rs @@ -1,17 +1,16 @@ -use serde_json::{Value as JSONValue, Value}; -use rusqlite::{Connection, Result as RusqliteResult}; -use tilejson::{Bounds, Center, tilejson, TileJSON}; use std::error::Error; -use utiles::mbtiles::metadata_row::MbtilesMetadataRow; + +use rusqlite::{Connection, Result as RusqliteResult}; +use tilejson::TileJSON; +use tracing::error; // use crate::metadata_row::MbtilesMetadataRow; -use utiles::mbtiles::{metadata2tilejson}; +use utiles::mbtiles::metadata2tilejson; +use utiles::mbtiles::metadata_row::MbtilesMetadataRow; pub struct Mbtiles { conn: rusqlite::Connection, } -use tracing::{debug, error, info, span, warn, Level}; - impl Mbtiles { pub fn from_conn(conn: rusqlite::Connection) -> Mbtiles { Mbtiles { From e1ca8f56e05817d5476af6f64f13dc35c45315b2 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Tue, 7 Nov 2023 16:04:09 -0800 Subject: [PATCH 19/80] fixes --- crates/utilesqlite/Cargo.lock | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/crates/utilesqlite/Cargo.lock b/crates/utilesqlite/Cargo.lock index 4de59eb3..a32373a1 100644 --- a/crates/utilesqlite/Cargo.lock +++ b/crates/utilesqlite/Cargo.lock @@ -149,6 +149,19 @@ dependencies = [ "serde", ] +[[package]] +name = "geojson" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d728c1df1fbf328d74151efe6cb0586f79ee813346ea981add69bd22c9241b" +dependencies = [ + "geo-types", + "log", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "gimli" version = "0.28.0" @@ -219,6 +232,12 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + [[package]] name = "memchr" version = "2.6.4" @@ -473,6 +492,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "tilejson" version = "0.3.3" @@ -568,6 +607,7 @@ version = "0.0.1" dependencies = [ "fast_hilbert", "geo-types", + "geojson", "serde", "serde_json", "tilejson", From 284d286622607dd1540500a92128282b188a133f Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Tue, 7 Nov 2023 19:03:45 -0800 Subject: [PATCH 20/80] aha --- crates/utiles/src/bbox.rs | 10 +-- crates/utiles/src/geojson/mod.rs | 59 +++++++++++++++++ crates/utiles/src/lib.rs | 7 +- .../utiles/src/mbtiles/metadata2tilejson.rs | 16 ++--- crates/utiles/src/mbtiles/metadata_row.rs | 1 - crates/utiles/src/mbtiles/mod.rs | 4 +- crates/utiles/src/parsing.rs | 64 ++++--------------- crates/utiles/src/tile.rs | 5 +- crates/utiles/src/tilejson.rs | 4 +- tests/test_mbtiles.py | 3 + 10 files changed, 96 insertions(+), 77 deletions(-) create mode 100644 crates/utiles/src/geojson/mod.rs create mode 100644 tests/test_mbtiles.py diff --git a/crates/utiles/src/bbox.rs b/crates/utiles/src/bbox.rs index 9e84df82..29dc5688 100644 --- a/crates/utiles/src/bbox.rs +++ b/crates/utiles/src/bbox.rs @@ -1,7 +1,7 @@ -use geo_types::Coord; use crate::lnglat::LngLat; use crate::parsing::parse_bbox; use crate::tile::Tile; +use geo_types::Coord; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)] @@ -90,7 +90,6 @@ impl BBox { } } - pub fn crosses_antimeridian(&self) -> bool { self.west > self.east } @@ -216,7 +215,6 @@ impl BBox { pub fn ll(&self) -> LngLat { LngLat::new(self.west, self.south) } - } impl From for BBoxTuple { @@ -225,8 +223,6 @@ impl From for BBoxTuple { } } - - impl From for BBox { fn from(tuple: BBoxTuple) -> Self { BBox::new(tuple.0, tuple.1, tuple.2, tuple.3) @@ -261,7 +257,7 @@ impl From for WebMercatorBbox { } } -impl From> for BBox{ +impl From> for BBox { fn from(coords: Vec) -> Self { let mut min_x = 180.0; let mut min_y = 90.0; @@ -285,4 +281,4 @@ impl From> for BBox{ } BBox::new(min_x, min_y, max_x, max_y) } -} \ No newline at end of file +} diff --git a/crates/utiles/src/geojson/mod.rs b/crates/utiles/src/geojson/mod.rs new file mode 100644 index 00000000..14af1a1d --- /dev/null +++ b/crates/utiles/src/geojson/mod.rs @@ -0,0 +1,59 @@ +use geo_types::coord; +use geo_types::Coord; +use geojson::{Geometry, Value as GeoJsonValue}; +use std::iter::{Chain, Flatten, Once}; +use std::vec::IntoIter; +use tracing::{warn, warn_span}; + +pub fn geojson_geometry_points(g: Geometry) -> Box>> { + let value = g.value; + match value { + GeoJsonValue::Point(c) => Box::new(std::iter::once(c)), + GeoJsonValue::MultiPoint(points) => Box::new(points.into_iter()), + GeoJsonValue::LineString(line_string) => Box::new(line_string.into_iter()), + GeoJsonValue::MultiLineString(multi_line_string) => { + Box::new(multi_line_string.into_iter().flatten()) + } + GeoJsonValue::Polygon(polygon) => Box::new(polygon.into_iter().flatten()), + GeoJsonValue::MultiPolygon(multi_polygon) => { + Box::new(multi_polygon.into_iter().flatten().flatten()) + } + GeoJsonValue::GeometryCollection(geometries) => { + Box::new(geometries.into_iter().flat_map(geojson_geometry_points)) + } + _ => Box::new(std::iter::empty()), // For any other case, return an empty iterator + } +} + +pub fn geojson_geometry_coords(g: Geometry) -> Box> { + let coord_vecs = geojson_geometry_points(g); + Box::new(coord_vecs.into_iter().map(|v| { + coord! { x: v[0], y: v[1]} + })) +} + +pub fn geojson_geometry_points_vec(g: Geometry) -> Vec> { + let value = g.value; + let coord_vecs = match value { + GeoJsonValue::Point(c) => { + vec![c] + } + GeoJsonValue::MultiPoint(c) => c.into_iter().collect(), + GeoJsonValue::LineString(c) => c.into_iter().collect(), + GeoJsonValue::MultiLineString(c) => c.into_iter().flatten().collect(), + GeoJsonValue::Polygon(c) => c.into_iter().flatten().collect(), + GeoJsonValue::MultiPolygon(c) => c.into_iter().flatten().flatten().collect(), + GeoJsonValue::GeometryCollection(c) => { + let t = c + .into_iter() + .map(|g| geojson_geometry_points_vec(g)) + .flatten() + .collect(); + t + } + _ => { + vec![] + } + }; + coord_vecs +} diff --git a/crates/utiles/src/lib.rs b/crates/utiles/src/lib.rs index 2e6cf62d..86eb1e0f 100644 --- a/crates/utiles/src/lib.rs +++ b/crates/utiles/src/lib.rs @@ -14,17 +14,18 @@ use zoom::ZoomOrZooms; pub mod bbox; pub mod constants; +pub mod geojson; pub mod libtiletype; pub mod lnglat; +pub mod mbtiles; +pub mod parsing; pub mod pmtiles; pub mod sibling_relationship; pub mod tile; pub mod tile_range; +pub mod tilejson; pub mod traits; pub mod zoom; -pub mod parsing; -pub mod mbtiles; -pub mod tilejson; /// Tile macro to create a new tile. /// - do you need this? probably not diff --git a/crates/utiles/src/mbtiles/metadata2tilejson.rs b/crates/utiles/src/mbtiles/metadata2tilejson.rs index 2bf86656..2e2ed4c7 100644 --- a/crates/utiles/src/mbtiles/metadata2tilejson.rs +++ b/crates/utiles/src/mbtiles/metadata2tilejson.rs @@ -4,9 +4,9 @@ use std::str::FromStr; use serde_json::{Value as JSONValue, Value}; -use tilejson::{Bounds, Center, tilejson, TileJSON}; -use tracing::{debug, error, info, Level, span, warn}; use crate::mbtiles::metadata_row::MbtilesMetadataRow; +use tilejson::{tilejson, Bounds, Center, TileJSON}; +use tracing::{debug, error, info, span, warn, Level}; fn to_val(val: Result, title: &str) -> Option { match val { @@ -19,12 +19,13 @@ fn to_val(val: Result, title: &str) -> Option { } } -pub fn metadata2tilejson(metadata: Vec) -> Result> { +pub fn metadata2tilejson( + metadata: Vec, +) -> Result> { let mut tj = tilejson! {tiles : vec![]}; let mut layer_type: Option = None; let mut json: Option = None; - for row in metadata { let name = row.name; let value = row.value; @@ -59,10 +60,9 @@ pub fn metadata2tilejson(metadata: Vec) -> Result serde_json::Result { } } - -fn vec2coord(v: Vec) -> Coord { - coord! { x: v[0], y: v[1]} -} - -fn geojson_geometry_points(g: Geometry) -> Vec> { - let value = g.value; - let coord_vecs = match value { - GeoJsonValue::Point(c) => { - vec![c] - } - GeoJsonValue::MultiPoint(c) => { - c.into_iter().collect() - } - GeoJsonValue::LineString(c) => { - c.into_iter().collect() - } - GeoJsonValue::MultiLineString(c) => { - c.into_iter().flatten().collect() - } - GeoJsonValue::Polygon(c) => { - c.into_iter().flatten().collect() - } - GeoJsonValue::MultiPolygon(c) => { - c.into_iter().flatten().flatten().collect() - } - GeoJsonValue::GeometryCollection(c) => { - let t = c.into_iter().map(|g| geojson_geometry_points(g)).flatten().collect(); - t - - - } - _ => { - vec![] - } - - - }; - coord_vecs - - - // convert from Vec to Vec - // coord_vecs.into_iter().map(|v| vec2coord(v)).collect() - -} fn geojson_geometry_coords(g: Geometry) -> Vec { let coord_vecs = geojson_geometry_points(g); + coord_vecs + .into_iter() + .map(|v| { + coord! { x: v[0], y: v[1]} + }) + .collect() // convert from Vec to Vec - coord_vecs.into_iter().map(|v| vec2coord(v)).collect() - + // coord_vecs.into_iter().map(|v| vec2coord(v)).collect() } fn geojson_feature_coords(feature: Feature) -> Vec { @@ -109,9 +70,7 @@ pub fn geojson_coords(geojson_str: &str) -> Vec { let geometry = feature.geometry.unwrap(); geojson_geometry_coords(geometry) } - GeoJson::Geometry(geometry) => { - geojson_geometry_coords(geometry) - } + GeoJson::Geometry(geometry) => geojson_geometry_coords(geometry), } } @@ -151,4 +110,3 @@ mod tests { assert!(bbox_result.is_err()); } } - diff --git a/crates/utiles/src/tile.rs b/crates/utiles/src/tile.rs index 8ebb0713..825d6380 100644 --- a/crates/utiles/src/tile.rs +++ b/crates/utiles/src/tile.rs @@ -6,7 +6,10 @@ use serde::{Deserialize, Serialize}; use crate::bbox::BBox; use crate::constants::EPSILON; use crate::lnglat::LngLat; -use crate::{bounds, children, flipy, ll, lr, neighbors, parent, pmtiles, quadkey2tile, siblings, traits, ul, ur, XYZ, xyz2quadkey}; +use crate::{ + bounds, children, flipy, ll, lr, neighbors, parent, pmtiles, quadkey2tile, + siblings, traits, ul, ur, xyz2quadkey, XYZ, +}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Tile { diff --git a/crates/utiles/src/tilejson.rs b/crates/utiles/src/tilejson.rs index aa623999..7cd41f72 100644 --- a/crates/utiles/src/tilejson.rs +++ b/crates/utiles/src/tilejson.rs @@ -1,5 +1,5 @@ use serde_json; -use tilejson::{TileJSON}; +use tilejson::TileJSON; pub fn tilejson_stringify(tj: &TileJSON, fmt: Option) -> String { match fmt { @@ -10,4 +10,4 @@ pub fn tilejson_stringify(tj: &TileJSON, fmt: Option) -> String { pub fn tilejson_parse(s: &str) -> Result { serde_json::from_str(s) -} \ No newline at end of file +} diff --git a/tests/test_mbtiles.py b/tests/test_mbtiles.py new file mode 100644 index 00000000..3163ed59 --- /dev/null +++ b/tests/test_mbtiles.py @@ -0,0 +1,3 @@ + +def test_todo(): + assert True From 496d0f3f4824d2f8d020bb43f152d0d265a091f8 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Tue, 7 Nov 2023 19:21:52 -0800 Subject: [PATCH 21/80] fmt --- crates/utiles/src/geojson/mod.rs | 32 ++++++++++++-- crates/utiles/src/parsing.rs | 74 ++++++++++++-------------------- 2 files changed, 56 insertions(+), 50 deletions(-) diff --git a/crates/utiles/src/geojson/mod.rs b/crates/utiles/src/geojson/mod.rs index 14af1a1d..e9d0a38c 100644 --- a/crates/utiles/src/geojson/mod.rs +++ b/crates/utiles/src/geojson/mod.rs @@ -1,9 +1,6 @@ use geo_types::coord; use geo_types::Coord; -use geojson::{Geometry, Value as GeoJsonValue}; -use std::iter::{Chain, Flatten, Once}; -use std::vec::IntoIter; -use tracing::{warn, warn_span}; +use geojson::{Feature, GeoJson, Geometry, Value as GeoJsonValue}; pub fn geojson_geometry_points(g: Geometry) -> Box>> { let value = g.value; @@ -57,3 +54,30 @@ pub fn geojson_geometry_points_vec(g: Geometry) -> Vec> { }; coord_vecs } + +pub fn geojson_feature_coords(feature: Feature) -> Box> { + let geometry = feature.geometry.unwrap(); + geojson_geometry_coords(geometry) +} + +pub fn geojson_coords(geojson_str: &str) -> Box> { + let gj = geojson_str.parse::().unwrap(); + match gj { + GeoJson::FeatureCollection(fc) => { + let mut coords = fc.features.into_iter().flat_map(geojson_feature_coords); + Box::new(coords) + // let mut bbox = BBox::new(180.0, 90.0, -180.0, -90.0); + // for feature in fc.features { + // let feature_bbox = geojson_feature_bounds(feature); + // bbox = bbox.union(feature_bbox); + // } + // bbox + } + GeoJson::Feature(feature) => { + // if it has a bbox + let geometry = feature.geometry.unwrap(); + geojson_geometry_coords(geometry) + } + GeoJson::Geometry(geometry) => geojson_geometry_coords(geometry), + } +} diff --git a/crates/utiles/src/parsing.rs b/crates/utiles/src/parsing.rs index 9050cf43..c473da02 100644 --- a/crates/utiles/src/parsing.rs +++ b/crates/utiles/src/parsing.rs @@ -1,17 +1,13 @@ use crate::bbox::BBox; -use crate::geojson::geojson_geometry_points; +use crate::geojson::geojson_coords; use geo_types::Coord; -use geojson::{Feature, GeoJson, Geometry, Value as GeoJsonValue}; use serde_json::Value; -use geo_types::coord; - // pub fn parse_bbox(s: &str) -> serde_json::Result { pub fn parse_bbox(s: &str) -> serde_json::Result { // if the first char is "{" assume it is geojson-like if s.chars().next().unwrap() == '{' { - let coords = geojson_coords(s); - return Ok(BBox::from(coords)); + return Ok(geojson_bounds(s)); } let v: Value = serde_json::from_str(s)?; @@ -31,53 +27,39 @@ pub fn parse_bbox(s: &str) -> serde_json::Result { } } -fn geojson_geometry_coords(g: Geometry) -> Vec { - let coord_vecs = geojson_geometry_points(g); - coord_vecs - .into_iter() - .map(|v| { - coord! { x: v[0], y: v[1]} - }) - .collect() - // convert from Vec to Vec - // coord_vecs.into_iter().map(|v| vec2coord(v)).collect() -} - -fn geojson_feature_coords(feature: Feature) -> Vec { - let geometry = feature.geometry.unwrap(); - geojson_geometry_coords(geometry) -} +pub fn coords2bounds(mut coords: I) -> Option<(f64, f64, f64, f64)> +where + I: Iterator, +{ + // Initialize the bounds with the first coordinate. + let first_coord = coords.next()?; + let mut min_x = first_coord.x; + let mut max_x = first_coord.x; + let mut min_y = first_coord.y; + let mut max_y = first_coord.y; -pub fn geojson_coords(geojson_str: &str) -> Vec { - let gj = geojson_str.parse::().unwrap(); - match gj { - GeoJson::FeatureCollection(fc) => { - let mut coords = Vec::new(); - for feature in fc.features { - let feature_coords = geojson_feature_coords(feature); - coords.extend(feature_coords); - } - coords - // let mut bbox = BBox::new(180.0, 90.0, -180.0, -90.0); - // for feature in fc.features { - // let feature_bbox = geojson_feature_bounds(feature); - // bbox = bbox.union(feature_bbox); - // } - // bbox + // Iterate through the coordinates to find the extremes. + for coord in coords { + if coord.x < min_x { + min_x = coord.x; } - GeoJson::Feature(feature) => { - // if it has a bbox - let geometry = feature.geometry.unwrap(); - geojson_geometry_coords(geometry) + if coord.x > max_x { + max_x = coord.x; + } + if coord.y < min_y { + min_y = coord.y; + } + if coord.y > max_y { + max_y = coord.y; } - GeoJson::Geometry(geometry) => geojson_geometry_coords(geometry), } -} + Some((min_x, min_y, max_x, max_y)) +} pub fn geojson_bounds(geojson_str: &str) -> BBox { let coords = geojson_coords(geojson_str); - // BBox::from(coords) - BBox::world_web() + let bounds = coords2bounds(coords).unwrap(); + BBox::new(bounds.0, bounds.1, bounds.2, bounds.3) } #[cfg(test)] From 36d797a17d34149ff488f099f73da85b1b62d0ce Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Tue, 7 Nov 2023 19:23:32 -0800 Subject: [PATCH 22/80] fmt and clippy --- crates/utiles/src/geojson/mod.rs | 23 +++++++------------ .../utiles/src/mbtiles/metadata2tilejson.rs | 4 ++-- crates/utiles/src/parsing.rs | 2 +- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/crates/utiles/src/geojson/mod.rs b/crates/utiles/src/geojson/mod.rs index e9d0a38c..0cb329d9 100644 --- a/crates/utiles/src/geojson/mod.rs +++ b/crates/utiles/src/geojson/mod.rs @@ -31,7 +31,8 @@ pub fn geojson_geometry_coords(g: Geometry) -> Box> { pub fn geojson_geometry_points_vec(g: Geometry) -> Vec> { let value = g.value; - let coord_vecs = match value { + + match value { GeoJsonValue::Point(c) => { vec![c] } @@ -40,19 +41,11 @@ pub fn geojson_geometry_points_vec(g: Geometry) -> Vec> { GeoJsonValue::MultiLineString(c) => c.into_iter().flatten().collect(), GeoJsonValue::Polygon(c) => c.into_iter().flatten().collect(), GeoJsonValue::MultiPolygon(c) => c.into_iter().flatten().flatten().collect(), - GeoJsonValue::GeometryCollection(c) => { - let t = c - .into_iter() - .map(|g| geojson_geometry_points_vec(g)) - .flatten() - .collect(); - t - } - _ => { - vec![] - } - }; - coord_vecs + GeoJsonValue::GeometryCollection(c) => c + .into_iter() + .flat_map(geojson_geometry_points_vec) + .collect(), + } } pub fn geojson_feature_coords(feature: Feature) -> Box> { @@ -64,7 +57,7 @@ pub fn geojson_coords(geojson_str: &str) -> Box> { let gj = geojson_str.parse::().unwrap(); match gj { GeoJson::FeatureCollection(fc) => { - let mut coords = fc.features.into_iter().flat_map(geojson_feature_coords); + let coords = fc.features.into_iter().flat_map(geojson_feature_coords); Box::new(coords) // let mut bbox = BBox::new(180.0, 90.0, -180.0, -90.0); // for feature in fc.features { diff --git a/crates/utiles/src/mbtiles/metadata2tilejson.rs b/crates/utiles/src/mbtiles/metadata2tilejson.rs index 2e2ed4c7..30574b21 100644 --- a/crates/utiles/src/mbtiles/metadata2tilejson.rs +++ b/crates/utiles/src/mbtiles/metadata2tilejson.rs @@ -6,12 +6,12 @@ use serde_json::{Value as JSONValue, Value}; use crate::mbtiles::metadata_row::MbtilesMetadataRow; use tilejson::{tilejson, Bounds, Center, TileJSON}; -use tracing::{debug, error, info, span, warn, Level}; +use tracing::{info, warn}; fn to_val(val: Result, title: &str) -> Option { match val { Ok(v) => Some(v), - Err(err) => { + Err(_err) => { // let name = &self.filename; warn!("Unable to parse metadata {title}"); None diff --git a/crates/utiles/src/parsing.rs b/crates/utiles/src/parsing.rs index c473da02..50e1b728 100644 --- a/crates/utiles/src/parsing.rs +++ b/crates/utiles/src/parsing.rs @@ -6,7 +6,7 @@ use serde_json::Value; // pub fn parse_bbox(s: &str) -> serde_json::Result { pub fn parse_bbox(s: &str) -> serde_json::Result { // if the first char is "{" assume it is geojson-like - if s.chars().next().unwrap() == '{' { + if s.starts_with('{') { return Ok(geojson_bounds(s)); } From 51b47b3b7412da15d310ae1b1cd3ebe0b685cd08 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Tue, 7 Nov 2023 19:24:29 -0800 Subject: [PATCH 23/80] wasm experiments --- crates/utiles-wasm/Cargo.lock | 417 +++ crates/utiles-wasm/Cargo.toml | 15 + crates/utiles-wasm/eslint.config.js | 22 + crates/utiles-wasm/package.json | 25 + crates/utiles-wasm/pnpm-lock.yaml | 3713 +++++++++++++++++++++++++++ crates/utiles-wasm/src/lib.rs | 30 + crates/utiles-wasm/tsconfig.json | 17 + crates/utiles-wasm/vitest.config.ts | 10 + 8 files changed, 4249 insertions(+) create mode 100644 crates/utiles-wasm/Cargo.lock create mode 100644 crates/utiles-wasm/Cargo.toml create mode 100644 crates/utiles-wasm/eslint.config.js create mode 100644 crates/utiles-wasm/package.json create mode 100644 crates/utiles-wasm/pnpm-lock.yaml create mode 100644 crates/utiles-wasm/src/lib.rs create mode 100644 crates/utiles-wasm/tsconfig.json create mode 100644 crates/utiles-wasm/vitest.config.ts diff --git a/crates/utiles-wasm/Cargo.lock b/crates/utiles-wasm/Cargo.lock new file mode 100644 index 00000000..2e488ac1 --- /dev/null +++ b/crates/utiles-wasm/Cargo.lock @@ -0,0 +1,417 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "fast_hilbert" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ec2bbe15af87954c739e236021f4411766c0f2b9c4a5f0b9317bcf6048ebf8" +dependencies = [ + "num-traits", +] + +[[package]] +name = "geo-types" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9705398c5c7b26132e74513f4ee7c1d7dafd786004991b375c172be2be0eecaa" +dependencies = [ + "approx", + "num-traits", + "serde", +] + +[[package]] +name = "geojson" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d728c1df1fbf328d74151efe6cb0586f79ee813346ea981add69bd22c9241b" +dependencies = [ + "geo-types", + "log", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "js-sys" +version = "0.3.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "serde" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_tuple" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f025b91216f15a2a32aa39669329a475733590a015835d1783549a56d09427" +dependencies = [ + "serde", + "serde_tuple_macros", +] + +[[package]] +name = "serde_tuple_macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4076151d1a2b688e25aaf236997933c66e18b870d0369f8b248b8ab2be630d7e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "tilejson" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a16212e5ea60f2b406835981338a1df9b206fab312e3470502d60f69e590a9c9" +dependencies = [ + "serde", + "serde_json", + "serde_tuple", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utiles" +version = "0.0.1" +dependencies = [ + "fast_hilbert", + "geo-types", + "geojson", + "serde", + "serde_json", + "tilejson", + "tracing", +] + +[[package]] +name = "utiles-wasm" +version = "0.1.0" +dependencies = [ + "utiles", + "wasm-bindgen", + "wasm-bindgen-test", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.39", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" + +[[package]] +name = "wasm-bindgen-test" +version = "0.3.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6433b7c56db97397842c46b67e11873eda263170afeb3a2dc74a7cb370fee0d" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "493fcbab756bb764fa37e6bee8cec2dd709eb4273d06d0c282a5e74275ded735" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "web-sys" +version = "0.3.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" +dependencies = [ + "js-sys", + "wasm-bindgen", +] diff --git a/crates/utiles-wasm/Cargo.toml b/crates/utiles-wasm/Cargo.toml new file mode 100644 index 00000000..83a0e2c8 --- /dev/null +++ b/crates/utiles-wasm/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "utiles-wasm" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +wasm-bindgen = "0.2.88" +wasm-bindgen-test = "0.3.38" +utiles = { path = "../utiles" } + +[lib] +crate-type = ["cdylib"] + diff --git a/crates/utiles-wasm/eslint.config.js b/crates/utiles-wasm/eslint.config.js new file mode 100644 index 00000000..8d8be00f --- /dev/null +++ b/crates/utiles-wasm/eslint.config.js @@ -0,0 +1,22 @@ +// eslint.config.js +import jsse from "@jsse/eslint-config"; + +export default [ + ...jsse({ + typescript: { + tsconfig: ["./tsconfig.json", "./tsconfig.eslint.json"], + }, + }), + { + files: ["schemas/**/*.schema.json"], + rules: { + "unicorn/filename-case": "off", + }, + }, + { + files: ["./src/dev/dev.ts", "./src/dev/dev.test.ts", "./src/scratch/**/*"], + rules: { + "@typescript-eslint/no-unused-vars": "off", + }, + }, +]; diff --git a/crates/utiles-wasm/package.json b/crates/utiles-wasm/package.json new file mode 100644 index 00000000..bcb87d11 --- /dev/null +++ b/crates/utiles-wasm/package.json @@ -0,0 +1,25 @@ +{ + "name": "utiles-wasm", + "type": "module", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "wasmpack": "wasm-pack build --target nodejs", + "wasmpack-r": "wasm-pack build --target nodejs --release" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@jsse/eslint-config": "^0.0.10", + "eslint": "^8.52.0", + "pmtiles": "^2.11.0", + "typescript": "^5.2.2", + "vite": "^4.5.0", + "vite-plugin-top-level-await": "^1.3.1", + "vite-plugin-wasm": "^3.2.2", + "vitest": "^0.34.6" + } +} diff --git a/crates/utiles-wasm/pnpm-lock.yaml b/crates/utiles-wasm/pnpm-lock.yaml new file mode 100644 index 00000000..4f668fc8 --- /dev/null +++ b/crates/utiles-wasm/pnpm-lock.yaml @@ -0,0 +1,3713 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: false + excludeLinksFromLockfile: false + +devDependencies: + '@jsse/eslint-config': + specifier: ^0.0.10 + version: 0.0.10(eslint@8.52.0)(typescript@5.2.2)(vitest@0.34.6) + eslint: + specifier: ^8.52.0 + version: 8.52.0 + pmtiles: + specifier: ^2.11.0 + version: 2.11.0 + typescript: + specifier: ^5.2.2 + version: 5.2.2 + vite: + specifier: ^4.5.0 + version: 4.5.0(@types/node@20.8.10) + vite-plugin-top-level-await: + specifier: ^1.3.1 + version: 1.3.1(vite@4.5.0) + vite-plugin-wasm: + specifier: ^3.2.2 + version: 3.2.2(vite@4.5.0) + vitest: + specifier: ^0.34.6 + version: 0.34.6 + +packages: + + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@alloc/quick-lru@5.2.0: + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + requiresBuild: true + dev: true + + /@antfu/eslint-define-config@1.23.0-2: + resolution: {integrity: sha512-LvxY21+ZhpuBf/aHeBUtGQhSEfad4PkNKXKvDOSvukaM3XVTfBhwmHX2EKwAsdq5DlfjbT3qqYyMiueBIO5iDQ==} + engines: {node: '>=18.0.0', npm: '>=9.0.0', pnpm: '>= 8.6.0'} + dev: true + + /@babel/code-frame@7.22.13: + resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.22.20 + chalk: 2.4.2 + dev: true + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/highlight@7.22.20: + resolution: {integrity: sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@es-joy/jsdoccomment@0.40.1: + resolution: {integrity: sha512-YORCdZSusAlBrFpZ77pJjc5r1bQs5caPWtAu+WWmiSo+8XaUzseapVrfAtiRFbQWnrBxxLLEwF6f6ZG/UgCQCg==} + engines: {node: '>=16'} + dependencies: + comment-parser: 1.4.0 + esquery: 1.5.0 + jsdoc-type-pratt-parser: 4.0.0 + dev: true + + /@esbuild/android-arm64@0.18.20: + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.18.20: + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.18.20: + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.18.20: + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.18.20: + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.18.20: + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.18.20: + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.18.20: + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.18.20: + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.18.20: + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.18.20: + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.18.20: + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.18.20: + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.18.20: + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.18.20: + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.18.20: + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.18.20: + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.18.20: + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.18.20: + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.18.20: + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.18.20: + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.18.20: + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.52.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.52.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.10.0: + resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.2: + resolution: {integrity: sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.23.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.52.0: + resolution: {integrity: sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@humanwhocodes/config-array@0.11.13: + resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 2.0.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@2.0.1: + resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} + dev: true + + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + requiresBuild: true + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.20 + dev: true + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + requiresBuild: true + dev: true + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + requiresBuild: true + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.20: + resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} + requiresBuild: true + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@jsse/eslint-config@0.0.10(eslint@8.52.0)(typescript@5.2.2)(vitest@0.34.6): + resolution: {integrity: sha512-RW+afjxT68Pp1YHOv2wN9BAnGJ1Xj7Tuxe4sJQEZky73UT5Ghx3DJ3F1Z/0APt4lobMQJcIRENEPWZPKNjecZw==} + hasBin: true + peerDependencies: + eslint: '>=8.0.0' + dependencies: + '@antfu/eslint-define-config': 1.23.0-2 + '@eslint/js': 8.52.0 + '@stylistic/eslint-plugin': 0.1.1(eslint@8.52.0)(typescript@5.2.2) + '@typescript-eslint/eslint-plugin': 6.9.1(@typescript-eslint/parser@6.9.1)(eslint@8.52.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + eslint: 8.52.0 + eslint-config-flat-gitignore: 0.1.1 + eslint-define-config: 1.24.1 + eslint-plugin-antfu: 1.0.1(eslint@8.52.0) + eslint-plugin-eslint-comments: 3.2.0(eslint@8.52.0) + eslint-plugin-i: 2.29.0(@typescript-eslint/parser@6.9.1)(eslint@8.52.0) + eslint-plugin-jsdoc: 46.8.2(eslint@8.52.0) + eslint-plugin-jsonc: 2.10.0(eslint@8.52.0) + eslint-plugin-markdown: 3.0.1(eslint@8.52.0) + eslint-plugin-n: 16.2.0(eslint@8.52.0) + eslint-plugin-no-only-tests: 3.1.0 + eslint-plugin-react: 7.33.2(eslint@8.52.0) + eslint-plugin-react-hooks: 4.6.0(eslint@8.52.0) + eslint-plugin-react-refresh: 0.4.4(eslint@8.52.0) + eslint-plugin-tailwindcss: 3.13.0(tailwindcss@3.3.5) + eslint-plugin-unicorn: 48.0.1(eslint@8.52.0) + eslint-plugin-unused-imports: 3.0.0(@typescript-eslint/eslint-plugin@6.9.1)(eslint@8.52.0) + eslint-plugin-vitest: 0.3.8(@typescript-eslint/eslint-plugin@6.9.1)(eslint@8.52.0)(typescript@5.2.2)(vitest@0.34.6) + eslint-plugin-yml: 1.10.0(eslint@8.52.0) + jsonc-eslint-parser: 2.4.0 + yaml-eslint-parser: 1.2.2 + optionalDependencies: + tailwindcss: 3.3.5 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + - ts-node + - typescript + - vitest + dev: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + requiresBuild: true + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + requiresBuild: true + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + requiresBuild: true + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@rollup/plugin-virtual@3.0.2: + resolution: {integrity: sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dev: true + + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + + /@stylistic/eslint-plugin-js@0.1.1(eslint@8.52.0): + resolution: {integrity: sha512-gZbT/Sqz1viW+87YaGrzosjI54IIAwGuYE+5AgXO1C9zGMLpxDroyU+HpcqVSynN5Nihuaocp89UU49nCJh9KA==} + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0) + acorn: 8.11.2 + escape-string-regexp: 4.0.0 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esutils: 2.0.3 + graphemer: 1.4.0 + transitivePeerDependencies: + - eslint + dev: true + + /@stylistic/eslint-plugin-jsx@0.1.1(eslint@8.52.0): + resolution: {integrity: sha512-36+iAWxGIAwGb2k7vS4S14NIt/2NVBCJSn3Q+T91t4MF1fWyaQFOoc5l6c4oW5UTzfr0vgMMcqsr8f8JJjqm0g==} + dependencies: + '@stylistic/eslint-plugin-js': 0.1.1(eslint@8.52.0) + estraverse: 5.3.0 + jsx-ast-utils: 3.3.5 + transitivePeerDependencies: + - eslint + dev: true + + /@stylistic/eslint-plugin-ts@0.1.1(eslint@8.52.0)(typescript@5.2.2): + resolution: {integrity: sha512-mJHYWzljqrDykOop60nyCAXRp4LLstfNQWXxubJFMOz5tlAoqd2Mex60TCa/fctPhaOd36f/lu1CuU9WgFh+DA==} + peerDependencies: + eslint: '*' + dependencies: + '@stylistic/eslint-plugin-js': 0.1.1(eslint@8.52.0) + '@typescript-eslint/scope-manager': 6.9.1 + '@typescript-eslint/type-utils': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + eslint: 8.52.0 + graphemer: 1.4.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@stylistic/eslint-plugin@0.1.1(eslint@8.52.0)(typescript@5.2.2): + resolution: {integrity: sha512-pOuT7q5XOain+YBExC7vHHSWEmvp2Z9/v1HTuYjy+EzvZkv7w8pXlC44qmO14G8A5uVaUp3CZtNzTvT2h4Ilaw==} + peerDependencies: + eslint: '*' + dependencies: + '@stylistic/eslint-plugin-js': 0.1.1(eslint@8.52.0) + '@stylistic/eslint-plugin-jsx': 0.1.1(eslint@8.52.0) + '@stylistic/eslint-plugin-ts': 0.1.1(eslint@8.52.0)(typescript@5.2.2) + eslint: 8.52.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@swc/core-darwin-arm64@1.3.95: + resolution: {integrity: sha512-VAuBAP3MNetO/yBIBzvorUXq7lUBwhfpJxYViSxyluMwtoQDhE/XWN598TWMwMl1ZuImb56d7eUsuFdjgY7pJw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-darwin-x64@1.3.95: + resolution: {integrity: sha512-20vF2rvUsN98zGLZc+dsEdHvLoCuiYq/1B+TDeE4oolgTFDmI1jKO+m44PzWjYtKGU9QR95sZ6r/uec0QC5O4Q==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm-gnueabihf@1.3.95: + resolution: {integrity: sha512-oEudEM8PST1MRNGs+zu0cx5i9uP8TsLE4/L9HHrS07Ck0RJ3DCj3O2fU832nmLe2QxnAGPwBpSO9FntLfOiWEQ==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-gnu@1.3.95: + resolution: {integrity: sha512-pIhFI+cuC1aYg+0NAPxwT/VRb32f2ia8oGxUjQR6aJg65gLkUYQzdwuUmpMtFR2WVf7WVFYxUnjo4UyMuyh3ng==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-musl@1.3.95: + resolution: {integrity: sha512-ZpbTr+QZDT4OPJfjPAmScqdKKaT+wGurvMU5AhxLaf85DuL8HwUwwlL0n1oLieLc47DwIJEMuKQkYhXMqmJHlg==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-gnu@1.3.95: + resolution: {integrity: sha512-n9SuHEFtdfSJ+sHdNXNRuIOVprB8nbsz+08apKfdo4lEKq6IIPBBAk5kVhPhkjmg2dFVHVo4Tr/OHXM1tzWCCw==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-musl@1.3.95: + resolution: {integrity: sha512-L1JrVlsXU3LC0WwmVnMK9HrOT2uhHahAoPNMJnZQpc18a0paO9fqifPG8M/HjNRffMUXR199G/phJsf326UvVg==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-arm64-msvc@1.3.95: + resolution: {integrity: sha512-YaP4x/aZbUyNdqCBpC2zL8b8n58MEpOUpmOIZK6G1SxGi+2ENht7gs7+iXpWPc0sy7X3YPKmSWMAuui0h8lgAA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-ia32-msvc@1.3.95: + resolution: {integrity: sha512-w0u3HI916zT4BC/57gOd+AwAEjXeUlQbGJ9H4p/gzs1zkSHtoDQghVUNy3n/ZKp9KFod/95cA8mbVF9t1+6epQ==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-x64-msvc@1.3.95: + resolution: {integrity: sha512-5RGnMt0S6gg4Gc6QtPUJ3Qs9Un4sKqccEzgH/tj7V/DVTJwKdnBKxFZfgQ34OR2Zpz7zGOn889xwsFVXspVWNA==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core@1.3.95: + resolution: {integrity: sha512-PMrNeuqIusq9DPDooV3FfNEbZuTu5jKAc04N3Hm6Uk2Fl49cqElLFQ4xvl4qDmVDz97n3n/C1RE0/f6WyGPEiA==} + engines: {node: '>=10'} + requiresBuild: true + peerDependencies: + '@swc/helpers': ^0.5.0 + peerDependenciesMeta: + '@swc/helpers': + optional: true + dependencies: + '@swc/counter': 0.1.2 + '@swc/types': 0.1.5 + optionalDependencies: + '@swc/core-darwin-arm64': 1.3.95 + '@swc/core-darwin-x64': 1.3.95 + '@swc/core-linux-arm-gnueabihf': 1.3.95 + '@swc/core-linux-arm64-gnu': 1.3.95 + '@swc/core-linux-arm64-musl': 1.3.95 + '@swc/core-linux-x64-gnu': 1.3.95 + '@swc/core-linux-x64-musl': 1.3.95 + '@swc/core-win32-arm64-msvc': 1.3.95 + '@swc/core-win32-ia32-msvc': 1.3.95 + '@swc/core-win32-x64-msvc': 1.3.95 + dev: true + + /@swc/counter@0.1.2: + resolution: {integrity: sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==} + dev: true + + /@swc/types@0.1.5: + resolution: {integrity: sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==} + dev: true + + /@types/chai-subset@1.3.4: + resolution: {integrity: sha512-CCWNXrJYSUIojZ1149ksLl3AN9cmZ5djf+yUoVVV+NuYrtydItQVlL2ZDqyC6M6O9LWRnVf8yYDxbXHO2TfQZg==} + dependencies: + '@types/chai': 4.3.9 + dev: true + + /@types/chai@4.3.9: + resolution: {integrity: sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==} + dev: true + + /@types/json-schema@7.0.14: + resolution: {integrity: sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==} + dev: true + + /@types/mdast@3.0.14: + resolution: {integrity: sha512-gVZ04PGgw1qLZKsnWnyFv4ORnaJ+DXLdHTVSFbU8yX6xZ34Bjg4Q32yPkmveUP1yItXReKfB0Aknlh/3zxTKAw==} + dependencies: + '@types/unist': 2.0.9 + dev: true + + /@types/node@20.8.10: + resolution: {integrity: sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==} + dependencies: + undici-types: 5.26.5 + dev: true + + /@types/normalize-package-data@2.4.3: + resolution: {integrity: sha512-ehPtgRgaULsFG8x0NeYJvmyH1hmlfsNLujHe9dQEia/7MAJYdzMSi19JtchUHjmBA6XC/75dK55mzZH+RyieSg==} + dev: true + + /@types/semver@7.5.4: + resolution: {integrity: sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==} + dev: true + + /@types/unist@2.0.9: + resolution: {integrity: sha512-zC0iXxAv1C1ERURduJueYzkzZ2zaGyc+P2c95hgkikHPr3z8EdUZOlgEQ5X0DRmwDZn+hekycQnoeiiRVrmilQ==} + dev: true + + /@typescript-eslint/eslint-plugin@6.9.1(@typescript-eslint/parser@6.9.1)(eslint@8.52.0)(typescript@5.2.2): + resolution: {integrity: sha512-w0tiiRc9I4S5XSXXrMHOWgHgxbrBn1Ro+PmiYhSg2ZVdxrAJtQgzU5o2m1BfP6UOn7Vxcc6152vFjQfmZR4xEg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.10.0 + '@typescript-eslint/parser': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + '@typescript-eslint/scope-manager': 6.9.1 + '@typescript-eslint/type-utils': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.9.1 + debug: 4.3.4 + eslint: 8.52.0 + graphemer: 1.4.0 + ignore: 5.2.4 + natural-compare: 1.4.0 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@6.9.1(eslint@8.52.0)(typescript@5.2.2): + resolution: {integrity: sha512-C7AK2wn43GSaCUZ9do6Ksgi2g3mwFkMO3Cis96kzmgudoVaKyt62yNzJOktP0HDLb/iO2O0n2lBOzJgr6Q/cyg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 6.9.1 + '@typescript-eslint/types': 6.9.1 + '@typescript-eslint/typescript-estree': 6.9.1(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.9.1 + debug: 4.3.4 + eslint: 8.52.0 + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@6.9.1: + resolution: {integrity: sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.9.1 + '@typescript-eslint/visitor-keys': 6.9.1 + dev: true + + /@typescript-eslint/type-utils@6.9.1(eslint@8.52.0)(typescript@5.2.2): + resolution: {integrity: sha512-eh2oHaUKCK58qIeYp19F5V5TbpM52680sB4zNSz29VBQPTWIlE/hCj5P5B1AChxECe/fmZlspAWFuRniep1Skg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 6.9.1(typescript@5.2.2) + '@typescript-eslint/utils': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + debug: 4.3.4 + eslint: 8.52.0 + ts-api-utils: 1.0.3(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@6.9.1: + resolution: {integrity: sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==} + engines: {node: ^16.0.0 || >=18.0.0} + dev: true + + /@typescript-eslint/typescript-estree@6.9.1(typescript@5.2.2): + resolution: {integrity: sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 6.9.1 + '@typescript-eslint/visitor-keys': 6.9.1 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@6.9.1(eslint@8.52.0)(typescript@5.2.2): + resolution: {integrity: sha512-L1T0A5nFdQrMVunpZgzqPL6y2wVreSyHhKGZryS6jrEN7bD9NplVAyMryUhXsQ4TWLnZmxc2ekar/lSGIlprCA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0) + '@types/json-schema': 7.0.14 + '@types/semver': 7.5.4 + '@typescript-eslint/scope-manager': 6.9.1 + '@typescript-eslint/types': 6.9.1 + '@typescript-eslint/typescript-estree': 6.9.1(typescript@5.2.2) + eslint: 8.52.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@6.9.1: + resolution: {integrity: sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.9.1 + eslint-visitor-keys: 3.4.3 + dev: true + + /@ungap/structured-clone@1.2.0: + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + dev: true + + /@vitest/expect@0.34.6: + resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} + dependencies: + '@vitest/spy': 0.34.6 + '@vitest/utils': 0.34.6 + chai: 4.3.10 + dev: true + + /@vitest/runner@0.34.6: + resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} + dependencies: + '@vitest/utils': 0.34.6 + p-limit: 4.0.0 + pathe: 1.1.1 + dev: true + + /@vitest/snapshot@0.34.6: + resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} + dependencies: + magic-string: 0.30.5 + pathe: 1.1.1 + pretty-format: 29.7.0 + dev: true + + /@vitest/spy@0.34.6: + resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} + dependencies: + tinyspy: 2.2.0 + dev: true + + /@vitest/utils@0.34.6: + resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} + dependencies: + diff-sequences: 29.6.3 + loupe: 2.3.7 + pretty-format: 29.7.0 + dev: true + + /acorn-jsx@5.3.2(acorn@8.11.2): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.11.2 + dev: true + + /acorn-walk@8.3.0: + resolution: {integrity: sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn@8.11.2: + resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + + /any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + requiresBuild: true + dev: true + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + requiresBuild: true + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /are-docs-informative@0.0.2: + resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} + engines: {node: '>=14'} + dev: true + + /arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + requiresBuild: true + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /array-buffer-byte-length@1.0.0: + resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} + dependencies: + call-bind: 1.0.5 + is-array-buffer: 3.0.2 + dev: true + + /array-includes@3.1.7: + resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 + is-string: 1.0.7 + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.tosorted@1.1.2: + resolution: {integrity: sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-shim-unscopables: 1.0.2 + get-intrinsic: 1.2.2 + dev: true + + /arraybuffer.prototype.slice@1.0.2: + resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 + is-array-buffer: 3.0.2 + is-shared-array-buffer: 1.0.2 + dev: true + + /assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true + + /asynciterator.prototype@1.0.0: + resolution: {integrity: sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==} + dependencies: + has-symbols: 1.0.3 + dev: true + + /available-typed-arrays@1.0.5: + resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + engines: {node: '>= 0.4'} + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + requiresBuild: true + dev: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + requiresBuild: true + dependencies: + fill-range: 7.0.1 + dev: true + + /builtin-modules@3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + dev: true + + /builtins@5.0.1: + resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==} + dependencies: + semver: 7.5.4 + dev: true + + /cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + dev: true + + /call-bind@1.0.5: + resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} + dependencies: + function-bind: 1.1.2 + get-intrinsic: 1.2.2 + set-function-length: 1.1.1 + dev: true + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + requiresBuild: true + dev: true + + /chai@4.3.10: + resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.3 + get-func-name: 2.0.2 + loupe: 2.3.7 + pathval: 1.1.1 + type-detect: 4.0.8 + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /character-entities-legacy@1.1.4: + resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} + dev: true + + /character-entities@1.2.4: + resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} + dev: true + + /character-reference-invalid@1.1.4: + resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} + dev: true + + /check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + dependencies: + get-func-name: 2.0.2 + dev: true + + /chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + requiresBuild: true + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + dev: true + + /clean-regexp@1.0.0: + resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} + engines: {node: '>=4'} + dependencies: + escape-string-regexp: 1.0.5 + dev: true + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + requiresBuild: true + dev: true + + /comment-parser@1.4.0: + resolution: {integrity: sha512-QLyTNiZ2KDOibvFPlZ6ZngVsZ/0gYnE6uTXi5aoDg8ed3AkJAz4sEje3Y8a29hQ1s6A99MZXe47fLAXQ1rTqaw==} + engines: {node: '>= 12.0.0'} + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + requiresBuild: true + dev: true + + /debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /deep-eql@4.1.3: + resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} + engines: {node: '>=6'} + dependencies: + type-detect: 4.0.8 + dev: true + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /define-data-property@1.1.1: + resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.2 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + dev: true + + /define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + has-property-descriptors: 1.0.1 + object-keys: 1.1.1 + dev: true + + /didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + requiresBuild: true + dev: true + + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + requiresBuild: true + dev: true + + /doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: true + + /es-abstract@1.22.3: + resolution: {integrity: sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + arraybuffer.prototype.slice: 1.0.2 + available-typed-arrays: 1.0.5 + call-bind: 1.0.5 + es-set-tostringtag: 2.0.2 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.2 + get-symbol-description: 1.0.0 + globalthis: 1.0.3 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + has-proto: 1.0.1 + has-symbols: 1.0.3 + hasown: 2.0.0 + internal-slot: 1.0.6 + is-array-buffer: 3.0.2 + is-callable: 1.2.7 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-typed-array: 1.1.12 + is-weakref: 1.0.2 + object-inspect: 1.13.1 + object-keys: 1.1.1 + object.assign: 4.1.4 + regexp.prototype.flags: 1.5.1 + safe-array-concat: 1.0.1 + safe-regex-test: 1.0.0 + string.prototype.trim: 1.2.8 + string.prototype.trimend: 1.0.7 + string.prototype.trimstart: 1.0.7 + typed-array-buffer: 1.0.0 + typed-array-byte-length: 1.0.0 + typed-array-byte-offset: 1.0.0 + typed-array-length: 1.0.4 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.13 + dev: true + + /es-iterator-helpers@1.0.15: + resolution: {integrity: sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==} + dependencies: + asynciterator.prototype: 1.0.0 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-set-tostringtag: 2.0.2 + function-bind: 1.1.2 + get-intrinsic: 1.2.2 + globalthis: 1.0.3 + has-property-descriptors: 1.0.1 + has-proto: 1.0.1 + has-symbols: 1.0.3 + internal-slot: 1.0.6 + iterator.prototype: 1.1.2 + safe-array-concat: 1.0.1 + dev: true + + /es-set-tostringtag@2.0.2: + resolution: {integrity: sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.2 + has-tostringtag: 1.0.0 + hasown: 2.0.0 + dev: true + + /es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + dependencies: + hasown: 2.0.0 + dev: true + + /es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + + /esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + dev: true + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-compat-utils@0.1.2(eslint@8.52.0): + resolution: {integrity: sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + dependencies: + eslint: 8.52.0 + dev: true + + /eslint-config-flat-gitignore@0.1.1: + resolution: {integrity: sha512-ysq0QpN63+uaxE67U0g0HeCweIpv8Ztp7yvm0nYiM2TBalRIG6KQLO5J6lAz2gkA8KVis/QsJppe+BR5VigtWQ==} + dependencies: + parse-gitignore: 2.0.0 + dev: true + + /eslint-define-config@1.24.1: + resolution: {integrity: sha512-o36vBhPSWyIQlHoMqGhhcGmOOm2A2ccBVIdLTG/AWdm9YmjpsLpf+5ntf9LlHR6dduLREgxtGwvwPwSt7vnXJg==} + engines: {node: '>=18.0.0', npm: '>=9.0.0', pnpm: '>= 8.6.0'} + dev: true + + /eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + dependencies: + debug: 3.2.7 + is-core-module: 2.13.1 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.9.1)(eslint-import-resolver-node@0.3.9)(eslint@8.52.0): + resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + debug: 3.2.7 + eslint: 8.52.0 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-antfu@1.0.1(eslint@8.52.0): + resolution: {integrity: sha512-NL89JeF73mMwPMaLALLizOk7m+WboEJn4QB+GKRufG3a7jm4fpnkxZPPd2gu8X81ad9eUo3y+RsZ9TczjPFdQQ==} + peerDependencies: + eslint: '*' + dependencies: + eslint: 8.52.0 + dev: true + + /eslint-plugin-es-x@7.3.0(eslint@8.52.0): + resolution: {integrity: sha512-W9zIs+k00I/I13+Bdkl/zG1MEO07G97XjUSQuH117w620SJ6bHtLUmoMvkGA2oYnI/gNdr+G7BONLyYnFaLLEQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '>=8' + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0) + '@eslint-community/regexpp': 4.10.0 + eslint: 8.52.0 + dev: true + + /eslint-plugin-eslint-comments@3.2.0(eslint@8.52.0): + resolution: {integrity: sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==} + engines: {node: '>=6.5.0'} + peerDependencies: + eslint: '>=4.19.1' + dependencies: + escape-string-regexp: 1.0.5 + eslint: 8.52.0 + ignore: 5.2.4 + dev: true + + /eslint-plugin-i@2.29.0(@typescript-eslint/parser@6.9.1)(eslint@8.52.0): + resolution: {integrity: sha512-slGeTS3GQzx9267wLJnNYNO8X9EHGsc75AKIAFvnvMYEcTJKotPKL1Ru5PIGVHIVet+2DsugePWp8Oxpx8G22w==} + engines: {node: '>=12'} + peerDependencies: + eslint: ^7.2.0 || ^8 + dependencies: + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.52.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.9.1)(eslint-import-resolver-node@0.3.9)(eslint@8.52.0) + get-tsconfig: 4.7.2 + is-glob: 4.0.3 + minimatch: 3.1.2 + resolve: 1.22.8 + semver: 7.5.4 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-plugin-jsdoc@46.8.2(eslint@8.52.0): + resolution: {integrity: sha512-5TSnD018f3tUJNne4s4gDWQflbsgOycIKEUBoCLn6XtBMgNHxQFmV8vVxUtiPxAQq8lrX85OaSG/2gnctxw9uQ==} + engines: {node: '>=16'} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + '@es-joy/jsdoccomment': 0.40.1 + are-docs-informative: 0.0.2 + comment-parser: 1.4.0 + debug: 4.3.4 + escape-string-regexp: 4.0.0 + eslint: 8.52.0 + esquery: 1.5.0 + is-builtin-module: 3.2.1 + semver: 7.5.4 + spdx-expression-parse: 3.0.1 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-jsonc@2.10.0(eslint@8.52.0): + resolution: {integrity: sha512-9d//o6Jyh4s1RxC9fNSt1+MMaFN2ruFdXPG9XZcb/mR2KkfjADYiNL/hbU6W0Cyxfg3tS/XSFuhl5LgtMD8hmw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0) + eslint: 8.52.0 + eslint-compat-utils: 0.1.2(eslint@8.52.0) + jsonc-eslint-parser: 2.4.0 + natural-compare: 1.4.0 + dev: true + + /eslint-plugin-markdown@3.0.1(eslint@8.52.0): + resolution: {integrity: sha512-8rqoc148DWdGdmYF6WSQFT3uQ6PO7zXYgeBpHAOAakX/zpq+NvFYbDA/H7PYzHajwtmaOzAwfxyl++x0g1/N9A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + eslint: 8.52.0 + mdast-util-from-markdown: 0.8.5 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-n@16.2.0(eslint@8.52.0): + resolution: {integrity: sha512-AQER2jEyQOt1LG6JkGJCCIFotzmlcCZFur2wdKrp1JX2cNotC7Ae0BcD/4lLv3lUAArM9uNS8z/fsvXTd0L71g==} + engines: {node: '>=16.0.0'} + peerDependencies: + eslint: '>=7.0.0' + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0) + builtins: 5.0.1 + eslint: 8.52.0 + eslint-plugin-es-x: 7.3.0(eslint@8.52.0) + get-tsconfig: 4.7.2 + ignore: 5.2.4 + is-core-module: 2.13.1 + minimatch: 3.1.2 + resolve: 1.22.8 + semver: 7.5.4 + dev: true + + /eslint-plugin-no-only-tests@3.1.0: + resolution: {integrity: sha512-Lf4YW/bL6Un1R6A76pRZyE1dl1vr31G/ev8UzIc/geCgFWyrKil8hVjYqWVKGB/UIGmb6Slzs9T0wNezdSVegw==} + engines: {node: '>=5.0.0'} + dev: true + + /eslint-plugin-react-hooks@4.6.0(eslint@8.52.0): + resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + dependencies: + eslint: 8.52.0 + dev: true + + /eslint-plugin-react-refresh@0.4.4(eslint@8.52.0): + resolution: {integrity: sha512-eD83+65e8YPVg6603Om2iCIwcQJf/y7++MWm4tACtEswFLYMwxwVWAfwN+e19f5Ad/FOyyNg9Dfi5lXhH3Y3rA==} + peerDependencies: + eslint: '>=7' + dependencies: + eslint: 8.52.0 + dev: true + + /eslint-plugin-react@7.33.2(eslint@8.52.0): + resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + array-includes: 3.1.7 + array.prototype.flatmap: 1.3.2 + array.prototype.tosorted: 1.1.2 + doctrine: 2.1.0 + es-iterator-helpers: 1.0.15 + eslint: 8.52.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.7 + object.fromentries: 2.0.7 + object.hasown: 1.1.3 + object.values: 1.1.7 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 6.3.1 + string.prototype.matchall: 4.0.10 + dev: true + + /eslint-plugin-tailwindcss@3.13.0(tailwindcss@3.3.5): + resolution: {integrity: sha512-Fcep4KDRLWaK3KmkQbdyKHG0P4GdXFmXdDaweTIPcgOP60OOuWFbh1++dufRT28Q4zpKTKaHwTsXPJ4O/EjU2Q==} + engines: {node: '>=12.13.0'} + peerDependencies: + tailwindcss: ^3.3.2 + dependencies: + fast-glob: 3.3.1 + postcss: 8.4.31 + tailwindcss: 3.3.5 + dev: true + + /eslint-plugin-unicorn@48.0.1(eslint@8.52.0): + resolution: {integrity: sha512-FW+4r20myG/DqFcCSzoumaddKBicIPeFnTrifon2mWIzlfyvzwyqZjqVP7m4Cqr/ZYisS2aiLghkUWaPg6vtCw==} + engines: {node: '>=16'} + peerDependencies: + eslint: '>=8.44.0' + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0) + ci-info: 3.9.0 + clean-regexp: 1.0.0 + eslint: 8.52.0 + esquery: 1.5.0 + indent-string: 4.0.0 + is-builtin-module: 3.2.1 + jsesc: 3.0.2 + lodash: 4.17.21 + pluralize: 8.0.0 + read-pkg-up: 7.0.1 + regexp-tree: 0.1.27 + regjsparser: 0.10.0 + semver: 7.5.4 + strip-indent: 3.0.0 + dev: true + + /eslint-plugin-unused-imports@3.0.0(@typescript-eslint/eslint-plugin@6.9.1)(eslint@8.52.0): + resolution: {integrity: sha512-sduiswLJfZHeeBJ+MQaG+xYzSWdRXoSw61DpU13mzWumCkR0ufD0HmO4kdNokjrkluMHpj/7PJeN35pgbhW3kw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^6.0.0 + eslint: ^8.0.0 + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + dependencies: + '@typescript-eslint/eslint-plugin': 6.9.1(@typescript-eslint/parser@6.9.1)(eslint@8.52.0)(typescript@5.2.2) + eslint: 8.52.0 + eslint-rule-composer: 0.3.0 + dev: true + + /eslint-plugin-vitest@0.3.8(@typescript-eslint/eslint-plugin@6.9.1)(eslint@8.52.0)(typescript@5.2.2)(vitest@0.34.6): + resolution: {integrity: sha512-MYQJzg3i+nLkaIQgjnOhtqHYIt0W6nErqAOKI3LTSQ2aOgcNHGYTwOhpnwGC1IXTvGWjKgAwb7rHwLpcHWHSAQ==} + engines: {node: 14.x || >= 16} + peerDependencies: + '@typescript-eslint/eslint-plugin': '*' + eslint: '>=8.0.0' + vitest: '*' + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + dependencies: + '@typescript-eslint/eslint-plugin': 6.9.1(@typescript-eslint/parser@6.9.1)(eslint@8.52.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + eslint: 8.52.0 + vitest: 0.34.6 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /eslint-plugin-yml@1.10.0(eslint@8.52.0): + resolution: {integrity: sha512-53SUwuNDna97lVk38hL/5++WXDuugPM9SUQ1T645R0EHMRCdBIIxGye/oOX2qO3FQ7aImxaUZJU/ju+NMUBrLQ==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + dependencies: + debug: 4.3.4 + eslint: 8.52.0 + eslint-compat-utils: 0.1.2(eslint@8.52.0) + lodash: 4.17.21 + natural-compare: 1.4.0 + yaml-eslint-parser: 1.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-rule-composer@0.3.0: + resolution: {integrity: sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==} + engines: {node: '>=4.0.0'} + dev: true + + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.52.0: + resolution: {integrity: sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0) + '@eslint-community/regexpp': 4.10.0 + '@eslint/eslintrc': 2.1.2 + '@eslint/js': 8.52.0 + '@humanwhocodes/config-array': 0.11.13 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.23.0 + graphemer: 1.4.0 + ignore: 5.2.4 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.11.2 + acorn-jsx: 5.3.2(acorn@8.11.2) + eslint-visitor-keys: 3.4.3 + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + requiresBuild: true + dependencies: + reusify: 1.0.4 + dev: true + + /fflate@0.8.1: + resolution: {integrity: sha512-/exOvEuc+/iaUm105QIiOt4LpBdMTWsXxqR0HDF35vx3fmaKzw7354gTilCh5rkzEt8WYyG//ku3h3nRmd7CHQ==} + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.1.1 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + requiresBuild: true + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + dev: true + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@3.1.1: + resolution: {integrity: sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==} + engines: {node: '>=12.0.0'} + dependencies: + flatted: 3.2.9 + keyv: 4.5.4 + rimraf: 3.0.2 + dev: true + + /flatted@3.2.9: + resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} + dev: true + + /for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + requiresBuild: true + dev: true + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + dev: true + + /function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + functions-have-names: 1.2.3 + dev: true + + /functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + + /get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + dev: true + + /get-intrinsic@1.2.2: + resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} + dependencies: + function-bind: 1.1.2 + has-proto: 1.0.1 + has-symbols: 1.0.3 + hasown: 2.0.0 + dev: true + + /get-symbol-description@1.0.0: + resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + dev: true + + /get-tsconfig@4.7.2: + resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} + dependencies: + resolve-pkg-maps: 1.0.0 + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + requiresBuild: true + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + requiresBuild: true + dependencies: + is-glob: 4.0.3 + dev: true + + /glob@7.1.6: + resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} + requiresBuild: true + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /globals@13.23.0: + resolution: {integrity: sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globalthis@1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.1 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.1 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.2 + dev: true + + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has-property-descriptors@1.0.1: + resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==} + dependencies: + get-intrinsic: 1.2.2 + dev: true + + /has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + dev: true + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: true + + /has-tostringtag@1.0.0: + resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /hasown@2.0.0: + resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + dev: true + + /hosted-git-info@2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + dev: true + + /ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + requiresBuild: true + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + requiresBuild: true + dev: true + + /internal-slot@1.0.6: + resolution: {integrity: sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.2 + hasown: 2.0.0 + side-channel: 1.0.4 + dev: true + + /is-alphabetical@1.0.4: + resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} + dev: true + + /is-alphanumerical@1.0.4: + resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} + dependencies: + is-alphabetical: 1.0.4 + is-decimal: 1.0.4 + dev: true + + /is-array-buffer@3.0.2: + resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + is-typed-array: 1.1.12 + dev: true + + /is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true + + /is-async-function@2.0.0: + resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: true + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + requiresBuild: true + dependencies: + binary-extensions: 2.2.0 + dev: true + + /is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + has-tostringtag: 1.0.0 + dev: true + + /is-builtin-module@3.2.1: + resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} + engines: {node: '>=6'} + dependencies: + builtin-modules: 3.3.0 + dev: true + + /is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: true + + /is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + dependencies: + hasown: 2.0.0 + dev: true + + /is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-decimal@1.0.4: + resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dev: true + + /is-finalizationregistry@1.0.2: + resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + dependencies: + call-bind: 1.0.5 + dev: true + + /is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-hexadecimal@1.0.4: + resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} + dev: true + + /is-map@2.0.2: + resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} + dev: true + + /is-negative-zero@2.0.2: + resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + requiresBuild: true + dev: true + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + has-tostringtag: 1.0.0 + dev: true + + /is-set@2.0.2: + resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} + dev: true + + /is-shared-array-buffer@1.0.2: + resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + dependencies: + call-bind: 1.0.5 + dev: true + + /is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /is-typed-array@1.1.12: + resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} + engines: {node: '>= 0.4'} + dependencies: + which-typed-array: 1.1.13 + dev: true + + /is-weakmap@2.0.1: + resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} + dev: true + + /is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.5 + dev: true + + /is-weakset@2.0.2: + resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + dev: true + + /isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /iterator.prototype@1.1.2: + resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + dependencies: + define-properties: 1.2.1 + get-intrinsic: 1.2.2 + has-symbols: 1.0.3 + reflect.getprototypeof: 1.0.4 + set-function-name: 2.0.1 + dev: true + + /jiti@1.21.0: + resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} + hasBin: true + requiresBuild: true + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /jsdoc-type-pratt-parser@4.0.0: + resolution: {integrity: sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==} + engines: {node: '>=12.0.0'} + dev: true + + /jsesc@0.5.0: + resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} + hasBin: true + dev: true + + /jsesc@3.0.2: + resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /jsonc-eslint-parser@2.4.0: + resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.11.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + semver: 7.5.4 + dev: true + + /jsonc-parser@3.2.0: + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + dev: true + + /jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + dependencies: + array-includes: 3.1.7 + array.prototype.flat: 1.3.2 + object.assign: 4.1.4 + object.values: 1.1.7 + dev: true + + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: true + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + requiresBuild: true + dev: true + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /local-pkg@0.4.3: + resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} + engines: {node: '>=14'} + dev: true + + /locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + dependencies: + p-locate: 4.1.0 + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + dev: true + + /loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + dependencies: + get-func-name: 2.0.2 + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /magic-string@0.30.5: + resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /mdast-util-from-markdown@0.8.5: + resolution: {integrity: sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==} + dependencies: + '@types/mdast': 3.0.14 + mdast-util-to-string: 2.0.0 + micromark: 2.11.4 + parse-entities: 2.0.0 + unist-util-stringify-position: 2.0.3 + transitivePeerDependencies: + - supports-color + dev: true + + /mdast-util-to-string@2.0.0: + resolution: {integrity: sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==} + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromark@2.11.4: + resolution: {integrity: sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==} + dependencies: + debug: 4.3.4 + parse-entities: 2.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + requiresBuild: true + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /mlly@1.4.2: + resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==} + dependencies: + acorn: 8.11.2 + pathe: 1.1.1 + pkg-types: 1.0.3 + ufo: 1.3.1 + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + requiresBuild: true + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + dev: true + + /nanoid@3.3.6: + resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /normalize-package-data@2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.22.8 + semver: 5.7.2 + validate-npm-package-license: 3.0.4 + dev: true + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dev: true + + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + dev: true + + /object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + requiresBuild: true + dev: true + + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + dev: true + + /object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object.assign@4.1.4: + resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + + /object.entries@1.1.7: + resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /object.fromentries@2.0.7: + resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /object.hasown@1.1.3: + resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==} + dependencies: + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /object.values@1.1.7: + resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + requiresBuild: true + dependencies: + wrappy: 1.0.2 + dev: true + + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + yocto-queue: 1.0.0 + dev: true + + /p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + dependencies: + p-limit: 2.3.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /parse-entities@2.0.0: + resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} + dependencies: + character-entities: 1.2.4 + character-entities-legacy: 1.1.4 + character-reference-invalid: 1.1.4 + is-alphanumerical: 1.0.4 + is-decimal: 1.0.4 + is-hexadecimal: 1.0.4 + dev: true + + /parse-gitignore@2.0.0: + resolution: {integrity: sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==} + engines: {node: '>=14'} + dev: true + + /parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.22.13 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /pathe@1.1.1: + resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==} + dev: true + + /pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + requiresBuild: true + dev: true + + /pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dev: true + + /pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + requiresBuild: true + dev: true + + /pkg-types@1.0.3: + resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} + dependencies: + jsonc-parser: 3.2.0 + mlly: 1.4.2 + pathe: 1.1.1 + dev: true + + /pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + dev: true + + /pmtiles@2.11.0: + resolution: {integrity: sha512-dU9SzzaqmCGpdEuTnIba6bDHT6j09ZJFIXxwGpvkiEnce3ZnBB1VKt6+EOmJGueriweaZLAMTUmKVElU2CBe0g==} + dependencies: + fflate: 0.8.1 + dev: true + + /postcss-import@15.1.0(postcss@8.4.31): + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + requiresBuild: true + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.31 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.8 + dev: true + + /postcss-js@4.0.1(postcss@8.4.31): + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + requiresBuild: true + peerDependencies: + postcss: ^8.4.21 + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.31 + dev: true + + /postcss-load-config@4.0.1(postcss@8.4.31): + resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} + engines: {node: '>= 14'} + requiresBuild: true + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.1.0 + postcss: 8.4.31 + yaml: 2.3.3 + dev: true + + /postcss-nested@6.0.1(postcss@8.4.31): + resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} + engines: {node: '>=12.0'} + requiresBuild: true + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.31 + postcss-selector-parser: 6.0.13 + dev: true + + /postcss-selector-parser@6.0.13: + resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==} + engines: {node: '>=4'} + requiresBuild: true + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + + /postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + requiresBuild: true + dev: true + + /postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + + /prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + dev: true + + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + requiresBuild: true + dev: true + + /react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + dev: true + + /react-is@18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true + + /read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + requiresBuild: true + dependencies: + pify: 2.3.0 + dev: true + + /read-pkg-up@7.0.1: + resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + read-pkg: 5.2.0 + type-fest: 0.8.1 + dev: true + + /read-pkg@5.2.0: + resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} + engines: {node: '>=8'} + dependencies: + '@types/normalize-package-data': 2.4.3 + normalize-package-data: 2.5.0 + parse-json: 5.2.0 + type-fest: 0.6.0 + dev: true + + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + requiresBuild: true + dependencies: + picomatch: 2.3.1 + dev: true + + /reflect.getprototypeof@1.0.4: + resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 + globalthis: 1.0.3 + which-builtin-type: 1.1.3 + dev: true + + /regexp-tree@0.1.27: + resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} + hasBin: true + dev: true + + /regexp.prototype.flags@1.5.1: + resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + set-function-name: 2.0.1 + dev: true + + /regjsparser@0.10.0: + resolution: {integrity: sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==} + hasBin: true + dependencies: + jsesc: 0.5.0 + dev: true + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + dev: true + + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + requiresBuild: true + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.1.6 + dev: true + + /rollup@3.29.4: + resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + requiresBuild: true + dependencies: + queue-microtask: 1.2.3 + dev: true + + /safe-array-concat@1.0.1: + resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + has-symbols: 1.0.3 + isarray: 2.0.5 + dev: true + + /safe-regex-test@1.0.0: + resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + is-regex: 1.1.4 + dev: true + + /semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + dev: true + + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + dev: true + + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /set-function-length@1.1.1: + resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + get-intrinsic: 1.2.2 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + dev: true + + /set-function-name@2.0.1: + resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.1 + dev: true + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /side-channel@1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + object-inspect: 1.13.1 + dev: true + + /siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + + /spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.16 + dev: true + + /spdx-exceptions@2.3.0: + resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} + dev: true + + /spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + dependencies: + spdx-exceptions: 2.3.0 + spdx-license-ids: 3.0.16 + dev: true + + /spdx-license-ids@3.0.16: + resolution: {integrity: sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==} + dev: true + + /stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: true + + /std-env@3.4.3: + resolution: {integrity: sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==} + dev: true + + /string.prototype.matchall@4.0.10: + resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 + has-symbols: 1.0.3 + internal-slot: 1.0.6 + regexp.prototype.flags: 1.5.1 + set-function-name: 2.0.1 + side-channel: 1.0.4 + dev: true + + /string.prototype.trim@1.2.8: + resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /string.prototype.trimend@1.0.7: + resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /string.prototype.trimstart@1.0.7: + resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + dependencies: + min-indent: 1.0.1 + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + dependencies: + acorn: 8.11.2 + dev: true + + /sucrase@3.34.0: + resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==} + engines: {node: '>=8'} + hasBin: true + requiresBuild: true + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + commander: 4.1.1 + glob: 7.1.6 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + dev: true + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /tailwindcss@3.3.5: + resolution: {integrity: sha512-5SEZU4J7pxZgSkv7FP1zY8i2TIAOooNZ1e/OGtxIEv6GltpoiXUqWvLy89+a10qYTB1N5Ifkuw9lqQkN9sscvA==} + engines: {node: '>=14.0.0'} + hasBin: true + requiresBuild: true + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.5.3 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.1 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.0 + lilconfig: 2.1.0 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.31 + postcss-import: 15.1.0(postcss@8.4.31) + postcss-js: 4.0.1(postcss@8.4.31) + postcss-load-config: 4.0.1(postcss@8.4.31) + postcss-nested: 6.0.1(postcss@8.4.31) + postcss-selector-parser: 6.0.13 + resolve: 1.22.8 + sucrase: 3.34.0 + transitivePeerDependencies: + - ts-node + dev: true + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + requiresBuild: true + dependencies: + thenify: 3.3.1 + dev: true + + /thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + requiresBuild: true + dependencies: + any-promise: 1.3.0 + dev: true + + /tinybench@2.5.1: + resolution: {integrity: sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==} + dev: true + + /tinypool@0.7.0: + resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} + engines: {node: '>=14.0.0'} + dev: true + + /tinyspy@2.2.0: + resolution: {integrity: sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==} + engines: {node: '>=14.0.0'} + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + requiresBuild: true + dependencies: + is-number: 7.0.0 + dev: true + + /ts-api-utils@1.0.3(typescript@5.2.2): + resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} + engines: {node: '>=16.13.0'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.2.2 + dev: true + + /ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + requiresBuild: true + dev: true + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /type-fest@0.6.0: + resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} + engines: {node: '>=8'} + dev: true + + /type-fest@0.8.1: + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} + dev: true + + /typed-array-buffer@1.0.0: + resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + is-typed-array: 1.1.12 + dev: true + + /typed-array-byte-length@1.0.0: + resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.12 + dev: true + + /typed-array-byte-offset@1.0.0: + resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.5 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.12 + dev: true + + /typed-array-length@1.0.4: + resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + dependencies: + call-bind: 1.0.5 + for-each: 0.3.3 + is-typed-array: 1.1.12 + dev: true + + /typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /ufo@1.3.1: + resolution: {integrity: sha512-uY/99gMLIOlJPwATcMVYfqDSxUR9//AUcgZMzwfSTJPDKzA1S8mX4VLqa+fiAtveraQUBCz4FFcwVZBGbwBXIw==} + dev: true + + /unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.5 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: true + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true + + /unist-util-stringify-position@2.0.3: + resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} + dependencies: + '@types/unist': 2.0.9 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.1 + dev: true + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + requiresBuild: true + dev: true + + /uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + dev: true + + /validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + dev: true + + /vite-node@0.34.6(@types/node@20.8.10): + resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} + engines: {node: '>=v14.18.0'} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + mlly: 1.4.2 + pathe: 1.1.1 + picocolors: 1.0.0 + vite: 4.5.0(@types/node@20.8.10) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /vite-plugin-top-level-await@1.3.1(vite@4.5.0): + resolution: {integrity: sha512-55M1h4NAwkrpxPNOJIBzKZFihqLUzIgnElLSmPNPMR2Fn9+JHKaNg3sVX1Fq+VgvuBksQYxiD3OnwQAUu7kaPQ==} + peerDependencies: + vite: '>=2.8' + dependencies: + '@rollup/plugin-virtual': 3.0.2 + '@swc/core': 1.3.95 + uuid: 9.0.1 + vite: 4.5.0(@types/node@20.8.10) + transitivePeerDependencies: + - '@swc/helpers' + - rollup + dev: true + + /vite-plugin-wasm@3.2.2(vite@4.5.0): + resolution: {integrity: sha512-cdbBUNR850AEoMd5nvLmnyeq63CSfoP1ctD/L2vLk/5+wsgAPlAVAzUK5nGKWO/jtehNlrSSHLteN+gFQw7VOA==} + peerDependencies: + vite: ^2 || ^3 || ^4 + dependencies: + vite: 4.5.0(@types/node@20.8.10) + dev: true + + /vite@4.5.0(@types/node@20.8.10): + resolution: {integrity: sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.8.10 + esbuild: 0.18.20 + postcss: 8.4.31 + rollup: 3.29.4 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /vitest@0.34.6: + resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} + engines: {node: '>=v14.18.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + playwright: '*' + safaridriver: '*' + webdriverio: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true + dependencies: + '@types/chai': 4.3.9 + '@types/chai-subset': 1.3.4 + '@types/node': 20.8.10 + '@vitest/expect': 0.34.6 + '@vitest/runner': 0.34.6 + '@vitest/snapshot': 0.34.6 + '@vitest/spy': 0.34.6 + '@vitest/utils': 0.34.6 + acorn: 8.11.2 + acorn-walk: 8.3.0 + cac: 6.7.14 + chai: 4.3.10 + debug: 4.3.4 + local-pkg: 0.4.3 + magic-string: 0.30.5 + pathe: 1.1.1 + picocolors: 1.0.0 + std-env: 3.4.3 + strip-literal: 1.3.0 + tinybench: 2.5.1 + tinypool: 0.7.0 + vite: 4.5.0(@types/node@20.8.10) + vite-node: 0.34.6(@types/node@20.8.10) + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which-builtin-type@1.1.3: + resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} + engines: {node: '>= 0.4'} + dependencies: + function.prototype.name: 1.1.6 + has-tostringtag: 1.0.0 + is-async-function: 2.0.0 + is-date-object: 1.0.5 + is-finalizationregistry: 1.0.2 + is-generator-function: 1.0.10 + is-regex: 1.1.4 + is-weakref: 1.0.2 + isarray: 2.0.5 + which-boxed-primitive: 1.0.2 + which-collection: 1.0.1 + which-typed-array: 1.1.13 + dev: true + + /which-collection@1.0.1: + resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} + dependencies: + is-map: 2.0.2 + is-set: 2.0.2 + is-weakmap: 2.0.1 + is-weakset: 2.0.2 + dev: true + + /which-typed-array@1.1.13: + resolution: {integrity: sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.5 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /why-is-node-running@2.2.2: + resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + requiresBuild: true + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yaml-eslint-parser@1.2.2: + resolution: {integrity: sha512-pEwzfsKbTrB8G3xc/sN7aw1v6A6c/pKxLAkjclnAyo5g5qOh6eL9WGu0o3cSDQZKrTNk4KL4lQSwZW+nBkANEg==} + engines: {node: ^14.17.0 || >=16.0.0} + dependencies: + eslint-visitor-keys: 3.4.3 + lodash: 4.17.21 + yaml: 2.3.3 + dev: true + + /yaml@2.3.3: + resolution: {integrity: sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==} + engines: {node: '>= 14'} + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true + + /yocto-queue@1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + dev: true diff --git a/crates/utiles-wasm/src/lib.rs b/crates/utiles-wasm/src/lib.rs new file mode 100644 index 00000000..0791d3c8 --- /dev/null +++ b/crates/utiles-wasm/src/lib.rs @@ -0,0 +1,30 @@ +use wasm_bindgen::prelude::*; +use utiles::pmtiles::xyz2pmid as _xyz2pmid ; +use utiles::xyz2quadkey as uxyz2qk; + +#[wasm_bindgen] +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[wasm_bindgen] +pub fn pmtileid(x: u32, y: u32, z: u8) -> u64 { + _xyz2pmid(x, y, z) +} + +#[wasm_bindgen] +pub fn xyz2qk(x: u32, y: u32, z: u8) -> String{ + + uxyz2qk(x, y, z) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/utiles-wasm/tsconfig.json b/crates/utiles-wasm/tsconfig.json new file mode 100644 index 00000000..9b803ed0 --- /dev/null +++ b/crates/utiles-wasm/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "Node16", + "moduleResolution": "Node16", + "resolveJsonModule": true, + "allowJs": true, + "strict": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "noEmit": false, + "outDir": "dist", + "esModuleInterop": true, + "skipLibCheck": true + }, + "exclude": ["node_modules", "dist", "scratch"] +} diff --git a/crates/utiles-wasm/vitest.config.ts b/crates/utiles-wasm/vitest.config.ts new file mode 100644 index 00000000..f15f8438 --- /dev/null +++ b/crates/utiles-wasm/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + benchmark: { + exclude: ["node_modules", "dist", ".idea", ".git", ".cache"], + include: ["**/*.{bench,benchmark}.?(c|m)[jt]s?(x)"], + }, + }, +}); From e2229df15b62f5b952f36eced1ab29ee1208a9b2 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Tue, 7 Nov 2023 19:24:45 -0800 Subject: [PATCH 24/80] wasm exp tests --- crates/utiles-wasm/test/utiles-wasm.bench.ts | 38 +++++++++++ crates/utiles-wasm/test/utiles-wasm.test.ts | 72 ++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 crates/utiles-wasm/test/utiles-wasm.bench.ts create mode 100644 crates/utiles-wasm/test/utiles-wasm.test.ts diff --git a/crates/utiles-wasm/test/utiles-wasm.bench.ts b/crates/utiles-wasm/test/utiles-wasm.bench.ts new file mode 100644 index 00000000..05895ab3 --- /dev/null +++ b/crates/utiles-wasm/test/utiles-wasm.bench.ts @@ -0,0 +1,38 @@ +import {test, expect, bench} from 'vitest' +import * as utw from '../pkg/utiles_wasm.js' +import * as pmtiles from 'pmtiles' + +function xyz2quadkey( + x: number, y: number, z: number +) { + let quadkey = '' + for (let i = z; i > 0; i--) { + let digit = 0 + const mask = 1 << (i - 1) + if ((x & mask) !== 0) digit += 1 + if ((y & mask) !== 0) digit += 2 + quadkey += digit + } + return quadkey +} + +bench('js-quadkey', () => { + xyz2quadkey(486, 332, 20) +}) + +bench('wasm-quadkey', () => { + utw.xyz2qk(486, 332, 20) +}) + + +bench('js-pmtile-id', () => { + pmtiles.zxyToTileId( + 20, 486, 332 + ) + // .pmtileId2(486, 332, 20) + }) + + +bench('wasm-pmtile-id', () => { + utw.pmtileid(486, 332, 20) +}) diff --git a/crates/utiles-wasm/test/utiles-wasm.test.ts b/crates/utiles-wasm/test/utiles-wasm.test.ts new file mode 100644 index 00000000..7c24df7b --- /dev/null +++ b/crates/utiles-wasm/test/utiles-wasm.test.ts @@ -0,0 +1,72 @@ +import {test, expect, bench} from 'vitest' +import * as utw from '../pkg/utiles_wasm.js' +import * as pmtiles from 'pmtiles' + +function xyz2quadkey( + x: number, y: number, z: number +) { + let quadkey = '' + for (let i = z; i > 0; i--) { + let digit = 0 + const mask = 1 << (i - 1) + if ((x & mask) !== 0) digit += 1 + if ((y & mask) !== 0) digit += 2 + quadkey += digit + } + return quadkey +} +test( + 'uno', () => { + console.log('uno') + expect(1).toBe(1) + } +) +// 486, 332, 20 +test( + 'thingy', async () => { + console.log('thingy') + console.log(utw) + const added = utw.add(1, 2) + expect(added).toBe(3) + + + // const called = await utw.default() + // console.log(called) + + // expect(utw.add(1, 2)).toBe(3) + } +) + +test( + 'quadkey', async () => { + const jsres = xyz2quadkey(486, 332, 20) + const wasmres = utw.xyz2qk(486, 332, 20) + expect(jsres).toBe(wasmres) + + + // const called = await utw.default() + // console.log(called) + + // expect(utw.add(1, 2)).toBe(3) + } +) + +test( + 'pmtileid', async () => { + const jsres = pmtiles.zxyToTileId( + 15, 486, 332 + ) + const wasmres = utw.pmtileid(486, 332, 15) + + console.log( + { + jsres, + wasmres + } + ) + expect(jsres).toBe( + // debigint + Number(wasmres) + + ) + }) From 19ee6f46870cc54004ea73378992423d189abfaf Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Tue, 7 Nov 2023 19:38:11 -0800 Subject: [PATCH 25/80] funky.... --- crates/utiles-cli/src/cli.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index 3a7a2f46..b05cdf90 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -195,9 +195,7 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { Some(argv) => argv, None => std::env::args().collect::>(), }; - let args = Cli::parse_from(&argv); - // level is info by default and debug if --debug is passed // let level = if args.debug { // tracing::Level::DEBUG @@ -247,22 +245,9 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { .filter(|l| !l.as_ref().unwrap().is_empty()) { let lstr = line.unwrap(); - // println!("Line from stdin: `{}`", lstr); - // let json: serde_json::Value = serde_json::from_str(the_file)l; - let thingy = parse_bbox( &lstr, ).unwrap(); - - // match thingy { - // Ok(thingy) => thingy, - // Err(e) => { - // println!("Error parsing bbox: {}", e); - // continue; - // } - // } - // let thingy = BBox::from(lstr); - for tile in tiles( (thingy.west, thingy.south, thingy.east, thingy.north), ZoomOrZooms::Zoom(zoom), From 97ccc6f99f69f5aa5042e62d3c826f236cfb8c15 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Tue, 7 Nov 2023 20:23:04 -0800 Subject: [PATCH 26/80] jfc --- crates/utiles-cli/src/cli.rs | 138 ++++++++++++++++++++------ crates/utiles-cli/src/stdinterator.rs | 92 ++++++++++++++--- crates/utiles/src/tile.rs | 10 ++ python/utiles/libutiles.pyi | 12 +-- tests/test_mbtiles.py | 1 - 5 files changed, 202 insertions(+), 51 deletions(-) diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index b05cdf90..15c4e55d 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -1,3 +1,4 @@ +use std::error::Error; use clap::{Parser, Subcommand, ValueEnum}; use tracing::debug; @@ -6,6 +7,7 @@ use tracing_subscriber::EnvFilter; use utiles::parsing::parse_bbox; use utiles::tilejson::tilejson_stringify; use utiles::tiles; +use utiles::Tile; use utiles::zoom::ZoomOrZooms; use crate::stdinterator::StdInterator; use utilesqlite::mbtiles::Mbtiles; @@ -37,8 +39,10 @@ pub struct QuadkeyArgs { /// The remote to clone // #[arg(required = false, default_value = "-")] // quadkey: MaybeStdin, + // #[arg(required = false)] + // quadkey: Option, #[arg(required = false)] - quadkey: Option, + input: Option, } #[derive(Debug, Subcommand)] @@ -80,7 +84,10 @@ pub enum Commands { // NOT IMPLEMENTED YET // =================== #[command(name = "quadkey", visible_alias = "qk", about = "convert xyz <-> quadkey", long_about = None)] - Quadkey(QuadkeyArgs), + Quadkey{ + #[arg(required = false)] + input: Option, + }, #[command(name = "bounding-tile", about = "output tilejson", long_about = None)] BoundingTile { @@ -230,45 +237,116 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { match args.command { - Commands::Quadkey(quadkey) => { - let thingy = StdInterator::new(quadkey.quadkey).unwrap(); - for line in thingy { - println!("Line from stdin: `{}`", line.unwrap()); + Commands::Quadkey{ + input + } => { + // let thingy = StdInterator::new(quadkey.quadkey).unwrap(); + // for line in thingy { + // println!("Line from stdin: `{}`", line.unwrap()); + // } + let input_lines = StdInterator::new(input).unwrap(); + let lines = input_lines + .filter(|l| !l.is_err()) + .filter(|l| !l.as_ref().unwrap().is_empty()); + for line in lines { + // if the line bgins w '[' treat as tile + // otherwise treat as quadkey + let lstr = line.unwrap(); + + if lstr.starts_with('[') { + // treat as tile + let tile = Tile::from_json_arr(&lstr); + println!("{}", tile.quadkey()); + // let qk = utiles::xyz2quadkey(t.west, t.south, t.zoom); + // println!("{}", qk); + // let tile = parse_bbox(&lstr).unwrap(); + // let qk = utiles::xyz2quadkey(tile.west, tile.south, tile.zoom); + // println!("{}", qk); + } else { + + // treat as quadkey + let qk = lstr; + let tile = Tile::from_quadkey(&qk); + if tile.is_err() { + println!("Invalid quadkey: {}", qk); + } else { + println!("{}", tile.unwrap().json_arr()); + } + + // let (x, y, z) = utiles::quadkey2xyz(&qk); + // println!("{} {} {}", x, y, z); + } } } Commands::Tiles { zoom, input, seq } => { let input_lines = StdInterator::new(input).unwrap(); - // println!("zoom: {}", zoom); let mut niter = 0; - for line in input_lines + let tiles = input_lines .filter(|l| !l.is_err()) .filter(|l| !l.as_ref().unwrap().is_empty()) - { - let lstr = line.unwrap(); - let thingy = parse_bbox( - &lstr, - ).unwrap(); - for tile in tiles( - (thingy.west, thingy.south, thingy.east, thingy.north), + .map(|l| parse_bbox(&l.unwrap()).unwrap()) + .flat_map(|b| tiles( + (b.west, b.south, b.east, b.north), ZoomOrZooms::Zoom(zoom), - ) { - let tstr = tile.json_arr(); - // RS char if seq else "" - let rs = if seq { "\x1e\n" } else { "" }; - println!("{}{}", rs, tstr); - // println!("{}", tile.json_arr()); - - // call loop_fn if it's defined - niter += 1; - - // call fn every 1000 iterations - if niter % 1000 == 0 { - if let Some(f) = loop_fn { - f(); - } + )).enumerate(); + // let bboxes = lines + for (i, tile) in tiles { + let rs = if seq { "\x1e\n" } else { "" }; + println!("{}{}", rs, tile.json_arr()); + // call loop_fn if it's defined every 1000 iterations for signal break + if i % 1024 == 0 { + if let Some(f) = loop_fn { + f(); } } } + + // for tile in tiles { + // let tstr = tile.json_arr(); + // // RS char if seq else "" + // let rs = if seq { "\x1e\n" } else { "" }; + // println!("{}{}", rs, tstr); + // // println!("{}", tile.json_arr()); + // + // // call loop_fn if it's defined + // niter += 1; + // + // // call fn every 1000 iterations + // if niter % 1000 == 0 { + // if let Some(f) = loop_fn { + // f(); + // } + // } + // } + // for line in input_lines + // .filter(|l| !l.is_err()) + // .filter(|l| !l.as_ref().unwrap().is_empty()) + // { + // let lstr = line.unwrap(); + // let thingy = parse_bbox( + // &lstr, + // ).unwrap(); + // for tile in tiles( + // (thingy.west, thingy.south, thingy.east, thingy.north), + // ZoomOrZooms::Zoom(zoom), + // ) { + // let tstr = tile.json_arr(); + // // RS char if seq else "" + // let rs = if seq { "\x1e\n" } else { "" }; + // println!("{}{}", rs, tstr); + // // println!("{}", tile.json_arr()); + // + // // call loop_fn if it's defined + // niter += 1; + // + // // call fn every 1000 iterations + // if niter % 1000 == 0 { + // if let Some(f) = loop_fn { + // f(); + // } + // } + // } + // } } Commands::Lint { filepath, fix } => { diff --git a/crates/utiles-cli/src/stdinterator.rs b/crates/utiles-cli/src/stdinterator.rs index f5d7fc66..d18ff39a 100644 --- a/crates/utiles-cli/src/stdinterator.rs +++ b/crates/utiles-cli/src/stdinterator.rs @@ -1,10 +1,75 @@ +// use std::io; +// use std::io::BufRead; +// use clap::builder::Str; +// use tracing::debug; +// +// pub enum StdInteratorSource { +// Args(Vec<&str>), +// StdinRead(Box), +// } +// +// pub struct StdInterator { +// source: StdInteratorSource, +// } +// +// impl StdInterator { +// pub fn new(input: Option) -> io::Result { +// let source = match input { +// Some(file_content) => { +// if file_content == "-" { +// debug!("reading from stdin - got '-'"); +// let reader = Box::new(io::BufReader::new(io::stdin())); +// StdInteratorSource::StdinRead(reader) +// } else { +// debug!("reading from args: {:?}", file_content); +// StdInteratorSource::Args( +// file_content.splitn( +// file_content.matches(char::is_whitespace).count() + 1, +// char::is_whitespace, +// ).collect::>() +// // ).map(|s| s.to_string()).collect(), +// ) +// } +// } +// None => { +// let reader = Box::new(io::BufReader::new(io::stdin())); +// debug!("reading from stdin - no args"); +// StdInteratorSource::StdinRead(reader) +// } +// }; +// Ok(Self { source }) +// } +// } +// +// impl Iterator for StdInterator { +// type Item = io::Result< >; +// fn next(&mut self) -> Option { +// match &mut self.source { +// StdInteratorSource::Args(content) => { +// +// } +// StdInteratorSource::StdinRead(reader) => { +// let mut line = String::new(); +// match reader.read_line(&mut line) { +// Ok(0) => None, // EOF +// Ok(_) => { +// let l +// Some(Ok(line.trim_end().to_string())) +// }, +// Err(e) => Some(Err(e)), +// } +// } +// } +// } +// } use std::io; use std::io::BufRead; +use std::collections::VecDeque; use tracing::debug; pub enum StdInteratorSource { - Single(String), - Multiple(Box), + Args(VecDeque), + StdinRead(Box), } pub struct StdInterator { @@ -18,16 +83,20 @@ impl StdInterator { if file_content == "-" { debug!("reading from stdin - got '-'"); let reader = Box::new(io::BufReader::new(io::stdin())); - StdInteratorSource::Multiple(reader) + StdInteratorSource::StdinRead(reader) } else { - debug!("reading from args: {:?}", file_content); - StdInteratorSource::Single(file_content) + debug!("reading from args"); + let args = file_content + .lines() // This assumes that each line is separated by '\n' + .map(|s| s.to_string()) + .collect::>(); + StdInteratorSource::Args(args) } } None => { let reader = Box::new(io::BufReader::new(io::stdin())); debug!("reading from stdin - no args"); - StdInteratorSource::Multiple(reader) + StdInteratorSource::StdinRead(reader) } }; Ok(Self { source }) @@ -36,16 +105,11 @@ impl StdInterator { impl Iterator for StdInterator { type Item = io::Result; + fn next(&mut self) -> Option { match &mut self.source { - StdInteratorSource::Single(content) => { - if content.is_empty() { - None - } else { - Some(Ok(std::mem::take(content))) - } - } - StdInteratorSource::Multiple(reader) => { + StdInteratorSource::Args(content) => content.pop_front().map(Ok), + StdInteratorSource::StdinRead(reader) => { let mut line = String::new(); match reader.read_line(&mut line) { Ok(0) => None, // EOF diff --git a/crates/utiles/src/tile.rs b/crates/utiles/src/tile.rs index 825d6380..4287cc82 100644 --- a/crates/utiles/src/tile.rs +++ b/crates/utiles/src/tile.rs @@ -157,6 +157,16 @@ impl Tile { } } + pub fn from_json_arr(json_arr: &str) -> Self { + let res = serde_json::from_str(json_arr); + match res { + Ok(tile) => tile, + Err(e) => { + panic!("Invalid json_arr: {e}"); + } + } + } + pub fn quadkey(&self) -> String { xyz2quadkey(self.x, self.y, self.z) } diff --git a/python/utiles/libutiles.pyi b/python/utiles/libutiles.pyi index 0c4dc0eb..14b500ce 100644 --- a/python/utiles/libutiles.pyi +++ b/python/utiles/libutiles.pyi @@ -207,12 +207,12 @@ def tiles_list( truncate: bool = ..., ) -> list[Tile]: ... def tiles_count( - west: float, - south: float, - east: float, - north: float, - zooms: list[int] | tuple[int, ...] | int, - truncate: bool = ..., + west: float, + south: float, + east: float, + north: float, + zooms: list[int] | tuple[int, ...] | int, + truncate: bool = ..., ) -> int: ... def tiletype(buf: bytes) -> int: ... def tiletype2headers(tiletype_int: int) -> list[tuple[str, str]]: ... diff --git a/tests/test_mbtiles.py b/tests/test_mbtiles.py index 3163ed59..0a9cc99d 100644 --- a/tests/test_mbtiles.py +++ b/tests/test_mbtiles.py @@ -1,3 +1,2 @@ - def test_todo(): assert True From 4b38c56a17c8350f25618f5585388f895ec5e56a Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Wed, 8 Nov 2023 07:35:13 -0800 Subject: [PATCH 27/80] that much closer! --- Cargo.lock | 8 + crates/utiles-cli/src/cli.rs | 84 +++- crates/utiles-cli/src/stdinterator.rs | 1 + crates/utiles/Cargo.lock | 8 + crates/utiles/Cargo.toml | 2 + crates/utiles/src/geojson/mod.rs | 1 - crates/utiles/src/lib.rs | 56 +-- crates/utiles/src/libtiletype.rs | 25 +- .../utiles/src/mbtiles/metadata2tilejson.rs | 2 + crates/utiles/src/parsing.rs | 9 + python/utiles/__init__.py | 8 + python/utiles/libutiles.pyi | 2 + src/cli.rs | 2 +- src/lib.rs | 14 +- tests/test_rust_cli.py | 442 ++++++++++++++++++ tests/test_tiletype.py | 5 + 16 files changed, 602 insertions(+), 67 deletions(-) create mode 100644 tests/test_rust_cli.py diff --git a/Cargo.lock b/Cargo.lock index c41def2e..e2514590 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,6 +92,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + [[package]] name = "approx" version = "0.5.1" @@ -966,11 +972,13 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" name = "utiles" version = "0.0.1" dependencies = [ + "anyhow", "fast_hilbert", "geo-types", "geojson", "serde", "serde_json", + "thiserror", "tilejson", "tracing", ] diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index 15c4e55d..e392e9e7 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -1,17 +1,15 @@ -use std::error::Error; use clap::{Parser, Subcommand, ValueEnum}; use tracing::debug; - -use tracing_subscriber::util::SubscriberInitExt; use tracing_subscriber::EnvFilter; +use tracing_subscriber::util::SubscriberInitExt; use utiles::parsing::parse_bbox; +use utiles::{bounding_tile, Tile}; use utiles::tilejson::tilejson_stringify; use utiles::tiles; -use utiles::Tile; use utiles::zoom::ZoomOrZooms; -use crate::stdinterator::StdInterator; use utilesqlite::mbtiles::Mbtiles; +use crate::stdinterator::StdInterator; /// A fictional versioning CLI #[derive(Debug, Parser)] // requires `derive` feature @@ -52,7 +50,6 @@ pub enum Commands { // #[arg(required = true)] // quadkey: String, // }, - #[command(name = "lint", about = "lint mbtiles file", long_about = None)] Lint { #[arg(required = true)] @@ -79,24 +76,21 @@ pub enum Commands { seq: bool, }, - // =================== // NOT IMPLEMENTED YET // =================== #[command(name = "quadkey", visible_alias = "qk", about = "convert xyz <-> quadkey", long_about = None)] - Quadkey{ + Quadkey { #[arg(required = false)] input: Option, }, #[command(name = "bounding-tile", about = "output tilejson", long_about = None)] BoundingTile { - #[arg(required = true)] - zoom: u8, - - #[arg(required = true)] - input: String, + #[arg(required = false)] + input: Option, + #[arg(required = false, long, action = clap::ArgAction::SetTrue)] seq: bool, }, @@ -111,7 +105,7 @@ pub enum Commands { seq: bool, }, - #[command(name="neighbors", about="print neighbors of tile(s)", long_about=None)] + #[command(name = "neighbors", about = "print neighbors of tile(s)", long_about = None)] Neighbors { #[arg(required = true)] input: String, @@ -119,7 +113,7 @@ pub enum Commands { seq: bool, }, - #[command(name="parent", about="print parent of tile(s)", long_about=None)] + #[command(name = "parent", about = "print parent of tile(s)", long_about = None)] Parent { #[arg(required = true)] input: String, @@ -127,7 +121,7 @@ pub enum Commands { seq: bool, }, - #[command(name="shapes", about="print shapes of tiles as geojson", long_about=None)] + #[command(name = "shapes", about = "print shapes of tiles as geojson", long_about = None)] Shapes { #[arg(required = true)] input: String, @@ -237,7 +231,7 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { match args.command { - Commands::Quadkey{ + Commands::Quadkey { input } => { // let thingy = StdInterator::new(quadkey.quadkey).unwrap(); @@ -278,13 +272,56 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { } } } + Commands::BoundingTile{ input, seq } => { + let input_lines = StdInterator::new(input).unwrap(); + let lines = input_lines + .filter(|l| !l.is_err()) + .filter(|l| !l.as_ref().unwrap().is_empty()).filter( + |l| l.as_ref().unwrap() != "\x1e" + ); + + let bboxes = lines.map(|l| { + let s = l.unwrap(); + debug!("l: {:?}", s); + parse_bbox(&s).unwrap() + }); + for bbox in bboxes { + let tile = bounding_tile( bbox, None); + // let tile = Tile::from_bbox(&bbox, zoom); + let rs = if seq { "\x1e\n" } else { "" }; + println!("{}{}", rs, tile.json_arr()); + } + // + // + // .flat_map(|b| tiles( + // (b.west, b.south, b.east, b.north), + // ZoomOrZooms::Zoom(zoom), + // )).enumerate(); + // // let bboxes = lines + // for (i, tile) in tiles { + // let rs = if seq { "\x1e\n" } else { "" }; + // println!("{}{}", rs, tile.json_arr()); + // // call loop_fn if it's defined every 1000 iterations for signal break + // if i % 1024 == 0 { + // if let Some(f) = loop_fn { + // f(); + // } + // } + // } + } Commands::Tiles { zoom, input, seq } => { let input_lines = StdInterator::new(input).unwrap(); - let mut niter = 0; - let tiles = input_lines + let lines = input_lines .filter(|l| !l.is_err()) - .filter(|l| !l.as_ref().unwrap().is_empty()) - .map(|l| parse_bbox(&l.unwrap()).unwrap()) + .filter(|l| !l.as_ref().unwrap().is_empty()).filter( + |l| l.as_ref().unwrap() != "\x1e" + ); + + let tiles = lines.map(|l| { + let s = l.unwrap(); + debug!("l: {:?}", s); + parse_bbox(&s).unwrap() + }) .flat_map(|b| tiles( (b.west, b.south, b.east, b.north), ZoomOrZooms::Zoom(zoom), @@ -350,8 +387,9 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { } Commands::Lint { filepath, fix } => { - println!("linting: {}", filepath); - println!("NOT IMPLEMENTED YET"); + println!("lint (fix -- {}): {}", fix, filepath); + // throw not implemented error + panic!("not implemented (yet)") } Commands::Tilejson { filepath } => { diff --git a/crates/utiles-cli/src/stdinterator.rs b/crates/utiles-cli/src/stdinterator.rs index d18ff39a..ff444398 100644 --- a/crates/utiles-cli/src/stdinterator.rs +++ b/crates/utiles-cli/src/stdinterator.rs @@ -90,6 +90,7 @@ impl StdInterator { .lines() // This assumes that each line is separated by '\n' .map(|s| s.to_string()) .collect::>(); + debug!("args: {:?}", args); StdInteratorSource::Args(args) } } diff --git a/crates/utiles/Cargo.lock b/crates/utiles/Cargo.lock index 63b5e953..c7f1ec3b 100644 --- a/crates/utiles/Cargo.lock +++ b/crates/utiles/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + [[package]] name = "approx" version = "0.5.1" @@ -260,11 +266,13 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" name = "utiles" version = "0.0.1" dependencies = [ + "anyhow", "fast_hilbert", "geo-types", "geojson", "serde", "serde_json", + "thiserror", "tilejson", "tracing", ] diff --git a/crates/utiles/Cargo.toml b/crates/utiles/Cargo.toml index 07ea967d..75c319a1 100644 --- a/crates/utiles/Cargo.toml +++ b/crates/utiles/Cargo.toml @@ -14,10 +14,12 @@ repository = "https://github.com/jessekrubin/utiles" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0.75" fast_hilbert = "2.0.0" geo-types = "0.7.9" geojson = "0.24.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.96" +thiserror = "1.0.50" tilejson = "0.3.2" tracing = { version = "0.1.40", features = [] } diff --git a/crates/utiles/src/geojson/mod.rs b/crates/utiles/src/geojson/mod.rs index 0cb329d9..012d5f59 100644 --- a/crates/utiles/src/geojson/mod.rs +++ b/crates/utiles/src/geojson/mod.rs @@ -18,7 +18,6 @@ pub fn geojson_geometry_points(g: Geometry) -> Box> GeoJsonValue::GeometryCollection(geometries) => { Box::new(geometries.into_iter().flat_map(geojson_geometry_points)) } - _ => Box::new(std::iter::empty()), // For any other case, return an empty iterator } } diff --git a/crates/utiles/src/lib.rs b/crates/utiles/src/lib.rs index 86eb1e0f..93f02973 100644 --- a/crates/utiles/src/lib.rs +++ b/crates/utiles/src/lib.rs @@ -193,34 +193,34 @@ pub fn _xy( } } -pub fn _xy_og(lng: f64, lat: f64, truncate: Option) -> (f64, f64) { - let trunc = truncate.unwrap_or(false); - - let mut lng = lng; - let mut lat = lat; - if trunc { - lng = truncate_lng(lng); - lat = truncate_lat(lat); - } - let x = (lng / 360.0) + 0.5; - - let sinlat = lat.to_radians().sin(); - - let y_inner = (1.0 + sinlat) / (1.0 - sinlat); - let y = match (1.0 + sinlat) / (1.0 - sinlat) { - y if y.is_infinite() => { - panic!("Invalid latitude (inf): {lat:?}"); - } - y if y.is_nan() => { - panic!("Invalid latitude (nan): {lat:?}"); - } - // y => 0.5 - 0.25 * y.ln() / std::f64::consts::PI, - // y = 0.5 - 0.25 * math.log((1.0 + sinlat) / (1.0 - sinlat)) / math.pi - _y => 0.5 - 0.25 * y_inner.ln() / PI, - }; - (x, y) - // y = 0.5 - 0.25 * math.log((1.0 + sinlat) / (1.0 - sinlat)) / math.pi -} +// pub fn _xy_og(lng: f64, lat: f64, truncate: Option) -> (f64, f64) { +// let trunc = truncate.unwrap_or(false); +// +// let mut lng = lng; +// let mut lat = lat; +// if trunc { +// lng = truncate_lng(lng); +// lat = truncate_lat(lat); +// } +// let x = (lng / 360.0) + 0.5; +// +// let sinlat = lat.to_radians().sin(); +// +// let y_inner = (1.0 + sinlat) / (1.0 - sinlat); +// let y = match (1.0 + sinlat) / (1.0 - sinlat) { +// y if y.is_infinite() => { +// panic!("Invalid latitude (inf): {lat:?}"); +// } +// y if y.is_nan() => { +// panic!("Invalid latitude (nan): {lat:?}"); +// } +// // y => 0.5 - 0.25 * y.ln() / std::f64::consts::PI, +// // y = 0.5 - 0.25 * math.log((1.0 + sinlat) / (1.0 - sinlat)) / math.pi +// _y => 0.5 - 0.25 * y_inner.ln() / PI, +// }; +// (x, y) +// // y = 0.5 - 0.25 * math.log((1.0 + sinlat) / (1.0 - sinlat)) / math.pi +// } pub fn xy(lng: f64, lat: f64, truncate: Option) -> (f64, f64) { let trunc = truncate.unwrap_or(false); diff --git a/crates/utiles/src/libtiletype.rs b/crates/utiles/src/libtiletype.rs index 33421086..ae448e10 100644 --- a/crates/utiles/src/libtiletype.rs +++ b/crates/utiles/src/libtiletype.rs @@ -2,19 +2,21 @@ pub enum TileType { Unknown = 0, Gif = 1, Jpg = 2, - Pbf = 3, - Pbfgz = 4, - Png = 5, - Webp = 6, + Json = 3, + Pbf = 4, + Pbfgz = 5, + Png = 6, + Webp = 7, } pub const TILETYPE_UNKNOWN: usize = 0; pub const TILETYPE_GIF: usize = 1; pub const TILETYPE_JPG: usize = 2; -pub const TILETYPE_PBF: usize = 3; -pub const TILETYPE_PBFGZ: usize = 4; -pub const TILETYPE_PNG: usize = 5; -pub const TILETYPE_WEBP: usize = 6; +pub const TILETYPE_JSON: usize = 3; +pub const TILETYPE_PBF: usize = 4; +pub const TILETYPE_PBFGZ: usize = 5; +pub const TILETYPE_PNG: usize = 6; +pub const TILETYPE_WEBP: usize = 7; pub fn tiletype(buffer: &[u8]) -> TileType { if buffer.len() >= 8 { @@ -56,6 +58,9 @@ pub fn tiletype(buffer: &[u8]) -> TileType { return TileType::Pbf; } else if buffer[0] == 0x1f && buffer[1] == 0x8b { return TileType::Pbfgz; + // if starts with '{' or '[' json + } else if buffer[0] == 0x7b || buffer[0] == 0x5b { + return TileType::Json; } } TileType::Unknown @@ -66,6 +71,7 @@ pub fn enum2const(tiletype: TileType) -> usize { TileType::Unknown => TILETYPE_UNKNOWN, TileType::Gif => TILETYPE_GIF, TileType::Jpg => TILETYPE_JPG, + TileType::Json => TILETYPE_JSON, TileType::Pbf => TILETYPE_PBF, TileType::Pbfgz => TILETYPE_PBFGZ, TileType::Png => TILETYPE_PNG, @@ -78,6 +84,7 @@ pub fn const2enum(tiletype: usize) -> TileType { TILETYPE_UNKNOWN => TileType::Unknown, TILETYPE_GIF => TileType::Gif, TILETYPE_JPG => TileType::Jpg, + TILETYPE_JSON => TileType::Json, TILETYPE_PBF => TileType::Pbf, TILETYPE_PBFGZ => TileType::Pbfgz, TILETYPE_PNG => TileType::Png, @@ -90,6 +97,7 @@ pub fn headers(tiletype: TileType) -> Vec<(&'static str, &'static str)> { match tiletype { TileType::Png => vec![("Content-Type", "image/png")], TileType::Jpg => vec![("Content-Type", "image/jpeg")], + TileType::Json => vec![("Content-Type", "application/json")], TileType::Gif => vec![("Content-Type", "image/gif")], TileType::Webp => vec![("Content-Type", "image/webp")], TileType::Pbf => vec![ @@ -110,6 +118,7 @@ pub fn tiletype_str(buffer: &[u8]) -> String { TileType::Unknown => "unknown".to_string(), TileType::Gif => "gif".to_string(), TileType::Jpg => "jpg".to_string(), + TileType::Json => "json".to_string(), TileType::Pbf => "pbf".to_string(), TileType::Pbfgz => "pbfgz".to_string(), TileType::Png => "png".to_string(), diff --git a/crates/utiles/src/mbtiles/metadata2tilejson.rs b/crates/utiles/src/mbtiles/metadata2tilejson.rs index 30574b21..3d220ee6 100644 --- a/crates/utiles/src/mbtiles/metadata2tilejson.rs +++ b/crates/utiles/src/mbtiles/metadata2tilejson.rs @@ -19,6 +19,8 @@ fn to_val(val: Result, title: &str) -> Option { } } +/// Convert metadata rows to a TileJSON object +/// (ripped from martin-mbtiles thank y'all very much) pub fn metadata2tilejson( metadata: Vec, ) -> Result> { diff --git a/crates/utiles/src/parsing.rs b/crates/utiles/src/parsing.rs index 50e1b728..2d92cd53 100644 --- a/crates/utiles/src/parsing.rs +++ b/crates/utiles/src/parsing.rs @@ -1,12 +1,21 @@ use crate::bbox::BBox; use crate::geojson::geojson_coords; use geo_types::Coord; +use tracing::debug; use serde_json::Value; // pub fn parse_bbox(s: &str) -> serde_json::Result { pub fn parse_bbox(s: &str) -> serde_json::Result { // if the first char is "{" assume it is geojson-like + debug!("parse_bbox: {}", s); if s.starts_with('{') { + // parse to serde_json::Value + let v: Value = serde_json::from_str(s)?; + // if it has a "bbox" key, use that + if v["bbox"].is_array() { + let bbox: (f64, f64, f64, f64) = serde_json::from_value(v["bbox"].clone())?; + return Ok(BBox::from(bbox)); + } return Ok(geojson_bounds(s)); } diff --git a/python/utiles/__init__.py b/python/utiles/__init__.py index d28c0aae..a6015d13 100644 --- a/python/utiles/__init__.py +++ b/python/utiles/__init__.py @@ -13,6 +13,8 @@ TILETYPE_PBF, TILETYPE_PNG, TILETYPE_UNKNOWN, + TILETYPE_JSON, + TILETYPE_PBFGZ, TILETYPE_WEBP, Tile, __build_profile__, @@ -64,7 +66,9 @@ "LngLatBbox", "TILETYPE_GIF", "TILETYPE_JPG", + "TILETYPE_JSON", "TILETYPE_PBF", + "TILETYPE_PBFGZ", "TILETYPE_PNG", "TILETYPE_UNKNOWN", "TILETYPE_WEBP", @@ -151,5 +155,9 @@ def tiletile_str(n: int) -> str: return "webp" elif n == TILETYPE_PBF: return "pbf" + elif n == TILETYPE_PBFGZ: + return "pbfgz" + elif n == TILETYPE_JSON: + return "json" else: return "unknown" diff --git a/python/utiles/libutiles.pyi b/python/utiles/libutiles.pyi index 14b500ce..e53a2416 100644 --- a/python/utiles/libutiles.pyi +++ b/python/utiles/libutiles.pyi @@ -22,7 +22,9 @@ TupleIntIntInt = Tuple[int, int, int] TILETYPE_GIF: int TILETYPE_JPG: int +TILETYPE_JSON: int TILETYPE_PBF: int +TILETYPE_PBFGZ: int TILETYPE_PNG: int TILETYPE_UNKNOWN: int TILETYPE_WEBP: int diff --git a/src/cli.rs b/src/cli.rs index 202e34ee..a7e1520d 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,4 +1,4 @@ -use pyo3::{pyfunction, PyResult, Python}; +use pyo3::{pyfunction, Python}; use utiles_cli::cli::cli_main; #[pyfunction] diff --git a/src/lib.rs b/src/lib.rs index cfd32189..32f44ed2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -809,12 +809,14 @@ fn libutiles(_py: Python<'_>, m: &PyModule) -> PyResult<()> { m.add_function(wrap_pyfunction!(tiletype, m)?)?; m.add_function(wrap_pyfunction!(tiletype_str, m)?)?; m.add_function(wrap_pyfunction!(tiletype2headers, m)?)?; - m.add("TILETYPE_UNKNOWN", libtiletype::TILETYPE_UNKNOWN)?; // 0 - m.add("TILETYPE_GIF", libtiletype::TILETYPE_GIF)?; // 1 - m.add("TILETYPE_JPG", libtiletype::TILETYPE_JPG)?; // 2 - m.add("TILETYPE_PBF", libtiletype::TILETYPE_PBF)?; // 3 - m.add("TILETYPE_PNG", libtiletype::TILETYPE_PNG)?; // 4 - m.add("TILETYPE_WEBP", libtiletype::TILETYPE_WEBP)?; // 5 + m.add("TILETYPE_UNKNOWN", libtiletype::TILETYPE_UNKNOWN)?; + m.add("TILETYPE_GIF", libtiletype::TILETYPE_GIF)?; + m.add("TILETYPE_JPG", libtiletype::TILETYPE_JPG)?; + m.add("TILETYPE_JSON", libtiletype::TILETYPE_JSON)?; + m.add("TILETYPE_PBF", libtiletype::TILETYPE_PBF)?; + m.add("TILETYPE_PBFGZ", libtiletype::TILETYPE_PBFGZ)?; + m.add("TILETYPE_PNG", libtiletype::TILETYPE_PNG)?; + m.add("TILETYPE_WEBP", libtiletype::TILETYPE_WEBP)?; // m.add_class::()?; m.add_class::()?; diff --git a/tests/test_rust_cli.py b/tests/test_rust_cli.py new file mode 100644 index 00000000..b01c1c40 --- /dev/null +++ b/tests/test_rust_cli.py @@ -0,0 +1,442 @@ +"""Utiles rust cli tests""" +from subprocess import run +from json import dumps as stringify +import json + +import pytest +from click.testing import CliRunner + +from utiles.ut import cli + + +# def test_cli_shapes_failure() -> None: +# runner = CliRunner() +# result = runner.invoke(cli, ["shapes"], "0") +# assert result.exit_code == 2 + + +# def test_cli_shapes() -> None: +# runner = CliRunner() +# result = runner.invoke(cli, ["shapes", "--precision", "6"], "[106, 193, 9]") +# assert result.exit_code == 0 +# assert ( +# result.output +# == '{"bbox": [-105.46875, 39.909736, -104.765625, 40.446947], "geometry": {"coordinates": [[[-105.46875, 39.909736], [-105.46875, 40.446947], [-104.765625, 40.446947], [-104.765625, 39.909736], [-105.46875, 39.909736]]], "type": "Polygon"}, "id": "(106, 193, 9)", "properties": {"title": "XYZ tile (106, 193, 9)"}, "type": "Feature"}\n' +# ) + +# def test_cli_shapes_arg() -> None: +# runner = CliRunner() +# result = runner.invoke(cli, ["shapes", "[106, 193, 9]", "--precision", "6"]) +# assert result.exit_code == 0 +# result_output_json = json.loads(result.output) + +# # '{"bbox": [-105.46875, 39.909736, -104.765625, 40.446947], "geometry": {"coordinates": [[[-105.46875, 39.909736], [-105.46875, 40.446947], [-104.765625, 40.446947], [-104.765625, 39.909736], [-105.46875, 39.909736]]], "type": "Polygon"}, "id": "(106, 193, 9)", "properties": {"title": "XYZ tile (106, 193, 9)"}, "type": "Feature"}\n' +# expected_dict = { +# "bbox": [-105.46875, 39.909736, -104.765625, 40.446947], +# "geometry": { +# "coordinates": [ +# [ +# [-105.46875, 39.909736], +# [-105.46875, 40.446947], +# [-104.765625, 40.446947], +# [-104.765625, 39.909736], +# [-105.46875, 39.909736], +# ] +# ], +# "type": "Polygon", +# }, +# "id": "(106, 193, 9)", +# "properties": {"title": "XYZ tile (106, 193, 9)"}, +# "type": "Feature", +# } + +# assert result_output_json == expected_dict + + +# def test_cli_shapes_buffer() -> None: +# runner = CliRunner() +# result = runner.invoke( +# cli, ["shapes", "[106, 193, 9]", "--buffer", "1.0", "--precision", "6"] +# ) +# assert result.exit_code == 0 +# assert ( +# result.output +# == '{"bbox": [-106.46875, 38.909736, -103.765625, 41.446947], "geometry": {"coordinates": [[[-106.46875, 38.909736], [-106.46875, 41.446947], [-103.765625, 41.446947], [-103.765625, 38.909736], [-106.46875, 38.909736]]], "type": "Polygon"}, "id": "(106, 193, 9)", "properties": {"title": "XYZ tile (106, 193, 9)"}, "type": "Feature"}\n' +# ) + + +# def test_cli_shapes_compact() -> None: +# """Output is compact.""" +# runner = CliRunner() +# result = runner.invoke(cli, ["shapes", "--compact"], "[106, 193, 9]") +# assert result.exit_code == 0 +# assert '"type":"Feature"' in result.output.strip() + + +# def test_cli_shapes_indentation() -> None: +# """Output is indented.""" +# runner = CliRunner() +# result = runner.invoke(cli, ["shapes", "--indent", "8"], "[106, 193, 9]") +# assert result.exit_code == 0 +# assert ' "type": "Feature"' in result.output.strip() + + +# def test_cli_shapes_collect() -> None: +# """Shapes are collected into a feature collection.""" +# runner = CliRunner() +# result = runner.invoke(cli, ["shapes", "--collect", "--feature"], "[106, 193, 9]") +# assert result.exit_code == 0 +# assert "FeatureCollection" in result.output + + +# def test_cli_shapes_extents() -> None: +# runner = CliRunner() +# result = runner.invoke( +# cli, ["shapes", "[106, 193, 9]", "--extents", "--mercator", "--precision", "3"] +# ) +# assert result.exit_code == 0 +# assert result.output == "-11740727.545 4852834.052 -11662456.028 4931105.569\n" + + +# def test_cli_shapes_bbox() -> None: +# """JSON text sequences of bboxes are output.""" +# runner = CliRunner() +# result = runner.invoke( +# cli, +# [ +# "shapes", +# "[106, 193, 9]", +# "--seq", +# "--bbox", +# "--mercator", +# "--precision", +# "3", +# ], +# ) +# assert result.exit_code == 0 +# assert ( +# result.output +# == "\x1e\n[-11740727.545, 4852834.052, -11662456.028, 4931105.569]\n" +# ) + + +# def test_cli_shapes_props_fid() -> None: +# runner = CliRunner() +# result = runner.invoke( +# cli, +# [ +# "shapes", +# '{"tile": [106, 193, 9], "properties": {"title": "foo"}, "id": "42"}', +# ], +# ) +# assert result.exit_code == 0 +# assert '"title": "foo"' in result.output +# assert '"id": "42"' in result.output + + +# def test_cli_strict_overlap_contain() -> None: +# runner = CliRunner() +# result1 = runner.invoke(cli, ["shapes"], "[2331,1185,12]") +# assert result1.exit_code == 0 +# result2 = runner.invoke(cli, ["tiles", "12"], result1.output) +# assert result2.exit_code == 0 +# assert result2.output == "[2331, 1185, 12]\n" + + + +# def test_cli_parent_failure() -> None: +# """[0, 0, 0] has no parent""" +# runner = CliRunner() +# result = runner.invoke(cli, ["parent"], "[0, 0, 0]") +# assert result.exit_code == 2 + + +# def test_cli_parent() -> None: +# runner = CliRunner() +# result = runner.invoke(cli, ["parent"], "[486, 332, 10]\n[486, 332, 10]") +# assert result.exit_code == 0 +# assert result.output == "[243, 166, 9]\n[243, 166, 9]\n" + + +# def test_cli_parent_depth() -> None: +# runner = CliRunner() +# result = runner.invoke(cli, ["parent", "--depth", "2"], "[486, 332, 10]") +# assert result.exit_code == 0 +# assert result.output == "[121, 83, 8]\n" + + +# def test_cli_parent_multidepth() -> None: +# runner = CliRunner() +# result = runner.invoke( +# cli, ["parent", "--depth", "2"], "[486, 332, 10]\n[121, 83, 8]" +# ) +# assert result.exit_code == 0 +# assert result.output == "[121, 83, 8]\n[30, 20, 6]\n" + + +# def test_cli_children() -> None: +# runner = CliRunner() +# result = runner.invoke(cli, ["children"], "[243, 166, 9]") +# assert result.exit_code == 0 +# assert ( +# result.output +# == "[486, 332, 10]\n[487, 332, 10]\n[487, 333, 10]\n[486, 333, 10]\n" +# ) + + +# def test_cli_neighbors() -> None: +# runner = CliRunner() +# result = runner.invoke(cli, ["neighbors"], "[243, 166, 9]") +# assert result.exit_code == 0 + +# tiles_lines = result.output.strip().split("\n") +# tiles = [tuple(json.loads(t)) for t in tiles_lines] +# assert len(tiles) == 8 + +# # We do not provide ordering guarantees +# # tiles = set([tuple(t) for t in tiles]) +# tiles_set = set(tiles) +# assert (243, 166, 9) not in tiles_set, "input not in neighbors" + +# assert (243 - 1, 166 - 1, 9) in tiles_set +# assert (243 - 1, 166 + 0, 9) in tiles_set +# assert (243 - 1, 166 + 1, 9) in tiles_set +# assert (243 + 0, 166 - 1, 9) in tiles_set +# assert (243 + 0, 166 + 1, 9) in tiles_set +# assert (243 + 1, 166 - 1, 9) in tiles_set +# assert (243 + 1, 166 + 0, 9) in tiles_set +# assert (243 + 1, 166 + 1, 9) in tiles_set + + +def _run_cli( + args: list[str] | None, + input: str | None = None, +): + res = run( + ["python", "-m", "utiles.ut", *args], + input=input, + capture_output=True, + text=True, + shell=True, + ) + return res + + +def test_rust_cli_help() -> None: + res = _run_cli(["--help"]) + assert "(rust)" in res.stdout + + +# +# def test_rust_cli_help() -> None: +# runner = CliRunner() +# res = runner.invoke(cli, ["--help"]) +# print(res) +# assert False + + +class TestTiles: + def test_cli_tiles_bad_bounds(self) -> None: + """Bounds of len 3 are bad.""" + result = _run_cli(["tiles", "14"], "[-105, 39.99, -104.99]") + assert result.returncode != 0 + + def test_cli_tiles_no_bounds(self) -> None: + result = _run_cli(["tiles", "14"], "[-105, 39.99, -104.99, 40]") + assert result.returncode == 0 + assert result.stdout == "[3413, 6202, 14]\n[3413, 6203, 14]\n" + + # + def test_cli_tiles_multi_bounds(self) -> None: + """A LF-delimited sequence can be used as input.""" + result = _run_cli( + ["tiles", "14"], "[-105, 39.99, -104.99, 40]\n[-105, 39.99, -104.99, 40]" + ) + assert result.returncode == 0 + assert len(result.stdout.strip().split("\n")) == 4 + + def test_cli_tiles_multi_bounds_seq(self) -> None: + """A JSON text sequence can be used as input.""" + result = _run_cli( + ["--debug", "tiles", "14"], + "\x1e\n[-105, 39.99, -104.99, 40]\n\x1e\n[-105, 39.99, -104.99, 40]", + ) + print(result.stdout) + print(result.stderr) + assert result.returncode == 0 + assert len(result.stdout.strip().split("\n")) == 4 + + def test_rust_cli_tiles_seq(self) -> None: + result = _run_cli(["tiles", "14", "--seq", "[14.0859, 5.798]"]) + print(result) + # runner = CliRunner() + # result = runner.invoke(cli, ["tiles", "14", "--seq", + # "[14.0859, 5.798]" + # ],) + # print(result) + assert result.returncode == 0 + assert result.stdout == "\x1e\n[8833, 7927, 14]\n" + + def test_cli_tiles_points(self) -> None: + result = _run_cli(["tiles", "14"], "[14.0859, 5.798]") + print(result) + # j + # runner = CliRunner() + # result = runner.invoke(cli, ["tiles", "14"], "[14.0859, 5.798]") + assert result.returncode == 0 + assert result.stdout == "[8833, 7927, 14]\n" + + def test_cli_tiles_point_geojson(self) -> None: + result = _run_cli( + ["tiles", "14"], '{"type":"Point","coordinates":[14.0859, 5.798]}' + ) + assert result.returncode == 0 + assert result.stdout == "[8833, 7927, 14]\n" + + def test_cli_tiles_implicit_stdin(self) -> None: + # result = _run_cli(["tiles", "14"], "[14.0859, 5.798]") + # assert result.returncode == 0 + # assert result.stdout == "[8833, 7927, 14]\n" + # runner = CliRunner() + result = _run_cli(["tiles", "14"], "[-105, 39.99, -104.99, 40]") + assert result.returncode == 0 + assert result.stdout == "[3413, 6202, 14]\n[3413, 6203, 14]\n" + + def test_cli_tiles_arg(self) -> None: + result = _run_cli(["tiles", "14", "[-105, 39.99, -104.99, 40]"]) + assert result.returncode == 0 + assert result.stdout == "[3413, 6202, 14]\n[3413, 6203, 14]\n" + + def test_cli_tiles_geosjon(self) -> None: + collection = stringify( + { + "features": [ + { + "geometry": { + "coordinates": [ + [ + [-105.46875, 39.909736], + [-105.46875, 40.446947], + [-104.765625, 40.446947], + [-104.765625, 39.909736], + [-105.46875, 39.909736], + ] + ], + "type": "Polygon", + }, + "id": "(106, 193, 9)", + "properties": {"title": "XYZ tile (106, 193, 9)"}, + "type": "Feature", + } + ], + "type": "FeatureCollection", + } + ) + result = _run_cli(["tiles", "9"], collection) + assert result.returncode == 0 + assert result.stdout == "[106, 193, 9]\n[106, 194, 9]\n" + + +class TestQuadkey: + def test_cli_quadkey_from_tiles(self) -> None: + result = _run_cli(["quadkey"], "[486, 332, 10]\n[6826, 12415, 15]") + assert result.returncode == 0 + assert result.stdout == "0313102310\n023101012323232\n" + + def test_cli_quadkey_from_quadkeys(self) -> None: + result = _run_cli(["quadkey"], "0313102310\n023101012323232") + assert result.returncode == 0 + assert result.stdout == "[486, 332, 10]\n[6826, 12415, 15]\n" + + def test_cli_quadkey_from_mixed(self) -> None: + result = _run_cli(["quadkey"], "0313102310\n[6826, 12415, 15]") + assert result.returncode == 0 + assert result.stdout == "[486, 332, 10]\n023101012323232\n" + + @pytest.mark.skip(reason="not implemented") + def test_cli_quadkey_failure(self) -> None: + """Abort when an invalid quadkey is passed""" + runner = CliRunner() + with pytest.warns(DeprecationWarning): + result = runner.invoke(cli, ["quadkey", "lolwut"]) + assert result.exit_code == 2 + assert "lolwut" in result.output + +class TestBoundingTile: + + def test_cli_bounding_tile_bad_bounds(self) -> None: + """Bounds of len 3 are bad.""" + runner = CliRunner() + result = _run_cli( ["bounding-tile"], "[-105, 39.99, -104.99]") + assert result.returncode != 0 + + + def test_cli_bounding_tile(self) -> None: + result = _run_cli( ["bounding-tile"], "[-105, 39.99, -104.99, 40]") + assert result.returncode == 0 + assert result.stdout == "[1706, 3101, 13]\n" + + + def test_cli_bounding_tile_bbox(self) -> None: + result = _run_cli( + ["bounding-tile"], '{"bbox": [-105, 39.99, -104.99, 40]}' + ) + assert result.returncode == 0 + assert result.stdout == "[1706, 3101, 13]\n" + + + def test_cli_bounding_tile2(self) -> None: + result = _run_cli( ["bounding-tile"], "[-105, 39.99]") + assert result.returncode == 0 + + + def test_cli_multi_bounding_tile(self) -> None: + """A JSON text sequence can be used as input.""" + result = _run_cli( + ["bounding-tile"], "[-105, 39.99, -104.99, 40]\n[-105, 39.99, -104.99, 40]" + ) + assert result.returncode == 0 + assert len(result.stdout.strip().split("\n")) == 2 + + + def test_cli_multi_bounding_tile_seq(self) -> None: + """A JSON text sequence can be used as input.""" + result = _run_cli( + ["bounding-tile"], + "\x1e\n[-105, 39.99, -104.99, 40]\n\x1e\n[-105, 39.99, -104.99, 40]", + ) + assert result.returncode == 0 + assert len(result.stdout.strip().split("\n")) == 2 + + + @pytest.mark.skip(reason="I dont think this is correct") + def test_cli_tiles_bounding_tiles_z0(self) -> None: + result = _run_cli( ["bounding-tile"], "[-1, -1, 1, 1]") + assert result.returncode == 0 + assert result.stdout == "[0, 0, 0]\n" + + + @pytest.mark.skip(reason="I dont think this is correct either") + def test_cli_tiles_bounding_tiles_seq(self) -> None: + result = _run_cli( ["bounding-tile", "--seq"], "[-1, -1, 1, 1]") + assert result.returncode == 0 + assert result.stdout == "\x1e\n[0, 0, 0]\n" + + + def test_cli_bounding_tile_geosjon(self) -> None: + collection_dict = {'features': [{'geometry': {'coordinates': [[[-105.46875, 39.909736], + [-105.46875, 40.446947], + [-104.765625, 40.446947], + [-104.765625, 39.909736], + [-105.46875, 39.909736]]], + 'type': 'Polygon'}, + 'id': '(106, 193, 9)', + 'properties': {'title': 'XYZ tile (106, 193, 9)'}, + 'type': 'Feature'}], + 'type': 'FeatureCollection'} + collection = json.dumps(collection_dict) + result = _run_cli( ["bounding-tile"], collection) + assert result.returncode == 0 + assert result.stdout == "[26, 48, 7]\n" + + diff --git a/tests/test_tiletype.py b/tests/test_tiletype.py index 26ac81bb..95d0217f 100644 --- a/tests/test_tiletype.py +++ b/tests/test_tiletype.py @@ -56,6 +56,9 @@ def tiletype(buffer: bytes) -> Extensions: # gzip: recklessly assumes contents are PBF. elif buffer[0] == 0x1F and buffer[1] == 0x8B: return "pbfgz" + # if buffer starts with '{' or '[' assume JSON + elif buffer[0] == 0x7B or buffer[0] == 0x5B: + return "json" return False @@ -77,6 +80,8 @@ def tiletype(buffer: bytes) -> Extensions: "tux_alpha.webp": "webp", "unknown.txt": False, "webp-550x368.webp": "webp", + "tile-arr.json": "json", + "tile-obj.json": "json", } From af4b0d42420f2abf18ed5fdb7a28aba2373f81db Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Wed, 8 Nov 2023 08:24:05 -0800 Subject: [PATCH 28/80] parsing! --- crates/utiles-cli/src/cli.rs | 14 ++++- .../utiles/src/mbtiles/metadata2tilejson.rs | 4 +- crates/utiles/src/tile.rs | 63 ++++++++++++++++++- 3 files changed, 75 insertions(+), 6 deletions(-) diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index e392e9e7..6996c39f 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -107,9 +107,10 @@ pub enum Commands { #[command(name = "neighbors", about = "print neighbors of tile(s)", long_about = None)] Neighbors { - #[arg(required = true)] + #[arg(required = false)] input: String, + #[arg(required = false, long, action = clap::ArgAction::SetTrue)] seq: bool, }, @@ -246,7 +247,6 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { // if the line bgins w '[' treat as tile // otherwise treat as quadkey let lstr = line.unwrap(); - if lstr.starts_with('[') { // treat as tile let tile = Tile::from_json_arr(&lstr); @@ -385,6 +385,16 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { // } // } } + Commands::Neighbors { + input, seq + } =>{ + let input_lines = StdInterator::new(input).unwrap(); + let lines = input_lines + .filter(|l| !l.is_err()) + .filter(|l| !l.as_ref().unwrap().is_empty()).filter( + |l| l.as_ref().unwrap() != "\x1e" + ); + } Commands::Lint { filepath, fix } => { println!("lint (fix -- {}): {}", fix, filepath); diff --git a/crates/utiles/src/mbtiles/metadata2tilejson.rs b/crates/utiles/src/mbtiles/metadata2tilejson.rs index 3d220ee6..4edeb930 100644 --- a/crates/utiles/src/mbtiles/metadata2tilejson.rs +++ b/crates/utiles/src/mbtiles/metadata2tilejson.rs @@ -25,7 +25,7 @@ pub fn metadata2tilejson( metadata: Vec, ) -> Result> { let mut tj = tilejson! {tiles : vec![]}; - let mut layer_type: Option = None; + // let mut layer_type: Option = None; let mut json: Option = None; for row in metadata { @@ -40,7 +40,7 @@ pub fn metadata2tilejson( "maxzoom" => tj.maxzoom = to_val(value.parse(), &name), "description" => tj.description = Some(value), "attribution" => tj.attribution = Some(value), - "type" => layer_type = Some(value), + // "type" => layer_type = Some(value), "legend" => tj.legend = Some(value), "template" => tj.template = Some(value), "json" => json = to_val(serde_json::from_str(&value), &name), diff --git a/crates/utiles/src/tile.rs b/crates/utiles/src/tile.rs index 4287cc82..22ea4bb1 100644 --- a/crates/utiles/src/tile.rs +++ b/crates/utiles/src/tile.rs @@ -1,5 +1,6 @@ use std::cmp::Ordering; use std::error::Error; +use std::str::FromStr; use serde::{Deserialize, Serialize}; @@ -84,6 +85,27 @@ impl Ord for Tile { } } +impl FromStr for Tile { + type Err = Box; + + fn from_str(s: &str) -> Result { + // if it starts with '{' assume json obj + if s.starts_with('{'){ // if '{' assume its an obj + return Ok(Tile::from_json_obj(s)); + } else if s.starts_with('[') { + return Ok(Tile::from_json_arr(s)); + } + // assume its a quadkey + let res = quadkey2tile(s); + match res { + Ok(tile) => Ok(tile), + Err(e) => { + panic!("Invalid quadkey: {e}"); + } + } + } +} + impl Tile { pub fn new(x: u32, y: u32, z: u8) -> Self { Tile { x, y, z } @@ -157,8 +179,18 @@ impl Tile { } } - pub fn from_json_arr(json_arr: &str) -> Self { - let res = serde_json::from_str(json_arr); + pub fn from_json_obj(json: &str) -> Self { + let res = serde_json::from_str(json); + match res { + Ok(tile) => tile, + Err(e) => { + panic!("Invalid json_arr: {e}"); + } + } + } + + pub fn from_json_arr(json: &str) -> Self { + let res = serde_json::from_str(json); match res { Ok(tile) => tile, Err(e) => { @@ -353,3 +385,30 @@ impl Tile { serde_json::to_string(self).unwrap() } } + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_json_obj() { + let json_obj = r#"{"x": 1, "y": 2, "z": 3}"#; + let tile = Tile::from_json_obj(json_obj); + assert_eq!(tile, Tile::new(1, 2, 3)); + } + + #[test] + fn parse_json_arr() { + let json_arr = r#"[1, 2, 3]"#; + let tile = Tile::from_json_arr(json_arr); + assert_eq!(tile, Tile::new(1, 2, 3)); + } + + #[test] + fn parse_quadkey() { + let quadkey = "023010203"; + let tile = quadkey.parse::(); + assert_eq!(tile.unwrap(), Tile::new(81, 197, 9)); + } +} \ No newline at end of file From f07d6c6f75287cd67ef345586c5cfa67e7998bcb Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Wed, 8 Nov 2023 09:14:37 -0800 Subject: [PATCH 29/80] json --- crates/utiles-cli/src/cli.rs | 146 +++++++++++++++++++++-------- crates/utiles/src/tile.rs | 7 ++ test-data/tile-types/tile-arr.json | 3 + test-data/tile-types/tile-obj.json | 3 + tests/test_rust_cli.py | 122 ++++++++++++------------ 5 files changed, 181 insertions(+), 100 deletions(-) create mode 100644 test-data/tile-types/tile-arr.json create mode 100644 test-data/tile-types/tile-obj.json diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index 6996c39f..67ab982d 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -43,6 +43,20 @@ pub struct QuadkeyArgs { input: Option, } +#[derive(Debug, Parser)] // requires `derive` feature +pub struct InputAndSequenceArgs { + /// The remote to clone + // #[arg(required = false, default_value = "-")] + // quadkey: MaybeStdin, + // #[arg(required = false)] + // quadkey: Option, + #[arg(required = false)] + input: Option, + + #[arg(required = false, long, action = clap::ArgAction::SetTrue)] + seq: bool, +} + #[derive(Debug, Subcommand)] pub enum Commands { /// quadkey @@ -93,22 +107,11 @@ pub enum Commands { #[arg(required = false, long, action = clap::ArgAction::SetTrue)] seq: bool, }, - - #[command(name = "children", about = "print children of tile(s)", long_about = None)] - Children { - #[arg(required = true)] - depth: u8, - - #[arg(required = true)] - input: String, - - seq: bool, - }, - #[command(name = "neighbors", about = "print neighbors of tile(s)", long_about = None)] - Neighbors { + Neighbors + { #[arg(required = false)] - input: String, + input: Option, #[arg(required = false, long, action = clap::ArgAction::SetTrue)] seq: bool, @@ -116,12 +119,26 @@ pub enum Commands { #[command(name = "parent", about = "print parent of tile(s)", long_about = None)] Parent { - #[arg(required = true)] - input: String, + #[arg(required = false)] + input: Option, + #[arg(required = false, long, action = clap::ArgAction::SetTrue)] seq: bool, + + #[arg(required = false, long, default_value = "1")] + depth: u8, }, + #[command(name = "children", about = "print children of tile(s)", long_about = None)] + Children { + #[arg(required = false)] + input: Option, + + #[arg(required = false, long, action = clap::ArgAction::SetTrue)] + seq: bool, + #[arg(required = false, long, default_value = "1")] + depth: u8, + }, #[command(name = "shapes", about = "print shapes of tiles as geojson", long_about = None)] Shapes { #[arg(required = true)] @@ -232,6 +249,31 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { match args.command { + Commands::Lint { filepath, fix } => { + println!("lint (fix -- {}): {}", fix, filepath); + // throw not implemented error + panic!("not implemented (yet)") + } + Commands::Tilejson { filepath } => { + println!("tilejson: {}", filepath); + println!("NOT IMPLEMENTED YET"); + let mbtiles = Mbtiles::from_filepath( + &filepath + ).unwrap(); + let tj = mbtiles.tilejson().unwrap(); + + let s = tilejson_stringify(&tj, None); + + println!("{}", s); + + // println!( + // "{}", + // serde_json::to_string_pretty(&tj).unwrap() + // ); + } + + + // mercantile cli like Commands::Quadkey { input } => { @@ -272,7 +314,7 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { } } } - Commands::BoundingTile{ input, seq } => { + Commands::BoundingTile { input, seq } => { let input_lines = StdInterator::new(input).unwrap(); let lines = input_lines .filter(|l| !l.is_err()) @@ -286,7 +328,7 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { parse_bbox(&s).unwrap() }); for bbox in bboxes { - let tile = bounding_tile( bbox, None); + let tile = bounding_tile(bbox, None); // let tile = Tile::from_bbox(&bbox, zoom); let rs = if seq { "\x1e\n" } else { "" }; println!("{}{}", rs, tile.json_arr()); @@ -387,37 +429,63 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { } Commands::Neighbors { input, seq - } =>{ + } => { let input_lines = StdInterator::new(input).unwrap(); let lines = input_lines .filter(|l| !l.is_err()) .filter(|l| !l.as_ref().unwrap().is_empty()).filter( |l| l.as_ref().unwrap() != "\x1e" ); + let tiles = lines.map(|l| { + Tile::from_json(&l.unwrap()) + }); + for tile in tiles { + let neighbors = tile.neighbors(); + for neighbor in neighbors { + let rs = if seq { "\x1e\n" } else { "" }; + println!("{}{}", rs, neighbor.json_arr()); + } + } } - Commands::Lint { filepath, fix } => { - println!("lint (fix -- {}): {}", fix, filepath); - // throw not implemented error - panic!("not implemented (yet)") + Commands::Children{ + input, seq, depth + } => { + let input_lines = StdInterator::new(input).unwrap(); + let lines = input_lines + .filter(|l| !l.is_err()) + .filter(|l| !l.as_ref().unwrap().is_empty()).filter( + |l| l.as_ref().unwrap() != "\x1e" + ); + let tiles = lines.map(|l| { + Tile::from_json(&l.unwrap()) + }); + for tile in tiles { + let children = tile.children(Option::from(tile.z + depth as u8)); + for child in children { + let rs = if seq { "\x1e\n" } else { "" }; + println!("{}{}", rs, child.json_arr()); + } + } } - Commands::Tilejson { filepath } => { - println!("tilejson: {}", filepath); - println!("NOT IMPLEMENTED YET"); - let mbtiles = Mbtiles::from_filepath( - &filepath - ).unwrap(); - let tj = mbtiles.tilejson().unwrap(); - - let s = tilejson_stringify(&tj, None); - - println!("{}", s); - - // println!( - // "{}", - // serde_json::to_string_pretty(&tj).unwrap() - // ); + Commands::Parent{ + input, seq, depth + } => { + let input_lines = StdInterator::new(input).unwrap(); + let lines = input_lines + .filter(|l| !l.is_err()) + .filter(|l| !l.as_ref().unwrap().is_empty()).filter( + |l| l.as_ref().unwrap() != "\x1e" + ); + let tiles = lines.map(|l| { + Tile::from_json(&l.unwrap()) + }); + for tile in tiles { + let parent = tile.parent(Option::from(depth-1)); + let rs = if seq { "\x1e\n" } else { "" }; + println!("{}{}", rs, parent.json_arr()); + } } _ => { diff --git a/crates/utiles/src/tile.rs b/crates/utiles/src/tile.rs index 22ea4bb1..55e8b851 100644 --- a/crates/utiles/src/tile.rs +++ b/crates/utiles/src/tile.rs @@ -199,6 +199,13 @@ impl Tile { } } + pub fn from_json(json: &str) -> Self { + if json.starts_with('[') { + return Self::from_json_arr(json); + } + Self::from_json_obj(json) + } + pub fn quadkey(&self) -> String { xyz2quadkey(self.x, self.y, self.z) } diff --git a/test-data/tile-types/tile-arr.json b/test-data/tile-types/tile-arr.json new file mode 100644 index 00000000..7d426359 --- /dev/null +++ b/test-data/tile-types/tile-arr.json @@ -0,0 +1,3 @@ +[ + "I am a tile-json-array" +] \ No newline at end of file diff --git a/test-data/tile-types/tile-obj.json b/test-data/tile-types/tile-obj.json new file mode 100644 index 00000000..6e03b53a --- /dev/null +++ b/test-data/tile-types/tile-obj.json @@ -0,0 +1,3 @@ +{ + "data": "I am a tile-object" +} diff --git a/tests/test_rust_cli.py b/tests/test_rust_cli.py index b01c1c40..b39a83b5 100644 --- a/tests/test_rust_cli.py +++ b/tests/test_rust_cli.py @@ -144,69 +144,8 @@ -# def test_cli_parent_failure() -> None: -# """[0, 0, 0] has no parent""" -# runner = CliRunner() -# result = runner.invoke(cli, ["parent"], "[0, 0, 0]") -# assert result.exit_code == 2 - - -# def test_cli_parent() -> None: -# runner = CliRunner() -# result = runner.invoke(cli, ["parent"], "[486, 332, 10]\n[486, 332, 10]") -# assert result.exit_code == 0 -# assert result.output == "[243, 166, 9]\n[243, 166, 9]\n" - - -# def test_cli_parent_depth() -> None: -# runner = CliRunner() -# result = runner.invoke(cli, ["parent", "--depth", "2"], "[486, 332, 10]") -# assert result.exit_code == 0 -# assert result.output == "[121, 83, 8]\n" - - -# def test_cli_parent_multidepth() -> None: -# runner = CliRunner() -# result = runner.invoke( -# cli, ["parent", "--depth", "2"], "[486, 332, 10]\n[121, 83, 8]" -# ) -# assert result.exit_code == 0 -# assert result.output == "[121, 83, 8]\n[30, 20, 6]\n" - - -# def test_cli_children() -> None: -# runner = CliRunner() -# result = runner.invoke(cli, ["children"], "[243, 166, 9]") -# assert result.exit_code == 0 -# assert ( -# result.output -# == "[486, 332, 10]\n[487, 332, 10]\n[487, 333, 10]\n[486, 333, 10]\n" -# ) -# def test_cli_neighbors() -> None: -# runner = CliRunner() -# result = runner.invoke(cli, ["neighbors"], "[243, 166, 9]") -# assert result.exit_code == 0 - -# tiles_lines = result.output.strip().split("\n") -# tiles = [tuple(json.loads(t)) for t in tiles_lines] -# assert len(tiles) == 8 - -# # We do not provide ordering guarantees -# # tiles = set([tuple(t) for t in tiles]) -# tiles_set = set(tiles) -# assert (243, 166, 9) not in tiles_set, "input not in neighbors" - -# assert (243 - 1, 166 - 1, 9) in tiles_set -# assert (243 - 1, 166 + 0, 9) in tiles_set -# assert (243 - 1, 166 + 1, 9) in tiles_set -# assert (243 + 0, 166 - 1, 9) in tiles_set -# assert (243 + 0, 166 + 1, 9) in tiles_set -# assert (243 + 1, 166 - 1, 9) in tiles_set -# assert (243 + 1, 166 + 0, 9) in tiles_set -# assert (243 + 1, 166 + 1, 9) in tiles_set - def _run_cli( args: list[str] | None, @@ -440,3 +379,64 @@ def test_cli_bounding_tile_geosjon(self) -> None: assert result.stdout == "[26, 48, 7]\n" +class TestNeighbors: + + def test_cli_neighbors(self) -> None: + result = _run_cli( ["neighbors"], "[243, 166, 9]") + assert result.returncode== 0 + print(result.stdout) + + tiles_lines = result.stdout.strip().split("\n") + tiles = [tuple(json.loads(t)) for t in tiles_lines] + assert len(tiles) == 8 + + # We do not provide ordering guarantees + # tiles = set([tuple(t) for t in tiles]) + tiles_set = set(tiles) + assert (243, 166, 9) not in tiles_set, "input not in neighbors" + + assert (243 - 1, 166 - 1, 9) in tiles_set + assert (243 - 1, 166 + 0, 9) in tiles_set + assert (243 - 1, 166 + 1, 9) in tiles_set + assert (243 + 0, 166 - 1, 9) in tiles_set + assert (243 + 0, 166 + 1, 9) in tiles_set + assert (243 + 1, 166 - 1, 9) in tiles_set + assert (243 + 1, 166 + 0, 9) in tiles_set + assert (243 + 1, 166 + 1, 9) in tiles_set + +class TestParent: + def test_cli_parent_failure(self) -> None: + """[0, 0, 0] has no parent""" + result = _run_cli( ["parent"], "[0, 0, 0]") + assert result.returncode !=0 + + + def test_cli_parent(self) -> None: + result = _run_cli( ["parent"], "[486, 332, 10]\n[486, 332, 10]") + assert result.returncode == 0 + assert result.stdout == "[243, 166, 9]\n[243, 166, 9]\n" + + + def test_cli_parent_depth(self) -> None: + result = _run_cli( ["parent", "--depth", "2"], "[486, 332, 10]") + assert result.returncode == 0 + assert result.stdout == "[121, 83, 8]\n" + + + def test_cli_parent_multidepth(self) -> None: + result = _run_cli( + ["parent", "--depth", "2"], "[486, 332, 10]\n[121, 83, 8]" + ) + assert result.returncode == 0 + assert result.stdout == "[121, 83, 8]\n[30, 20, 6]\n" + +class TestChildren: + + def test_cli_children(self) -> None: + result = _run_cli( ["children"], "[243, 166, 9]") + assert result.returncode == 0 + assert ( + result.stdout + == "[486, 332, 10]\n[487, 332, 10]\n[487, 333, 10]\n[486, 333, 10]\n" + ) + From 047ec26cc928cc6aba2c4d7dc503108de1e2b115 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Wed, 8 Nov 2023 09:17:15 -0800 Subject: [PATCH 30/80] aha --- src/pyutiles/pytile.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pyutiles/pytile.rs b/src/pyutiles/pytile.rs index f2548858..444bc1eb 100644 --- a/src/pyutiles/pytile.rs +++ b/src/pyutiles/pytile.rs @@ -477,8 +477,6 @@ impl From for Tile { #[cfg(test)] mod tests { use super::*; - use crate::utile; - #[test] fn test_pytile_macro() { let tile = pytile!(0, 0, 0); From 5a0dea0a0433e842536befd3036d4180556cf306 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Wed, 8 Nov 2023 09:30:39 -0800 Subject: [PATCH 31/80] huh --- crates/utiles-cli/Cargo.lock | 8 ++++++++ crates/utiles-cli/src/cli.rs | 13 ++++++------- crates/utiles-cli/src/stdinterator.rs | 2 +- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/crates/utiles-cli/Cargo.lock b/crates/utiles-cli/Cargo.lock index 4c04bc27..68834a2e 100644 --- a/crates/utiles-cli/Cargo.lock +++ b/crates/utiles-cli/Cargo.lock @@ -92,6 +92,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + [[package]] name = "approx" version = "0.5.1" @@ -862,11 +868,13 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" name = "utiles" version = "0.0.1" dependencies = [ + "anyhow", "fast_hilbert", "geo-types", "geojson", "serde", "serde_json", + "thiserror", "tilejson", "tracing", ] diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index 67ab982d..a33eb7f5 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -1,7 +1,6 @@ use clap::{Parser, Subcommand, ValueEnum}; use tracing::debug; use tracing_subscriber::EnvFilter; -use tracing_subscriber::util::SubscriberInitExt; use utiles::parsing::parse_bbox; use utiles::{bounding_tile, Tile}; use utiles::tilejson::tilejson_stringify; @@ -208,7 +207,7 @@ impl std::fmt::Display for ColorWhen { } } -pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { +pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { // print args let argv = match argv { Some(argv) => argv, @@ -250,12 +249,12 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { match args.command { Commands::Lint { filepath, fix } => { - println!("lint (fix -- {}): {}", fix, filepath); + println!("lint (fix -- {fix}): {filepath}"); // throw not implemented error panic!("not implemented (yet)") } Commands::Tilejson { filepath } => { - println!("tilejson: {}", filepath); + println!("tilejson: {filepath}"); println!("NOT IMPLEMENTED YET"); let mbtiles = Mbtiles::from_filepath( &filepath @@ -264,7 +263,7 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { let s = tilejson_stringify(&tj, None); - println!("{}", s); + println!("{s}"); // println!( // "{}", @@ -304,7 +303,7 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { let qk = lstr; let tile = Tile::from_quadkey(&qk); if tile.is_err() { - println!("Invalid quadkey: {}", qk); + println!("Invalid quadkey: {qk}"); } else { println!("{}", tile.unwrap().json_arr()); } @@ -461,7 +460,7 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn() -> ()>) { Tile::from_json(&l.unwrap()) }); for tile in tiles { - let children = tile.children(Option::from(tile.z + depth as u8)); + let children = tile.children(Option::from(tile.z + depth)); for child in children { let rs = if seq { "\x1e\n" } else { "" }; println!("{}{}", rs, child.json_arr()); diff --git a/crates/utiles-cli/src/stdinterator.rs b/crates/utiles-cli/src/stdinterator.rs index ff444398..fd1d7199 100644 --- a/crates/utiles-cli/src/stdinterator.rs +++ b/crates/utiles-cli/src/stdinterator.rs @@ -88,7 +88,7 @@ impl StdInterator { debug!("reading from args"); let args = file_content .lines() // This assumes that each line is separated by '\n' - .map(|s| s.to_string()) + .map(std::string::ToString::to_string) .collect::>(); debug!("args: {:?}", args); StdInteratorSource::Args(args) From ed07e6f67efbdcfcee21c9e36472e0fca06f14ef Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Wed, 8 Nov 2023 10:23:51 -0800 Subject: [PATCH 32/80] fmt! --- crates/utiles-cli/src/cli.rs | 207 ++++++++------------------ crates/utiles-cli/src/lib.rs | 2 +- crates/utiles-cli/src/stdinterator.rs | 2 +- crates/utiles/src/tilejson.rs | 2 +- 4 files changed, 65 insertions(+), 148 deletions(-) diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index a33eb7f5..621e1e72 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -1,11 +1,13 @@ +use std::io::{self, Write}; + use clap::{Parser, Subcommand, ValueEnum}; use tracing::debug; use tracing_subscriber::EnvFilter; use utiles::parsing::parse_bbox; -use utiles::{bounding_tile, Tile}; use utiles::tilejson::tilejson_stringify; use utiles::tiles; use utiles::zoom::ZoomOrZooms; +use utiles::{bounding_tile, Tile}; use utilesqlite::mbtiles::Mbtiles; use crate::stdinterator::StdInterator; @@ -20,35 +22,20 @@ pub struct Cli { // debug flag #[arg( - long, - short, - global = true, - default_value = "false", - help = "debug mode" + long, + short, + global = true, + default_value = "false", + help = "debug mode" )] debug: bool, // #[command(flatten , help="verbosity level (-v, -vv, -vvv, -vvvv)" )] // verbose: Verbosity, } -#[derive(Debug, Parser)] // requires `derive` feature -pub struct QuadkeyArgs { - /// The remote to clone - // #[arg(required = false, default_value = "-")] - // quadkey: MaybeStdin, - // #[arg(required = false)] - // quadkey: Option, - #[arg(required = false)] - input: Option, -} - #[derive(Debug, Parser)] // requires `derive` feature pub struct InputAndSequenceArgs { /// The remote to clone - // #[arg(required = false, default_value = "-")] - // quadkey: MaybeStdin, - // #[arg(required = false)] - // quadkey: Option, #[arg(required = false)] input: Option, @@ -58,11 +45,6 @@ pub struct InputAndSequenceArgs { #[derive(Debug, Subcommand)] pub enum Commands { - /// quadkey - // Quadkey { - // #[arg(required = true)] - // quadkey: String, - // }, #[command(name = "lint", about = "lint mbtiles file", long_about = None)] Lint { #[arg(required = true)] @@ -71,13 +53,21 @@ pub enum Commands { #[arg(required = false, long, action = clap::ArgAction::SetTrue)] fix: bool, }, - #[command(name = "tilejson", visible_alias = "tj", about = "output tilejson", long_about = None)] + #[command(name = "tilejson", visible_alias = "tj", about = "echo tilejson", long_about = None)] Tilejson { - #[arg(required = true)] + #[arg(required = true, help = "mbtiles filepath")] filepath: String, + + #[arg(required = false, short, long, help= "compact json", action = clap::ArgAction::SetTrue)] + min: bool, }, - // MERCANTILE CLIKE (cli+like) + + // ======================================================================== + // TILE CLI UTILS - MERCANTILE LIKE CLI + // ======================================================================== + + #[command(name = "tiles", about = "echo tiles of bbox", long_about = None)] Tiles { #[arg(required = true)] zoom: u8, @@ -106,9 +96,8 @@ pub enum Commands { #[arg(required = false, long, action = clap::ArgAction::SetTrue)] seq: bool, }, - #[command(name = "neighbors", about = "print neighbors of tile(s)", long_about = None)] - Neighbors - { + #[command(name = "neighbors", about = "echo neighbors of tile(s)", long_about = None)] + Neighbors { #[arg(required = false)] input: Option, @@ -116,7 +105,7 @@ pub enum Commands { seq: bool, }, - #[command(name = "parent", about = "print parent of tile(s)", long_about = None)] + #[command(name = "parent", about = "echo parent of tile(s)", long_about = None)] Parent { #[arg(required = false)] input: Option, @@ -127,7 +116,7 @@ pub enum Commands { #[arg(required = false, long, default_value = "1")] depth: u8, }, - #[command(name = "children", about = "print children of tile(s)", long_about = None)] + #[command(name = "children", about = "echo children of tile(s)", long_about = None)] Children { #[arg(required = false)] input: Option, @@ -138,57 +127,13 @@ pub enum Commands { #[arg(required = false, long, default_value = "1")] depth: u8, }, - #[command(name = "shapes", about = "print shapes of tiles as geojson", long_about = None)] + #[command(name = "shapes", about = "echo shapes of tiles as geojson", long_about = None)] Shapes { #[arg(required = true)] input: String, seq: bool, }, - - - // /// Clones repos - // #[command(arg_required_else_help = true)] - // Clone { - // /// The remote to clone - // remote: String, - // }, - // /// Compare two commits - // Diff { - // #[arg(value_name = "COMMIT")] - // base: Option, - // #[arg(value_name = "COMMIT")] - // head: Option, - // #[arg(last = true)] - // path: Option, - // #[arg( - // long, - // require_equals = true, - // value_name = "WHEN", - // num_args = 0..=1, - // default_value_t = ColorWhen::Auto, - // default_missing_value = "always", - // value_enum - // )] - // color: ColorWhen, - // }, - // /// pushes things - // #[command(arg_required_else_help = true)] - // Push { - // /// The remote to target - // remote: String, - // }, - // /// adds things - // #[command(arg_required_else_help = true)] - // Add { - // /// Stuff to add - // #[arg(required = true)] - // path: Vec, - // }, - - // Stash(StashArgs), - // #[command(external_subcommand)] - // External(Vec), } #[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq)] @@ -229,7 +174,6 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { // .init(); // Configure the filter - let filter = if args.debug { EnvFilter::new("DEBUG") } else { @@ -246,36 +190,22 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { debug!("args: {:?}", args); - match args.command { Commands::Lint { filepath, fix } => { println!("lint (fix -- {fix}): {filepath}"); // throw not implemented error panic!("not implemented (yet)") } - Commands::Tilejson { filepath } => { - println!("tilejson: {filepath}"); - println!("NOT IMPLEMENTED YET"); - let mbtiles = Mbtiles::from_filepath( - &filepath - ).unwrap(); + Commands::Tilejson { filepath, min } => { + debug!("tilejson: {filepath}"); + let mbtiles = Mbtiles::from_filepath(&filepath).unwrap(); let tj = mbtiles.tilejson().unwrap(); - - let s = tilejson_stringify(&tj, None); - + let s = tilejson_stringify(&tj, Option::from(!min)); println!("{s}"); - - // println!( - // "{}", - // serde_json::to_string_pretty(&tj).unwrap() - // ); } - // mercantile cli like - Commands::Quadkey { - input - } => { + Commands::Quadkey { input } => { // let thingy = StdInterator::new(quadkey.quadkey).unwrap(); // for line in thingy { // println!("Line from stdin: `{}`", line.unwrap()); @@ -298,7 +228,6 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { // let qk = utiles::xyz2quadkey(tile.west, tile.south, tile.zoom); // println!("{}", qk); } else { - // treat as quadkey let qk = lstr; let tile = Tile::from_quadkey(&qk); @@ -317,9 +246,8 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { let input_lines = StdInterator::new(input).unwrap(); let lines = input_lines .filter(|l| !l.is_err()) - .filter(|l| !l.as_ref().unwrap().is_empty()).filter( - |l| l.as_ref().unwrap() != "\x1e" - ); + .filter(|l| !l.as_ref().unwrap().is_empty()) + .filter(|l| l.as_ref().unwrap() != "\x1e"); let bboxes = lines.map(|l| { let s = l.unwrap(); @@ -354,30 +282,34 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { let input_lines = StdInterator::new(input).unwrap(); let lines = input_lines .filter(|l| !l.is_err()) - .filter(|l| !l.as_ref().unwrap().is_empty()).filter( - |l| l.as_ref().unwrap() != "\x1e" - ); - - let tiles = lines.map(|l| { - let s = l.unwrap(); - debug!("l: {:?}", s); - parse_bbox(&s).unwrap() - }) - .flat_map(|b| tiles( - (b.west, b.south, b.east, b.north), - ZoomOrZooms::Zoom(zoom), - )).enumerate(); + .filter(|l| !l.as_ref().unwrap().is_empty()) + .filter(|l| l.as_ref().unwrap() != "\x1e"); + let mut stdout = io::stdout(); + + let tiles = lines + .map(|l| { + let s = l.unwrap(); + debug!("l: {:?}", s); + parse_bbox(&s).unwrap() + }) + .flat_map(|b| { + tiles((b.west, b.south, b.east, b.north), ZoomOrZooms::Zoom(zoom)) + }) + .enumerate(); // let bboxes = lines for (i, tile) in tiles { let rs = if seq { "\x1e\n" } else { "" }; - println!("{}{}", rs, tile.json_arr()); + // println!("{}{}", rs, tile.json_arr()); + writeln!(stdout, "{}{}", rs, tile.json_arr()).unwrap(); // call loop_fn if it's defined every 1000 iterations for signal break if i % 1024 == 0 { + stdout.flush().unwrap(); if let Some(f) = loop_fn { f(); } } } + stdout.flush().unwrap(); // for tile in tiles { // let tstr = tile.json_arr(); @@ -426,18 +358,13 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { // } // } } - Commands::Neighbors { - input, seq - } => { + Commands::Neighbors { input, seq } => { let input_lines = StdInterator::new(input).unwrap(); let lines = input_lines .filter(|l| !l.is_err()) - .filter(|l| !l.as_ref().unwrap().is_empty()).filter( - |l| l.as_ref().unwrap() != "\x1e" - ); - let tiles = lines.map(|l| { - Tile::from_json(&l.unwrap()) - }); + .filter(|l| !l.as_ref().unwrap().is_empty()) + .filter(|l| l.as_ref().unwrap() != "\x1e"); + let tiles = lines.map(|l| Tile::from_json(&l.unwrap())); for tile in tiles { let neighbors = tile.neighbors(); for neighbor in neighbors { @@ -447,18 +374,13 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { } } - Commands::Children{ - input, seq, depth - } => { + Commands::Children { input, seq, depth } => { let input_lines = StdInterator::new(input).unwrap(); let lines = input_lines .filter(|l| !l.is_err()) - .filter(|l| !l.as_ref().unwrap().is_empty()).filter( - |l| l.as_ref().unwrap() != "\x1e" - ); - let tiles = lines.map(|l| { - Tile::from_json(&l.unwrap()) - }); + .filter(|l| !l.as_ref().unwrap().is_empty()) + .filter(|l| l.as_ref().unwrap() != "\x1e"); + let tiles = lines.map(|l| Tile::from_json(&l.unwrap())); for tile in tiles { let children = tile.children(Option::from(tile.z + depth)); for child in children { @@ -468,20 +390,15 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { } } - Commands::Parent{ - input, seq, depth - } => { + Commands::Parent { input, seq, depth } => { let input_lines = StdInterator::new(input).unwrap(); let lines = input_lines .filter(|l| !l.is_err()) - .filter(|l| !l.as_ref().unwrap().is_empty()).filter( - |l| l.as_ref().unwrap() != "\x1e" - ); - let tiles = lines.map(|l| { - Tile::from_json(&l.unwrap()) - }); + .filter(|l| !l.as_ref().unwrap().is_empty()) + .filter(|l| l.as_ref().unwrap() != "\x1e"); + let tiles = lines.map(|l| Tile::from_json(&l.unwrap())); for tile in tiles { - let parent = tile.parent(Option::from(depth-1)); + let parent = tile.parent(Option::from(depth - 1)); let rs = if seq { "\x1e\n" } else { "" }; println!("{}{}", rs, parent.json_arr()); } diff --git a/crates/utiles-cli/src/lib.rs b/crates/utiles-cli/src/lib.rs index ab0653ee..1e2f04de 100644 --- a/crates/utiles-cli/src/lib.rs +++ b/crates/utiles-cli/src/lib.rs @@ -1,2 +1,2 @@ pub mod cli; -pub mod stdinterator; \ No newline at end of file +pub mod stdinterator; diff --git a/crates/utiles-cli/src/stdinterator.rs b/crates/utiles-cli/src/stdinterator.rs index fd1d7199..ae2f6b61 100644 --- a/crates/utiles-cli/src/stdinterator.rs +++ b/crates/utiles-cli/src/stdinterator.rs @@ -62,9 +62,9 @@ // } // } // } +use std::collections::VecDeque; use std::io; use std::io::BufRead; -use std::collections::VecDeque; use tracing::debug; pub enum StdInteratorSource { diff --git a/crates/utiles/src/tilejson.rs b/crates/utiles/src/tilejson.rs index 7cd41f72..24c23d4c 100644 --- a/crates/utiles/src/tilejson.rs +++ b/crates/utiles/src/tilejson.rs @@ -3,7 +3,7 @@ use tilejson::TileJSON; pub fn tilejson_stringify(tj: &TileJSON, fmt: Option) -> String { match fmt { - Some(true) => serde_json::to_string(&tj).unwrap(), + Some(false) => serde_json::to_string(&tj).unwrap(), _ => serde_json::to_string_pretty(&tj).unwrap(), } } From ea08f7f69cf7a88908604b53669aac83eeb3e06d Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Wed, 8 Nov 2023 14:51:24 -0800 Subject: [PATCH 33/80] ut cli entry --- python/utiles/ut.py | 51 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 python/utiles/ut.py diff --git a/python/utiles/ut.py b/python/utiles/ut.py new file mode 100644 index 00000000..e363f7a8 --- /dev/null +++ b/python/utiles/ut.py @@ -0,0 +1,51 @@ +"""Utiles cli""" +from __future__ import annotations + +import logging +import sys + +import click + +import utiles +from utiles import libutiles + +logger = logging.getLogger(__name__) + + +class NoHelpCommand(click.Command): + def get_help_option(self, ctx: click.Context) -> None: + return None + + +# The CLI command group. +@click.command( + name="utiles", + cls=NoHelpCommand, + help="utiles cli (python-rust)", + no_args_is_help=False, + context_settings={ + "ignore_unknown_options": True, + "allow_extra_args": True, + }, +) +# @click.argument("cmd", required=false) +# @click.option("--verbose", "-v", count=True, help="Increase verbosity.") +# @click.option("--quiet", "-q", count=True, help="Decrease verbosity.") +@click.version_option(version=utiles.__version__, message="%(version)s") +@click.pass_context +def cli(ctx: click.Context) -> None: + """Execute the main utiles command""" + # verbosity = verbose - quiet + # configure_logging(verbosity) + # ctx.obj["verbosity"] = verbosity + args = ["ut", *sys.argv[1:]] + try: + res = libutiles.ut_cli(args) + click.echo(res, err=True) + except Exception as e: + logger.error(e) + raise click.BadParameter(str(e)) + + +if __name__ == "__main__": + cli() From bcb808106ec133c3b1ec6ba718497cf3830a8a3d Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Wed, 8 Nov 2023 14:51:33 -0800 Subject: [PATCH 34/80] all the things --- crates/utiles-cli/src/cli.rs | 12 ++++- crates/utilesqlite/src/mbtiles.rs | 17 ++++++ python/utiles/__init__.py | 11 ++-- python/utiles/rio_plugin.py | 1 + tests/test_rust_cli.py | 88 ++++++++++++++----------------- 5 files changed, 75 insertions(+), 54 deletions(-) diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index 621e1e72..b5cad8d3 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -1,4 +1,5 @@ use std::io::{self, Write}; +use std::path::Path; use clap::{Parser, Subcommand, ValueEnum}; use tracing::debug; @@ -198,7 +199,16 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { } Commands::Tilejson { filepath, min } => { debug!("tilejson: {filepath}"); - let mbtiles = Mbtiles::from_filepath(&filepath).unwrap(); + // check that filepath exists and is file + let filepath = Path::new(&filepath); + if !filepath.exists() { + panic!("File does not exist: {}", filepath.display()); + } + if !filepath.is_file() { + panic!("Not a file: {filepath}", filepath = filepath.display()); + } + let mbtiles: Mbtiles = Mbtiles::from(filepath); + // let mbtiles = Mbtiles::from_filepath(&filepath).unwrap(); let tj = mbtiles.tilejson().unwrap(); let s = tilejson_stringify(&tj, Option::from(!min)); println!("{s}"); diff --git a/crates/utilesqlite/src/mbtiles.rs b/crates/utilesqlite/src/mbtiles.rs index 61d2b0da..f2e30113 100644 --- a/crates/utilesqlite/src/mbtiles.rs +++ b/crates/utilesqlite/src/mbtiles.rs @@ -1,4 +1,5 @@ use std::error::Error; +use std::path::Path; use rusqlite::{Connection, Result as RusqliteResult}; use tilejson::TileJSON; @@ -51,11 +52,27 @@ impl Mbtiles { let mbt = Mbtiles { conn: conn, }; + return Ok(mbt); + } + pub fn from_filepath_str(fspath: &str) -> Result> { + let conn = rusqlite::Connection::open(fspath)?; + let mbt = Mbtiles { + conn: conn, + }; return Ok(mbt); } } +impl From<&Path> for Mbtiles { + fn from(path: &Path) -> Self { + let conn = rusqlite::Connection::open(path).unwrap(); + Mbtiles { + conn: conn, + } + } +} + pub struct MbtilesManager { conn: Option, diff --git a/python/utiles/__init__.py b/python/utiles/__init__.py index a6015d13..56f1b84a 100644 --- a/python/utiles/__init__.py +++ b/python/utiles/__init__.py @@ -3,19 +3,20 @@ import math from typing import List, Sequence, Tuple, Union + from utiles import libutiles from utiles.libutiles import ( - Bbox, - LngLat, - LngLatBbox, TILETYPE_GIF, TILETYPE_JPG, + TILETYPE_JSON, TILETYPE_PBF, + TILETYPE_PBFGZ, TILETYPE_PNG, TILETYPE_UNKNOWN, - TILETYPE_JSON, - TILETYPE_PBFGZ, TILETYPE_WEBP, + Bbox, + LngLat, + LngLatBbox, Tile, __build_profile__, __version_lib__, diff --git a/python/utiles/rio_plugin.py b/python/utiles/rio_plugin.py index ee44d13c..ecace2f0 100644 --- a/python/utiles/rio_plugin.py +++ b/python/utiles/rio_plugin.py @@ -1,4 +1,5 @@ import click + from utiles.cli import cli as rio_utiles __all__ = ("rio_ut", "rio_utiles") diff --git a/tests/test_rust_cli.py b/tests/test_rust_cli.py index b39a83b5..a5632279 100644 --- a/tests/test_rust_cli.py +++ b/tests/test_rust_cli.py @@ -143,10 +143,6 @@ # assert result2.output == "[2331, 1185, 12]\n" - - - - def _run_cli( args: list[str] | None, input: str | None = None, @@ -301,34 +297,28 @@ def test_cli_quadkey_failure(self) -> None: assert result.exit_code == 2 assert "lolwut" in result.output -class TestBoundingTile: +class TestBoundingTile: def test_cli_bounding_tile_bad_bounds(self) -> None: """Bounds of len 3 are bad.""" runner = CliRunner() - result = _run_cli( ["bounding-tile"], "[-105, 39.99, -104.99]") + result = _run_cli(["bounding-tile"], "[-105, 39.99, -104.99]") assert result.returncode != 0 - def test_cli_bounding_tile(self) -> None: - result = _run_cli( ["bounding-tile"], "[-105, 39.99, -104.99, 40]") + result = _run_cli(["bounding-tile"], "[-105, 39.99, -104.99, 40]") assert result.returncode == 0 assert result.stdout == "[1706, 3101, 13]\n" - def test_cli_bounding_tile_bbox(self) -> None: - result = _run_cli( - ["bounding-tile"], '{"bbox": [-105, 39.99, -104.99, 40]}' - ) + result = _run_cli(["bounding-tile"], '{"bbox": [-105, 39.99, -104.99, 40]}') assert result.returncode == 0 assert result.stdout == "[1706, 3101, 13]\n" - def test_cli_bounding_tile2(self) -> None: - result = _run_cli( ["bounding-tile"], "[-105, 39.99]") + result = _run_cli(["bounding-tile"], "[-105, 39.99]") assert result.returncode == 0 - def test_cli_multi_bounding_tile(self) -> None: """A JSON text sequence can be used as input.""" result = _run_cli( @@ -337,7 +327,6 @@ def test_cli_multi_bounding_tile(self) -> None: assert result.returncode == 0 assert len(result.stdout.strip().split("\n")) == 2 - def test_cli_multi_bounding_tile_seq(self) -> None: """A JSON text sequence can be used as input.""" result = _run_cli( @@ -347,43 +336,51 @@ def test_cli_multi_bounding_tile_seq(self) -> None: assert result.returncode == 0 assert len(result.stdout.strip().split("\n")) == 2 - @pytest.mark.skip(reason="I dont think this is correct") def test_cli_tiles_bounding_tiles_z0(self) -> None: - result = _run_cli( ["bounding-tile"], "[-1, -1, 1, 1]") + result = _run_cli(["bounding-tile"], "[-1, -1, 1, 1]") assert result.returncode == 0 assert result.stdout == "[0, 0, 0]\n" - @pytest.mark.skip(reason="I dont think this is correct either") def test_cli_tiles_bounding_tiles_seq(self) -> None: - result = _run_cli( ["bounding-tile", "--seq"], "[-1, -1, 1, 1]") + result = _run_cli(["bounding-tile", "--seq"], "[-1, -1, 1, 1]") assert result.returncode == 0 assert result.stdout == "\x1e\n[0, 0, 0]\n" - def test_cli_bounding_tile_geosjon(self) -> None: - collection_dict = {'features': [{'geometry': {'coordinates': [[[-105.46875, 39.909736], - [-105.46875, 40.446947], - [-104.765625, 40.446947], - [-104.765625, 39.909736], - [-105.46875, 39.909736]]], - 'type': 'Polygon'}, - 'id': '(106, 193, 9)', - 'properties': {'title': 'XYZ tile (106, 193, 9)'}, - 'type': 'Feature'}], - 'type': 'FeatureCollection'} + collection_dict = { + "features": [ + { + "geometry": { + "coordinates": [ + [ + [-105.46875, 39.909736], + [-105.46875, 40.446947], + [-104.765625, 40.446947], + [-104.765625, 39.909736], + [-105.46875, 39.909736], + ] + ], + "type": "Polygon", + }, + "id": "(106, 193, 9)", + "properties": {"title": "XYZ tile (106, 193, 9)"}, + "type": "Feature", + } + ], + "type": "FeatureCollection", + } collection = json.dumps(collection_dict) - result = _run_cli( ["bounding-tile"], collection) + result = _run_cli(["bounding-tile"], collection) assert result.returncode == 0 assert result.stdout == "[26, 48, 7]\n" class TestNeighbors: - def test_cli_neighbors(self) -> None: - result = _run_cli( ["neighbors"], "[243, 166, 9]") - assert result.returncode== 0 + result = _run_cli(["neighbors"], "[243, 166, 9]") + assert result.returncode == 0 print(result.stdout) tiles_lines = result.stdout.strip().split("\n") @@ -404,39 +401,34 @@ def test_cli_neighbors(self) -> None: assert (243 + 1, 166 + 0, 9) in tiles_set assert (243 + 1, 166 + 1, 9) in tiles_set + class TestParent: def test_cli_parent_failure(self) -> None: """[0, 0, 0] has no parent""" - result = _run_cli( ["parent"], "[0, 0, 0]") - assert result.returncode !=0 - + result = _run_cli(["parent"], "[0, 0, 0]") + assert result.returncode != 0 def test_cli_parent(self) -> None: - result = _run_cli( ["parent"], "[486, 332, 10]\n[486, 332, 10]") + result = _run_cli(["parent"], "[486, 332, 10]\n[486, 332, 10]") assert result.returncode == 0 assert result.stdout == "[243, 166, 9]\n[243, 166, 9]\n" - def test_cli_parent_depth(self) -> None: - result = _run_cli( ["parent", "--depth", "2"], "[486, 332, 10]") + result = _run_cli(["parent", "--depth", "2"], "[486, 332, 10]") assert result.returncode == 0 assert result.stdout == "[121, 83, 8]\n" - def test_cli_parent_multidepth(self) -> None: - result = _run_cli( - ["parent", "--depth", "2"], "[486, 332, 10]\n[121, 83, 8]" - ) + result = _run_cli(["parent", "--depth", "2"], "[486, 332, 10]\n[121, 83, 8]") assert result.returncode == 0 assert result.stdout == "[121, 83, 8]\n[30, 20, 6]\n" -class TestChildren: +class TestChildren: def test_cli_children(self) -> None: - result = _run_cli( ["children"], "[243, 166, 9]") + result = _run_cli(["children"], "[243, 166, 9]") assert result.returncode == 0 assert ( result.stdout == "[486, 332, 10]\n[487, 332, 10]\n[487, 333, 10]\n[486, 333, 10]\n" ) - From cb4a4f7d0c92489e4a249bfadad3296645554058 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Wed, 8 Nov 2023 15:17:13 -0800 Subject: [PATCH 35/80] why is cicd not running right? --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d84f0bdd..3ff5550f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: shell: bash run: | set -e - pip install utiles --find-links dist --force-reinstall + pip install utiles --find-links dist --force-reinstall -vvv pip install -r requirements/dev.txt pytest # - name: pytest From 6c9f077fe2eb973e5be1bfb1eb895176ea247eba Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Wed, 8 Nov 2023 15:19:45 -0800 Subject: [PATCH 36/80] uh why --- .github/workflows/dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 4cba6b1c..f6e63627 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -36,7 +36,7 @@ jobs: shell: bash run: | set -e - pip install utiles --find-links dist --force-reinstall + pip install utiles --find-links dist --force-reinstall -vvv pip install -r requirements/dev.txt pytest From 7d534ae0fcdb9dcfd6fca516ecdd0dcf2fe4ae87 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Wed, 8 Nov 2023 15:26:19 -0800 Subject: [PATCH 37/80] aha --- tests/test_rust_cli.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_rust_cli.py b/tests/test_rust_cli.py index a5632279..5d23d3e2 100644 --- a/tests/test_rust_cli.py +++ b/tests/test_rust_cli.py @@ -1,4 +1,5 @@ """Utiles rust cli tests""" +import sys from subprocess import run from json import dumps as stringify import json @@ -147,8 +148,9 @@ def _run_cli( args: list[str] | None, input: str | None = None, ): + _python = sys.executable res = run( - ["python", "-m", "utiles.ut", *args], + [_python, "-m", "utiles.ut", *args], input=input, capture_output=True, text=True, From 5c8e8554d9003ff11798902f3c64f5c537fd0f63 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Wed, 8 Nov 2023 15:32:40 -0800 Subject: [PATCH 38/80] super wonky that I cannot install this version.... --- .github/workflows/dev.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index f6e63627..f58cc950 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -36,8 +36,14 @@ jobs: shell: bash run: | set -e - pip install utiles --find-links dist --force-reinstall -vvv - pip install -r requirements/dev.txt + which pip -a + which python -a + ls dist + + # pip install utiles --find-links dist --force-reinstall -vvv + # pip install -r requirements/dev.txt + + which pytest -a pytest # - name: pytest From 9624b8cf31b856a6a36ee7782f58928286ee15d7 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Wed, 8 Nov 2023 15:44:15 -0800 Subject: [PATCH 39/80] python is a terrible language --- pyproject.toml | 3 +-- {tests => python/tests}/__init__.py | 0 {tests => python/tests}/conftest.py | 0 {tests => python/tests}/mercantests/__init__.py | 0 {tests => python/tests}/mercantests/test_cli.py | 0 {tests => python/tests}/mercantests/test_funcs.py | 0 {tests => python/tests}/test_exports.py | 0 {tests => python/tests}/test_mbtiles.py | 0 {tests => python/tests}/test_rust_cli.py | 0 {tests => python/tests}/test_tiletype.py | 2 +- {tests => python/tests}/test_utiles.py | 0 {tests => python/tests}/tutils.py | 0 12 files changed, 2 insertions(+), 3 deletions(-) rename {tests => python/tests}/__init__.py (100%) rename {tests => python/tests}/conftest.py (100%) rename {tests => python/tests}/mercantests/__init__.py (100%) rename {tests => python/tests}/mercantests/test_cli.py (100%) rename {tests => python/tests}/mercantests/test_funcs.py (100%) rename {tests => python/tests}/test_exports.py (100%) rename {tests => python/tests}/test_mbtiles.py (100%) rename {tests => python/tests}/test_rust_cli.py (100%) rename {tests => python/tests}/test_tiletype.py (99%) rename {tests => python/tests}/test_utiles.py (100%) rename {tests => python/tests}/tutils.py (100%) diff --git a/pyproject.toml b/pyproject.toml index a3315f2d..e13c5ca5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,8 +33,7 @@ module-name = "utiles.libutiles" [tool.pytest.ini_options] testpaths = [ - "tests", -# "python" + "python" ] addopts = [ "--doctest-modules", diff --git a/tests/__init__.py b/python/tests/__init__.py similarity index 100% rename from tests/__init__.py rename to python/tests/__init__.py diff --git a/tests/conftest.py b/python/tests/conftest.py similarity index 100% rename from tests/conftest.py rename to python/tests/conftest.py diff --git a/tests/mercantests/__init__.py b/python/tests/mercantests/__init__.py similarity index 100% rename from tests/mercantests/__init__.py rename to python/tests/mercantests/__init__.py diff --git a/tests/mercantests/test_cli.py b/python/tests/mercantests/test_cli.py similarity index 100% rename from tests/mercantests/test_cli.py rename to python/tests/mercantests/test_cli.py diff --git a/tests/mercantests/test_funcs.py b/python/tests/mercantests/test_funcs.py similarity index 100% rename from tests/mercantests/test_funcs.py rename to python/tests/mercantests/test_funcs.py diff --git a/tests/test_exports.py b/python/tests/test_exports.py similarity index 100% rename from tests/test_exports.py rename to python/tests/test_exports.py diff --git a/tests/test_mbtiles.py b/python/tests/test_mbtiles.py similarity index 100% rename from tests/test_mbtiles.py rename to python/tests/test_mbtiles.py diff --git a/tests/test_rust_cli.py b/python/tests/test_rust_cli.py similarity index 100% rename from tests/test_rust_cli.py rename to python/tests/test_rust_cli.py diff --git a/tests/test_tiletype.py b/python/tests/test_tiletype.py similarity index 99% rename from tests/test_tiletype.py rename to python/tests/test_tiletype.py index 95d0217f..4f75ef4a 100644 --- a/tests/test_tiletype.py +++ b/python/tests/test_tiletype.py @@ -8,7 +8,7 @@ Extensions = Union[str, bool] PWD = Path(__file__).parent -REPO_ROOT = PWD.parent +REPO_ROOT = PWD.parent.parent def tiletype(buffer: bytes) -> Extensions: diff --git a/tests/test_utiles.py b/python/tests/test_utiles.py similarity index 100% rename from tests/test_utiles.py rename to python/tests/test_utiles.py diff --git a/tests/tutils.py b/python/tests/tutils.py similarity index 100% rename from tests/tutils.py rename to python/tests/tutils.py From 7539f26f3c93e85625ace4f016f6bb4853190d05 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Wed, 8 Nov 2023 15:50:00 -0800 Subject: [PATCH 40/80] why? --- .github/workflows/dev.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index f58cc950..32b7a203 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -39,11 +39,8 @@ jobs: which pip -a which python -a ls dist - - # pip install utiles --find-links dist --force-reinstall -vvv - # pip install -r requirements/dev.txt - - which pytest -a + pip install utiles --find-links dist --force-reinstall -vvv + pip install -r requirements/dev.txt pytest # - name: pytest From 6e3c133cbbd29991478d1b1aec326f523f8bc2bd Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Wed, 8 Nov 2023 15:54:37 -0800 Subject: [PATCH 41/80] jesus I hate python --- .github/workflows/dev.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 32b7a203..f6e63627 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -36,9 +36,6 @@ jobs: shell: bash run: | set -e - which pip -a - which python -a - ls dist pip install utiles --find-links dist --force-reinstall -vvv pip install -r requirements/dev.txt pytest From 40f9bee31f7dff384f49049dc799af96001def0b Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Wed, 8 Nov 2023 16:00:39 -0800 Subject: [PATCH 42/80] why is that ok/ --- python/utiles/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/utiles/__init__.py b/python/utiles/__init__.py index 56f1b84a..fcfec73d 100644 --- a/python/utiles/__init__.py +++ b/python/utiles/__init__.py @@ -4,7 +4,7 @@ import math from typing import List, Sequence, Tuple, Union -from utiles import libutiles +from . import libutiles from utiles.libutiles import ( TILETYPE_GIF, TILETYPE_JPG, From 6162e9daa599607ed659d048d8d4b9d91066f7ba Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Wed, 8 Nov 2023 16:05:02 -0800 Subject: [PATCH 43/80] jfc --- python/utiles/__init__.py | 2 -- python/utiles/ut.py | 20 +++++--------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/python/utiles/__init__.py b/python/utiles/__init__.py index fcfec73d..26332c48 100644 --- a/python/utiles/__init__.py +++ b/python/utiles/__init__.py @@ -4,7 +4,6 @@ import math from typing import List, Sequence, Tuple, Union -from . import libutiles from utiles.libutiles import ( TILETYPE_GIF, TILETYPE_JPG, @@ -88,7 +87,6 @@ "from_pmtileid", "from_tuple", "geojson_bounds", - "libutiles", "lnglat", "minmax", "neighbors", diff --git a/python/utiles/ut.py b/python/utiles/ut.py index e363f7a8..60d76daa 100644 --- a/python/utiles/ut.py +++ b/python/utiles/ut.py @@ -5,15 +5,13 @@ import sys import click - -import utiles -from utiles import libutiles +from utiles import ut_cli, __version__ logger = logging.getLogger(__name__) class NoHelpCommand(click.Command): - def get_help_option(self, ctx: click.Context) -> None: + def get_help_option(self, _ctx: click.Context) -> None: return None @@ -28,24 +26,16 @@ def get_help_option(self, ctx: click.Context) -> None: "allow_extra_args": True, }, ) -# @click.argument("cmd", required=false) -# @click.option("--verbose", "-v", count=True, help="Increase verbosity.") -# @click.option("--quiet", "-q", count=True, help="Decrease verbosity.") -@click.version_option(version=utiles.__version__, message="%(version)s") -@click.pass_context -def cli(ctx: click.Context) -> None: +@click.version_option(version=__version__, message="%(version)s") +def cli() -> None: """Execute the main utiles command""" - # verbosity = verbose - quiet - # configure_logging(verbosity) - # ctx.obj["verbosity"] = verbosity args = ["ut", *sys.argv[1:]] try: - res = libutiles.ut_cli(args) + res = ut_cli(args) click.echo(res, err=True) except Exception as e: logger.error(e) raise click.BadParameter(str(e)) - if __name__ == "__main__": cli() From 817fc0b10bfef13b7d38e1f2a45961ff673ed55a Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Wed, 8 Nov 2023 16:12:11 -0800 Subject: [PATCH 44/80] why is this so crappy.... --- python/utiles/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/utiles/__init__.py b/python/utiles/__init__.py index 26332c48..43f860fd 100644 --- a/python/utiles/__init__.py +++ b/python/utiles/__init__.py @@ -4,7 +4,7 @@ import math from typing import List, Sequence, Tuple, Union -from utiles.libutiles import ( +from .libutiles import ( # noqa: TID252 TILETYPE_GIF, TILETYPE_JPG, TILETYPE_JSON, From 383e7599006e1ddad70df3376e7663af2c11a122 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 06:00:10 -0800 Subject: [PATCH 45/80] test for utiles tile type test data found --- pyproject.toml | 1 + python/tests/test_tiletype.py | 3 +++ python/utiles/__about__.py | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e13c5ca5..e803ebba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -160,6 +160,7 @@ exclude = [ "venv", ] ignore = [ + "TID252", "A003", # Allow non-abstract empty methods in abstract base classes "B027", diff --git a/python/tests/test_tiletype.py b/python/tests/test_tiletype.py index 4f75ef4a..8b6b2820 100644 --- a/python/tests/test_tiletype.py +++ b/python/tests/test_tiletype.py @@ -84,6 +84,9 @@ def tiletype(buffer: bytes) -> Extensions: "tile-obj.json": "json", } +def test_found_test_files() -> None: + assert len(TEST_TILES_BYTES) == len(TEST_TILE_NAME2TYPE) + @pytest.mark.parametrize( "tile", diff --git a/python/utiles/__about__.py b/python/utiles/__about__.py index 7ca6fbfb..46b514ac 100644 --- a/python/utiles/__about__.py +++ b/python/utiles/__about__.py @@ -1,6 +1,6 @@ """Package metadata/info""" -from utiles.libutiles import __version_lib__ +from .libutiles import __version_lib__ # noqa: TID252 __all__ = ("__title__", "__description__", "__pkgroot__", "__version__") __title__ = "utiles" From c79391d765c12c17af31a7059062312d9b9b73be Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 06:02:31 -0800 Subject: [PATCH 46/80] why do these things keep failing --- .github/workflows/dev.yml | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index f6e63627..058e8379 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -40,19 +40,20 @@ jobs: pip install -r requirements/dev.txt pytest -# - name: pytest -# if: ${{ !startsWith(matrix.target, 'x86') && matrix.target != 'ppc64' }} -# uses: uraimo/run-on-arch-action@v2.5.0 -# with: -# arch: ${{ matrix.target }} -# distro: ubuntu22.04 -# githubToken: ${{ github.token }} -# install: | -# apt-get update -# apt-get install -y --no-install-recommends python3 python3-pip -# pip3 install -U pip pytest -# pip3 install -r requirements/dev.txt -# run: | -# set -e -# pip3 install utiles --find-links dist --force-reinstall -# pip3 install -r requirements/dev.txt + - name: pytest + if: ${{ !startsWith(matrix.target, 'x86') && matrix.target != 'ppc64' }} + uses: uraimo/run-on-arch-action@v2.5.0 + with: + arch: ${{ matrix.target }} + distro: ubuntu22.04 + githubToken: ${{ github.token }} + install: | + apt-get update + apt-get install -y --no-install-recommends python3 python3-pip + pip3 install -U pip pytest + pip3 install -r requirements/dev.txt + run: | + set -e + pip3 install utiles --find-links dist --force-reinstall + pip3 install -r requirements/dev.txt + pytest From 39dee3b09d031af7bc227247e13b9fcbbd616b8b Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 06:23:38 -0800 Subject: [PATCH 47/80] christ --- noxfile.py | 22 ++++++++++++++++++++++ pyproject.toml | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/noxfile.py b/noxfile.py index e69de29b..4c6855a1 100644 --- a/noxfile.py +++ b/noxfile.py @@ -0,0 +1,22 @@ +"""Nox sessions for linting, docs, and testing.""" +from __future__ import annotations + +import argparse +import os +import shutil +from pathlib import Path + +import nox + +DIR = Path(__file__).parent.resolve() + +nox.options.sessions = ["test"] + + +@nox.session +def test(session: nox.Session) -> None: + """Run the unit and regular tests.""" + session.install("maturin") + session.install("pytest", "hypothesis", "pytest-cov", "pytest-benchmark") + session.run("maturin", "develop", "--release", "--extras=test") + session.run("pytest") diff --git a/pyproject.toml b/pyproject.toml index e803ebba..6755ddf7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["maturin>=0.14,<0.15"] +requires = ["maturin>=0.15"] build-backend = "maturin" [project] From 39bad1f66b5f8b32aa6c8088abf82a6d7dc231a2 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 06:27:23 -0800 Subject: [PATCH 48/80] something is not right.... --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 6755ddf7..96c70dfa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,7 @@ ut = "utiles.rio_plugin:rio_ut" [tool.maturin] python-source = "python" features = ["pyo3/extension-module"] +bindings = "pyo3" module-name = "utiles.libutiles" [tool.pytest.ini_options] From 71eb053af83ba3fc3672da63eb5e2e629e4d05e7 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 06:40:55 -0800 Subject: [PATCH 49/80] this is so oddddddd --- pyproject.toml | 2 +- python/tests/test_exports.py | 2 +- python/utiles/__about__.py | 2 +- python/utiles/__init__.py | 2 +- python/utiles/__main__.py | 2 +- src/lib.rs | 1 + 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 96c70dfa..fe22c3ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ ut = "utiles.rio_plugin:rio_ut" python-source = "python" features = ["pyo3/extension-module"] bindings = "pyo3" -module-name = "utiles.libutiles" +module-name = "utiles._utiles" [tool.pytest.ini_options] testpaths = [ diff --git a/python/tests/test_exports.py b/python/tests/test_exports.py index bb5b0cda..63fe5ffd 100644 --- a/python/tests/test_exports.py +++ b/python/tests/test_exports.py @@ -1,7 +1,7 @@ from collections import Counter import utiles -from utiles import libutiles +from utiles import _utiles as libutiles def test_import() -> None: diff --git a/python/utiles/__about__.py b/python/utiles/__about__.py index 46b514ac..0787384f 100644 --- a/python/utiles/__about__.py +++ b/python/utiles/__about__.py @@ -1,6 +1,6 @@ """Package metadata/info""" -from .libutiles import __version_lib__ # noqa: TID252 +from ._utiles import __version_lib__ # noqa: TID252 __all__ = ("__title__", "__description__", "__pkgroot__", "__version__") __title__ = "utiles" diff --git a/python/utiles/__init__.py b/python/utiles/__init__.py index 43f860fd..93571979 100644 --- a/python/utiles/__init__.py +++ b/python/utiles/__init__.py @@ -4,7 +4,7 @@ import math from typing import List, Sequence, Tuple, Union -from .libutiles import ( # noqa: TID252 +from ._utiles import ( # noqa: TID252 TILETYPE_GIF, TILETYPE_JPG, TILETYPE_JSON, diff --git a/python/utiles/__main__.py b/python/utiles/__main__.py index 318c6fab..84d2eee9 100644 --- a/python/utiles/__main__.py +++ b/python/utiles/__main__.py @@ -5,7 +5,7 @@ import sys from typing import Dict, Union -from utiles import libutiles +from utiles import _utiles as libutiles from utiles.__about__ import __pkgroot__, __title__, __version__ diff --git a/src/lib.rs b/src/lib.rs index 32f44ed2..230bd2ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -764,6 +764,7 @@ fn lib_constants(_py: Python<'_>, m: &PyModule) -> PyResult<()> { /// Utiles python module #[pymodule] +#[pyo3(name="_utiles")] fn libutiles(_py: Python<'_>, m: &PyModule) -> PyResult<()> { // lib constants lib_constants(_py, m)?; From ce32284363dc6e9e948e7c081788e37b9359cd70 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 06:49:05 -0800 Subject: [PATCH 50/80] utiles!? --- pyproject.toml | 3 ++- {python/tests => tests}/__init__.py | 0 {python/tests => tests}/conftest.py | 0 {python/tests => tests}/mercantests/__init__.py | 0 {python/tests => tests}/mercantests/test_cli.py | 0 {python/tests => tests}/mercantests/test_funcs.py | 0 {python/tests => tests}/test_exports.py | 0 {python/tests => tests}/test_mbtiles.py | 0 {python/tests => tests}/test_rust_cli.py | 0 {python/tests => tests}/test_tiletype.py | 13 ++++++++++++- {python/tests => tests}/test_utiles.py | 0 {python/tests => tests}/tutils.py | 0 12 files changed, 14 insertions(+), 2 deletions(-) rename {python/tests => tests}/__init__.py (100%) rename {python/tests => tests}/conftest.py (100%) rename {python/tests => tests}/mercantests/__init__.py (100%) rename {python/tests => tests}/mercantests/test_cli.py (100%) rename {python/tests => tests}/mercantests/test_funcs.py (100%) rename {python/tests => tests}/test_exports.py (100%) rename {python/tests => tests}/test_mbtiles.py (100%) rename {python/tests => tests}/test_rust_cli.py (100%) rename {python/tests => tests}/test_tiletype.py (92%) rename {python/tests => tests}/test_utiles.py (100%) rename {python/tests => tests}/tutils.py (100%) diff --git a/pyproject.toml b/pyproject.toml index fe22c3ce..304642e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,8 @@ module-name = "utiles._utiles" [tool.pytest.ini_options] testpaths = [ - "python" + "python", + "tests" ] addopts = [ "--doctest-modules", diff --git a/python/tests/__init__.py b/tests/__init__.py similarity index 100% rename from python/tests/__init__.py rename to tests/__init__.py diff --git a/python/tests/conftest.py b/tests/conftest.py similarity index 100% rename from python/tests/conftest.py rename to tests/conftest.py diff --git a/python/tests/mercantests/__init__.py b/tests/mercantests/__init__.py similarity index 100% rename from python/tests/mercantests/__init__.py rename to tests/mercantests/__init__.py diff --git a/python/tests/mercantests/test_cli.py b/tests/mercantests/test_cli.py similarity index 100% rename from python/tests/mercantests/test_cli.py rename to tests/mercantests/test_cli.py diff --git a/python/tests/mercantests/test_funcs.py b/tests/mercantests/test_funcs.py similarity index 100% rename from python/tests/mercantests/test_funcs.py rename to tests/mercantests/test_funcs.py diff --git a/python/tests/test_exports.py b/tests/test_exports.py similarity index 100% rename from python/tests/test_exports.py rename to tests/test_exports.py diff --git a/python/tests/test_mbtiles.py b/tests/test_mbtiles.py similarity index 100% rename from python/tests/test_mbtiles.py rename to tests/test_mbtiles.py diff --git a/python/tests/test_rust_cli.py b/tests/test_rust_cli.py similarity index 100% rename from python/tests/test_rust_cli.py rename to tests/test_rust_cli.py diff --git a/python/tests/test_tiletype.py b/tests/test_tiletype.py similarity index 92% rename from python/tests/test_tiletype.py rename to tests/test_tiletype.py index 8b6b2820..6aa03894 100644 --- a/python/tests/test_tiletype.py +++ b/tests/test_tiletype.py @@ -8,7 +8,18 @@ Extensions = Union[str, bool] PWD = Path(__file__).parent -REPO_ROOT = PWD.parent.parent + +def _repo_root() -> Path: + _root = PWD + for _i in range(5): + _root = _root.parent + if (_root / ".github").is_dir(): + return _root + raise RuntimeError("Could not find repo root") + +REPO_ROOT = _repo_root() + +# go up and find dir with sub dir ".github" def tiletype(buffer: bytes) -> Extensions: diff --git a/python/tests/test_utiles.py b/tests/test_utiles.py similarity index 100% rename from python/tests/test_utiles.py rename to tests/test_utiles.py diff --git a/python/tests/tutils.py b/tests/tutils.py similarity index 100% rename from python/tests/tutils.py rename to tests/tutils.py From 87e52f23a18fda4432b950a19274a54202606367 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 07:12:55 -0800 Subject: [PATCH 51/80] cli mod --- crates/utilesqlite/Cargo.lock | 8 +++ noxfile.py | 11 ++++ pyproject.toml | 107 ++++++++++++++++--------------- python/utiles/{ut.py => _cli.py} | 0 python/utiles/libutiles.pyi | 7 +- tests/test_rust_cli.py | 14 ++-- 6 files changed, 78 insertions(+), 69 deletions(-) rename python/utiles/{ut.py => _cli.py} (100%) diff --git a/crates/utilesqlite/Cargo.lock b/crates/utilesqlite/Cargo.lock index a32373a1..6464e641 100644 --- a/crates/utilesqlite/Cargo.lock +++ b/crates/utilesqlite/Cargo.lock @@ -35,6 +35,12 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + [[package]] name = "approx" version = "0.5.1" @@ -605,11 +611,13 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" name = "utiles" version = "0.0.1" dependencies = [ + "anyhow", "fast_hilbert", "geo-types", "geojson", "serde", "serde_json", + "thiserror", "tilejson", "tracing", ] diff --git a/noxfile.py b/noxfile.py index 4c6855a1..7d82d1fd 100644 --- a/noxfile.py +++ b/noxfile.py @@ -20,3 +20,14 @@ def test(session: nox.Session) -> None: session.install("pytest", "hypothesis", "pytest-cov", "pytest-benchmark") session.run("maturin", "develop", "--release", "--extras=test") session.run("pytest") + +@nox.session +def test_wheel(session: nox.Session) -> None: + """Run the unit and regular tests.""" + # install from dist... + + session.install("utiles", ) + session.install("maturin") + session.install("pytest", "hypothesis", "pytest-cov", "pytest-benchmark") + session.run("maturin", "build", "--release", "--extras=test") + session.run("pytest") diff --git a/pyproject.toml b/pyproject.toml index 304642e9..e1030de8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ dependencies = [ [project.scripts] utiles = "utiles.cli:cli" -ut = "utiles.ut:cli" +ut = "utiles._cli:cli" [project.entry-points."rasterio.rio_plugins"] utiles = "utiles.rio_plugin:rio_utiles" @@ -46,58 +46,6 @@ markers = [ "bench" ] -# ============================================================================= -[tool.hatch.envs.default] -dependencies = [ - "coverage[toml]>=6.5", - "mercantile", - "click", - "typing_extensions", - "tomli", - "hypothesis", - "maturin", - "pytest", - "pytest-benchmark", - "pytest-cov", -] - -[tool.hatch.envs.default.scripts] -test = "pytest {args:tests}" -test-cov = "coverage run -m pytest {args:tests}" -cov-report = [ - "- coverage combine", - "coverage report", -] -cov = [ - "test-cov", - "cov-report", -] - -[[tool.hatch.envs.all.matrix]] -python = ["3.8", "3.9", "3.10", "3.11", "3.12"] - -[tool.hatch.envs.lint] -detached = true -dependencies = [ - "black>=23.1.0", - "mypy>=1.0.0", - "ruff>=0.0.265", -] -[tool.hatch.envs.lint.scripts] -typing = "mypy --install-types --non-interactive {args:utiles tests}" -style = [ - "ruff {args:.}", - "black --check --diff {args:.}", -] -fmt = [ - "black {args:.}", - "ruff --fix {args:.}", - "style", -] -all = [ - "style", - "typing", -] [tool.black] target-version = ["py37"] @@ -223,3 +171,56 @@ exclude_lines = [ [tool.mypy] strict = true ignore_missing_imports = true + +# ============================================================================= +# [tool.hatch.envs.default] +# dependencies = [ +# "coverage[toml]>=6.5", +# "mercantile", +# "click", +# "typing_extensions", +# "tomli", +# "hypothesis", +# "maturin", +# "pytest", +# "pytest-benchmark", +# "pytest-cov", +# ] + +# [tool.hatch.envs.default.scripts] +# test = "pytest {args:tests}" +# test-cov = "coverage run -m pytest {args:tests}" +# cov-report = [ +# "- coverage combine", +# "coverage report", +# ] +# cov = [ +# "test-cov", +# "cov-report", +# ] + +# [[tool.hatch.envs.all.matrix]] +# python = ["3.8", "3.9", "3.10", "3.11", "3.12"] + +# [tool.hatch.envs.lint] +# detached = true +# dependencies = [ +# "black>=23.1.0", +# "mypy>=1.0.0", +# "ruff>=0.0.265", +# ] +# [tool.hatch.envs.lint.scripts] +# typing = "mypy --install-types --non-interactive {args:utiles tests}" +# style = [ +# "ruff {args:.}", +# "black --check --diff {args:.}", +# ] +# fmt = [ +# "black {args:.}", +# "ruff --fix {args:.}", +# "style", +# ] +# all = [ +# "style", +# "typing", +# ] diff --git a/python/utiles/ut.py b/python/utiles/_cli.py similarity index 100% rename from python/utiles/ut.py rename to python/utiles/_cli.py diff --git a/python/utiles/libutiles.pyi b/python/utiles/libutiles.pyi index e53a2416..7b08722d 100644 --- a/python/utiles/libutiles.pyi +++ b/python/utiles/libutiles.pyi @@ -11,10 +11,9 @@ from typing import ( Tuple, Union, overload, + Literal, ) -from typing_extensions import Literal, TypedDict - __version_lib__: str __build_profile__: Literal["debug", "release"] TupleIntInt = Tuple[int, int] @@ -29,10 +28,6 @@ TILETYPE_PNG: int TILETYPE_UNKNOWN: int TILETYPE_WEBP: int -class TileDict(TypedDict): - x: int - y: int - z: int class LngLat: lat: float diff --git a/tests/test_rust_cli.py b/tests/test_rust_cli.py index 5d23d3e2..279bf022 100644 --- a/tests/test_rust_cli.py +++ b/tests/test_rust_cli.py @@ -5,9 +5,6 @@ import json import pytest -from click.testing import CliRunner - -from utiles.ut import cli # def test_cli_shapes_failure() -> None: @@ -150,7 +147,7 @@ def _run_cli( ): _python = sys.executable res = run( - [_python, "-m", "utiles.ut", *args], + [_python, "-m", "utiles._cli", *args], input=input, capture_output=True, text=True, @@ -293,17 +290,14 @@ def test_cli_quadkey_from_mixed(self) -> None: @pytest.mark.skip(reason="not implemented") def test_cli_quadkey_failure(self) -> None: """Abort when an invalid quadkey is passed""" - runner = CliRunner() - with pytest.warns(DeprecationWarning): - result = runner.invoke(cli, ["quadkey", "lolwut"]) - assert result.exit_code == 2 - assert "lolwut" in result.output + result = _run_cli(["quadkey"], "lolwut") + assert result.returncode !=0 + assert "lolwut" in result.stdout class TestBoundingTile: def test_cli_bounding_tile_bad_bounds(self) -> None: """Bounds of len 3 are bad.""" - runner = CliRunner() result = _run_cli(["bounding-tile"], "[-105, 39.99, -104.99]") assert result.returncode != 0 From 8e962abaf92ce942f0c4424650dfbc8a4c93ba1f Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 07:40:48 -0800 Subject: [PATCH 52/80] format --- crates/utiles-cli/src/cli.rs | 6 ++++ noxfile.py | 7 +++-- python/utiles/__about__.py | 11 ++++++-- python/utiles/__init__.py | 2 +- python/utiles/__main__.py | 54 ++++++++++++++++++++++++++++++++++++ python/utiles/_cli.py | 1 + python/utiles/libutiles.pyi | 1 - tests/test_utiles.py | 6 ---- 8 files changed, 75 insertions(+), 13 deletions(-) diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index b5cad8d3..ef6f703c 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -408,6 +408,12 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { .filter(|l| l.as_ref().unwrap() != "\x1e"); let tiles = lines.map(|l| Tile::from_json(&l.unwrap())); for tile in tiles { + + let nup = tile.z as i32 - depth as i32; + if nup < 0 { + // error + panic!("depth must be less than or equal to tile zoom"); + } let parent = tile.parent(Option::from(depth - 1)); let rs = if seq { "\x1e\n" } else { "" }; println!("{}{}", rs, parent.json_arr()); diff --git a/noxfile.py b/noxfile.py index 7d82d1fd..63e5aa90 100644 --- a/noxfile.py +++ b/noxfile.py @@ -12,12 +12,14 @@ nox.options.sessions = ["test"] +def _session_install_test_deps(session: nox.Session) -> None: + session.install("pytest", "hypothesis", "pytest-cov", "pytest-benchmark", "tomli") @nox.session def test(session: nox.Session) -> None: """Run the unit and regular tests.""" session.install("maturin") - session.install("pytest", "hypothesis", "pytest-cov", "pytest-benchmark") + _session_install_test_deps(session) session.run("maturin", "develop", "--release", "--extras=test") session.run("pytest") @@ -25,9 +27,8 @@ def test(session: nox.Session) -> None: def test_wheel(session: nox.Session) -> None: """Run the unit and regular tests.""" # install from dist... - session.install("utiles", ) session.install("maturin") - session.install("pytest", "hypothesis", "pytest-cov", "pytest-benchmark") + _session_install_test_deps(session) session.run("maturin", "build", "--release", "--extras=test") session.run("pytest") diff --git a/python/utiles/__about__.py b/python/utiles/__about__.py index 0787384f..9398a2bd 100644 --- a/python/utiles/__about__.py +++ b/python/utiles/__about__.py @@ -1,8 +1,15 @@ """Package metadata/info""" -from ._utiles import __version_lib__ # noqa: TID252 +from utiles._utiles import __version_lib__, __build_profile__ # noqa: TID252 -__all__ = ("__title__", "__description__", "__pkgroot__", "__version__") +__all__ = ( + "__title__", + "__description__", + "__pkgroot__", + "__version__", + "__version_lib__", + "__build_profile__", +) __title__ = "utiles" __description__ = "utiles = utils + tiles + rust" __pkgroot__ = __file__.replace("__about__.py", "").rstrip("/\\") diff --git a/python/utiles/__init__.py b/python/utiles/__init__.py index 93571979..ed64fbbf 100644 --- a/python/utiles/__init__.py +++ b/python/utiles/__init__.py @@ -4,7 +4,7 @@ import math from typing import List, Sequence, Tuple, Union -from ._utiles import ( # noqa: TID252 +from utiles._utiles import ( # noqa: TID252 TILETYPE_GIF, TILETYPE_JPG, TILETYPE_JSON, diff --git a/python/utiles/__main__.py b/python/utiles/__main__.py index 84d2eee9..8a494732 100644 --- a/python/utiles/__main__.py +++ b/python/utiles/__main__.py @@ -9,11 +9,65 @@ from utiles.__about__ import __pkgroot__, __title__, __version__ +def _nbytes_str(nbytes: Union[int, float]) -> str: + """Format nbytesber of bytes to human readable form + + Ripped from `fmts` library which I wrote... + + ref: https://github.com/dynamic-graphics-inc/dgpy-libs/blob/main/libs/fmts/README.md + + Args: + nbytes: number of bytes + + Returns: + str: nbytesber of bytes formatted + + Raises: + ValueError: If given number of bytes is invalid/negative + + Examples: + >>> _nbytes_str(100) + '100.0 bytes' + >>> _nbytes_str(1000) + '1000.0 bytes' + >>> _nbytes_str(10000) + '9.8 KB' + >>> _nbytes_str(100000) + '97.7 KB' + >>> _nbytes_str(1000000) + '976.6 KB' + >>> _nbytes_str(10_000_000) + '9.5 MB' + >>> _nbytes_str(100_000_000) + '95.4 MB' + >>> _nbytes_str(1000000000) + '953.7 MB' + >>> _nbytes_str(10000000000) + '9.3 GB' + >>> _nbytes_str(100000000000) + '93.1 GB' + >>> _nbytes_str(1000000000000) + '931.3 GB' + >>> _nbytes_str(10000000000000) + '9.1 TB' + >>> _nbytes_str(100000000000000) + '90.9 TB' + + """ + for x in ["bytes", "KB", "MB", "GB", "TB"]: + if nbytes < 1024.0 or x == "TB": + _str = f"{nbytes:3.1f} {x}" + return _str + nbytes /= 1024.0 + raise ValueError(f"Invalid number of bytes: {nbytes}") # pragma: no cover + + def _utiles_ext_info() -> Dict[str, Union[str, int]]: size = os.path.getsize(libutiles.__file__) return { "abspath": os.path.abspath(libutiles.__file__), "fsize": size, + "fsize_str": _nbytes_str(size), } diff --git a/python/utiles/_cli.py b/python/utiles/_cli.py index 60d76daa..5a72d07f 100644 --- a/python/utiles/_cli.py +++ b/python/utiles/_cli.py @@ -37,5 +37,6 @@ def cli() -> None: logger.error(e) raise click.BadParameter(str(e)) + if __name__ == "__main__": cli() diff --git a/python/utiles/libutiles.pyi b/python/utiles/libutiles.pyi index 7b08722d..1418a0ad 100644 --- a/python/utiles/libutiles.pyi +++ b/python/utiles/libutiles.pyi @@ -28,7 +28,6 @@ TILETYPE_PNG: int TILETYPE_UNKNOWN: int TILETYPE_WEBP: int - class LngLat: lat: float lng: float diff --git a/tests/test_utiles.py b/tests/test_utiles.py index fd9c56cc..94298a6c 100644 --- a/tests/test_utiles.py +++ b/tests/test_utiles.py @@ -5,7 +5,6 @@ import pytest from pytest_benchmark.fixture import BenchmarkFixture -from typing_extensions import TypedDict import utiles from utiles import Tile @@ -27,11 +26,6 @@ def test_version() -> None: assert utiles.__version__ == pyproject_version -class TileDict(TypedDict): - x: int - y: int - z: int - @pytest.mark.parametrize( "tile,quadkey", From 61e2ed605a0091ecce930616e0a0797c0e5a0326 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 07:55:38 -0800 Subject: [PATCH 53/80] why is this installing the wrong stuff --- .github/workflows/dev.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 058e8379..bf286d3f 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -27,7 +27,7 @@ jobs: uses: PyO3/maturin-action@v1 with: target: ${{ matrix.target }} - args: --release --out dist --find-interpreter + args: --release --out dist --find-interpreter --verbose sccache: "true" manylinux: auto @@ -36,7 +36,7 @@ jobs: shell: bash run: | set -e - pip install utiles --find-links dist --force-reinstall -vvv + pip install utiles --no-index --find-links dist --force-reinstall pip install -r requirements/dev.txt pytest @@ -54,6 +54,6 @@ jobs: pip3 install -r requirements/dev.txt run: | set -e - pip3 install utiles --find-links dist --force-reinstall + pip3 install utiles --no-index --find-links dist --force-reinstall pip3 install -r requirements/dev.txt pytest From 9d7b2c81eab49860e7a4235bb6fae2e9d908a837 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 08:01:42 -0800 Subject: [PATCH 54/80] oy vey --- .github/workflows/dev.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index bf286d3f..d46fe328 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -36,6 +36,7 @@ jobs: shell: bash run: | set -e + pip install click pip install utiles --no-index --find-links dist --force-reinstall pip install -r requirements/dev.txt pytest @@ -54,6 +55,7 @@ jobs: pip3 install -r requirements/dev.txt run: | set -e + pip install click pip3 install utiles --no-index --find-links dist --force-reinstall pip3 install -r requirements/dev.txt pytest From beb693442d9d1ac76cc136cbc3b81afcc0e6a9b1 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 08:07:13 -0800 Subject: [PATCH 55/80] ok --- .github/workflows/dev.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index d46fe328..6abf8738 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -37,7 +37,7 @@ jobs: run: | set -e pip install click - pip install utiles --no-index --find-links dist --force-reinstall + pip install utiles --no-index --no-deps --find-links dist --force-reinstall pip install -r requirements/dev.txt pytest @@ -56,6 +56,6 @@ jobs: run: | set -e pip install click - pip3 install utiles --no-index --find-links dist --force-reinstall + pip3 install utiles --no-index --no-deps --find-links dist --force-reinstall pip3 install -r requirements/dev.txt pytest From 90be2e2ce9ebf257f4bd6c2f5eab8d0a0916fbe5 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 08:14:08 -0800 Subject: [PATCH 56/80] why is pytest picking up the wrong thing... --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e1030de8..d26d08ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,6 @@ module-name = "utiles._utiles" [tool.pytest.ini_options] testpaths = [ - "python", "tests" ] addopts = [ From 241e9d28fff940eace400cbbeb4b0bb1cdd5f600 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 08:23:09 -0800 Subject: [PATCH 57/80] shell false? --- tests/test_rust_cli.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_rust_cli.py b/tests/test_rust_cli.py index 279bf022..b2be87ec 100644 --- a/tests/test_rust_cli.py +++ b/tests/test_rust_cli.py @@ -151,8 +151,10 @@ def _run_cli( input=input, capture_output=True, text=True, - shell=True, + shell=False, ) + print(res.stdout) + print(res.stderr) return res From c9e023a2b3409e00162d909a4e33ae5c176bfded Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 08:29:39 -0800 Subject: [PATCH 58/80] finally sorted that --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d26d08ea..6c656d9b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,11 +47,11 @@ markers = [ [tool.black] -target-version = ["py37"] +target-version = ["py38"] line-length = 88 [tool.ruff] -target-version = "py37" +target-version = "py38" line-length = 88 select = [ "A", From 802893f93e260681e8f57678e918b38af682186e Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 08:35:48 -0800 Subject: [PATCH 59/80] utiles pyi --- python/utiles/_utiles.pyi | 239 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 python/utiles/_utiles.pyi diff --git a/python/utiles/_utiles.pyi b/python/utiles/_utiles.pyi new file mode 100644 index 00000000..e7d11f64 --- /dev/null +++ b/python/utiles/_utiles.pyi @@ -0,0 +1,239 @@ +from __future__ import annotations + +from typing import ( + Any, + Collection, + Iterable, + Iterator, + Literal, + Optional, + Sequence, + Set, + Tuple, + TypedDict, + Union, + overload, +) + +__version_lib__: str +__build_profile__: Literal["debug", "release"] +TupleIntInt = Tuple[int, int] +TupleIntIntInt = Tuple[int, int, int] + +TILETYPE_GIF: int +TILETYPE_JPG: int +TILETYPE_JSON: int +TILETYPE_PBF: int +TILETYPE_PBFGZ: int +TILETYPE_PNG: int +TILETYPE_UNKNOWN: int +TILETYPE_WEBP: int + +class TileDict(TypedDict): + x: int + y: int + z: int + +class LngLat: + lat: float + lng: float + + @classmethod + def __init__(cls, lng: float, lat: float) -> None: ... + @classmethod + def from_tile(cls, tile: Tile) -> Any: ... + def members(self) -> tuple[float, float]: ... + def __eq__(self, other: Any) -> bool: ... + def __ge__(self, other: Any) -> bool: ... + def __getitem__(self, index: int) -> float: ... + def __gt__(self, other: Any) -> bool: ... + def __le__(self, other: Any) -> bool: ... + def __len__(self) -> int: ... + def __lt__(self, other: Any) -> bool: ... + def __ne__(self, other: Any) -> bool: ... + def __hash__(self) -> int: ... + def __iter__(self) -> Iterator[float]: ... + +class Bbox: + right: float + top: float + bottom: float + left: float + + def __init__( + self, right: float, top: float, left: float, bottom: float + ) -> None: ... + @classmethod + def from_tile(cls, tile: Tile) -> Bbox: ... + def members(self) -> Tuple[float, float, float, float]: ... + @overload + def __getitem__(self, index: int) -> float: ... + @overload + def __getitem__(self, index: slice) -> tuple[float, ...]: ... + def __len__(self) -> int: ... + def __hash__(self) -> int: ... + def __iter__(self) -> Iterator[float]: ... + def tuple(self) -> Tuple[float, float, float, float]: ... + def __eq__(self, other: Any) -> bool: ... + def __ge__(self, other: Any) -> bool: ... + +class LngLatBbox: + east: float + north: float + south: float + west: float + + def __init__( + self, east: float, north: float, west: float, south: float + ) -> None: ... + @classmethod + def from_tile(cls, tile: Tile) -> LngLatBbox: ... + def members(self) -> Tuple[float, float, float, float]: ... + @overload + def __getitem__(self, index: int) -> float: ... + @overload + def __getitem__(self, index: slice) -> tuple[float, ...]: ... + def __len__(self) -> int: ... + def __hash__(self) -> int: ... + def __iter__(self) -> Iterator[float]: ... + def tuple(self) -> Tuple[float, float, float, float]: ... + def __eq__(self, other: Any) -> bool: ... + def __ge__(self, other: Any) -> bool: ... + +class Tile: + def __init__(self, x: int, y: int, z: int) -> None: ... + def bounds(self) -> LngLatBbox: ... + def children(self) -> list[Tile]: ... + def flipy(self) -> Tile: ... + @property + def x(self) -> int: ... + @property + def y(self) -> int: ... + @property + def z(self) -> int: ... + @classmethod + def from_lnglat_zoom(cls, lng: float, lat: float, zoom: int) -> Tile: ... + @classmethod + def from_quadkey(cls, quadkey: str) -> Tile: ... + @classmethod + def from_pmtileid(cls, pmtileid: int) -> Tile: ... + def pmtileid(self) -> int: ... + def parent_pmtileid(self) -> int: ... + def json(self, obj: bool) -> str: ... + def ll(self) -> LngLat: ... + def lr(self) -> LngLat: ... + def members(self) -> tuple[int, int, int]: ... + def neighbors(self) -> list[Tile]: ... + def parent(self) -> Tile: ... + def siblings(self) -> list[Tile]: ... + def ul(self) -> LngLat: ... + def ur(self) -> LngLat: ... + def __eq__(self, other: Any) -> bool: ... + def __ge__(self, other: Tile | tuple[int, int, int]) -> bool: ... + def qk(self) -> str: ... + def quadkey(self) -> str: ... + def fmt_zxy(self, sep: str | None) -> str: ... + def fmt_zxy_ext(self, ext: str, sep: str | None) -> str: ... + # def __getitem__(self, index: int) -> bool: ... + + @overload + def __getitem__(self, index: int) -> int: ... + @overload + def __getitem__(self, index: slice) -> tuple[int, ...]: ... + def __gt__(self, other: Tile) -> bool: ... + def __invert__(self) -> Tile: ... + def __le__(self, other: Tile) -> bool: ... + def __len__(self) -> int: ... + def __lt__(self, other: Tile) -> bool: ... + def __ne__(self, other: Any) -> bool: ... + def __hash__(self) -> int: ... + def tuple(self) -> tuple[int, int, int]: ... + def __iter__(self) -> Iterator[int]: ... + def valid(self) -> bool: ... + def asdict(self) -> TileDict: ... + def feature( + self, + fid: str | None = ..., + props: dict[Any, Any] | None = ..., + projected: str | None = ..., + buffer: float | None = ..., + precision: int | None = ..., + ) -> Any: ... + +TileLike = Union[Tile, Tuple[int, int, int], int] +# Bbox = Tuple[float, float, float, float] + +def _parse_tile_arg(*args: TileLike) -> Tile: ... +def _xy( + lng: float, lat: float, truncate: Optional[bool] = None +) -> Tuple[float, float]: ... +def bounding_tile( + *bbox: Bbox | tuple[float, float] | float, truncate: bool = ... +) -> Tile: ... +def bounds(tile: TileLike) -> LngLatBbox: ... +def children(*tile: TileLike, zoom: int | None = ...) -> list[Tile]: ... +def feature( + tile: TileLike, + fid: str | None = ..., + props: dict[Any, Any] | None = ..., + projected: str | None = ..., + buffer: float | None = ..., + precision: int | None = ..., +) -> Any: ... +def from_tuple(tile: Tuple[int, int, int]) -> Tile: ... +def lnglat(lng: float, lat: float, truncate: Optional[bool] = None) -> LngLat: ... +def minmax(zoom: int) -> tuple[int, int]: ... +def neighbors(*tile: TileLike) -> list[Tile]: ... +def parent(*tile: TileLike, zoom: int | None = ...) -> Tile | None: ... +def parse_tile_arg(*args: TileLike) -> Tile: ... +def quadkey(*tile: TileLike) -> str: ... +def quadkey2xyz(qk: str) -> Tile: ... +def quadkey_to_tile(qk: str) -> Tile: ... +def tile( + lng: float, lat: float, zoom: int, truncate: Optional[bool] = None +) -> Tile: ... +def tiles( + west: float, + south: float, + east: float, + north: float, + zooms: list[int] | tuple[int, ...] | int, + truncate: bool = ..., +) -> Collection[Tile]: ... +def tiles_list( + west: float, + south: float, + east: float, + north: float, + zooms: list[int] | tuple[int, ...] | int, + truncate: bool = ..., +) -> list[Tile]: ... +def tiles_count( + west: float, + south: float, + east: float, + north: float, + zooms: list[int] | tuple[int, ...] | int, + truncate: bool = ..., +) -> int: ... +def tiletype(buf: bytes) -> int: ... +def tiletype2headers(tiletype_int: int) -> list[tuple[str, str]]: ... +def tiletype_str(buf: bytes) -> str: ... +def ul(*tile: TileLike) -> LngLat: ... +def xy( + lng: float, lat: float, truncate: Optional[bool] = None +) -> Tuple[float, float]: ... +def xy_bounds(*tile: TileLike) -> Bbox: ... +def xyz2quadkey(x: int, y: int, z: int) -> Any: ... +def xyz(x: int, y: int, z: int) -> Tile: ... +def parse_tiles(tilesish: Sequence[TileLike]) -> list[Tile]: ... +def truncate_lnglat(lng: float, lat: float) -> Tuple[float, float]: ... +def simplify(tiles: Sequence[TileLike]) -> Set[Tile]: ... +def coords(obj: Any) -> Iterable[Tuple[float, float]]: ... +def _coords(obj: Any) -> Iterable[Tuple[float, float]]: ... +def geojson_bounds(obj: Any) -> LngLatBbox: ... +def pmtileid(*tile: TileLike) -> int: ... +def from_pmtileid(pmtileid: int) -> Tile: ... + +# CLI +def ut_cli(args: list[str]) -> None: ... From 34f3835b1251a054a3bd36d92d920a06a90ec761 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 08:36:00 -0800 Subject: [PATCH 60/80] lib utiles pyi gone --- python/utiles/__about__.py | 2 +- python/utiles/__init__.py | 2 +- python/utiles/__main__.py | 3 +- python/utiles/_cli.py | 3 +- python/utiles/libutiles.pyi | 233 ------------------------------------ tests/test_rust_cli.py | 19 +-- tests/test_tiletype.py | 3 +- 7 files changed, 10 insertions(+), 255 deletions(-) delete mode 100644 python/utiles/libutiles.pyi diff --git a/python/utiles/__about__.py b/python/utiles/__about__.py index 9398a2bd..82e2e771 100644 --- a/python/utiles/__about__.py +++ b/python/utiles/__about__.py @@ -1,6 +1,6 @@ """Package metadata/info""" -from utiles._utiles import __version_lib__, __build_profile__ # noqa: TID252 +from utiles._utiles import __build_profile__, __version_lib__ __all__ = ( "__title__", diff --git a/python/utiles/__init__.py b/python/utiles/__init__.py index ed64fbbf..c90a6204 100644 --- a/python/utiles/__init__.py +++ b/python/utiles/__init__.py @@ -4,7 +4,7 @@ import math from typing import List, Sequence, Tuple, Union -from utiles._utiles import ( # noqa: TID252 +from utiles._utiles import ( TILETYPE_GIF, TILETYPE_JPG, TILETYPE_JSON, diff --git a/python/utiles/__main__.py b/python/utiles/__main__.py index 8a494732..8d0155e2 100644 --- a/python/utiles/__main__.py +++ b/python/utiles/__main__.py @@ -59,7 +59,8 @@ def _nbytes_str(nbytes: Union[int, float]) -> str: _str = f"{nbytes:3.1f} {x}" return _str nbytes /= 1024.0 - raise ValueError(f"Invalid number of bytes: {nbytes}") # pragma: no cover + msg = f"Invalid number of bytes: {nbytes}" + raise ValueError(msg) # pragma: no cover def _utiles_ext_info() -> Dict[str, Union[str, int]]: diff --git a/python/utiles/_cli.py b/python/utiles/_cli.py index 5a72d07f..94d59518 100644 --- a/python/utiles/_cli.py +++ b/python/utiles/_cli.py @@ -5,7 +5,8 @@ import sys import click -from utiles import ut_cli, __version__ + +from utiles import __version__, ut_cli logger = logging.getLogger(__name__) diff --git a/python/utiles/libutiles.pyi b/python/utiles/libutiles.pyi deleted file mode 100644 index 1418a0ad..00000000 --- a/python/utiles/libutiles.pyi +++ /dev/null @@ -1,233 +0,0 @@ -from __future__ import annotations - -from typing import ( - Any, - Collection, - Iterable, - Iterator, - Optional, - Sequence, - Set, - Tuple, - Union, - overload, - Literal, -) - -__version_lib__: str -__build_profile__: Literal["debug", "release"] -TupleIntInt = Tuple[int, int] -TupleIntIntInt = Tuple[int, int, int] - -TILETYPE_GIF: int -TILETYPE_JPG: int -TILETYPE_JSON: int -TILETYPE_PBF: int -TILETYPE_PBFGZ: int -TILETYPE_PNG: int -TILETYPE_UNKNOWN: int -TILETYPE_WEBP: int - -class LngLat: - lat: float - lng: float - - @classmethod - def __init__(cls, lng: float, lat: float) -> None: ... - @classmethod - def from_tile(cls, tile: Tile) -> Any: ... - def members(self) -> tuple[float, float]: ... - def __eq__(self, other: Any) -> bool: ... - def __ge__(self, other: Any) -> bool: ... - def __getitem__(self, index: int) -> float: ... - def __gt__(self, other: Any) -> bool: ... - def __le__(self, other: Any) -> bool: ... - def __len__(self) -> int: ... - def __lt__(self, other: Any) -> bool: ... - def __ne__(self, other: Any) -> bool: ... - def __hash__(self) -> int: ... - def __iter__(self) -> Iterator[float]: ... - -class Bbox: - right: float - top: float - bottom: float - left: float - - def __init__( - self, right: float, top: float, left: float, bottom: float - ) -> None: ... - @classmethod - def from_tile(cls, tile: Tile) -> Bbox: ... - def members(self) -> Tuple[float, float, float, float]: ... - @overload - def __getitem__(self, index: int) -> float: ... - @overload - def __getitem__(self, index: slice) -> tuple[float, ...]: ... - def __len__(self) -> int: ... - def __hash__(self) -> int: ... - def __iter__(self) -> Iterator[float]: ... - def tuple(self) -> Tuple[float, float, float, float]: ... - def __eq__(self, other: Any) -> bool: ... - def __ge__(self, other: Any) -> bool: ... - -class LngLatBbox: - east: float - north: float - south: float - west: float - - def __init__( - self, east: float, north: float, west: float, south: float - ) -> None: ... - @classmethod - def from_tile(cls, tile: Tile) -> LngLatBbox: ... - def members(self) -> Tuple[float, float, float, float]: ... - @overload - def __getitem__(self, index: int) -> float: ... - @overload - def __getitem__(self, index: slice) -> tuple[float, ...]: ... - def __len__(self) -> int: ... - def __hash__(self) -> int: ... - def __iter__(self) -> Iterator[float]: ... - def tuple(self) -> Tuple[float, float, float, float]: ... - def __eq__(self, other: Any) -> bool: ... - def __ge__(self, other: Any) -> bool: ... - -class Tile: - def __init__(self, x: int, y: int, z: int) -> None: ... - def bounds(self) -> LngLatBbox: ... - def children(self) -> list[Tile]: ... - def flipy(self) -> Tile: ... - @property - def x(self) -> int: ... - @property - def y(self) -> int: ... - @property - def z(self) -> int: ... - @classmethod - def from_lnglat_zoom(cls, lng: float, lat: float, zoom: int) -> Tile: ... - @classmethod - def from_quadkey(cls, quadkey: str) -> Tile: ... - @classmethod - def from_pmtileid(cls, pmtileid: int) -> Tile: ... - def pmtileid(self) -> int: ... - def parent_pmtileid(self) -> int: ... - def json(self, obj: bool) -> str: ... - def ll(self) -> LngLat: ... - def lr(self) -> LngLat: ... - def members(self) -> tuple[int, int, int]: ... - def neighbors(self) -> list[Tile]: ... - def parent(self) -> Tile: ... - def siblings(self) -> list[Tile]: ... - def ul(self) -> LngLat: ... - def ur(self) -> LngLat: ... - def __eq__(self, other: Any) -> bool: ... - def __ge__(self, other: Tile | tuple[int, int, int]) -> bool: ... - def qk(self) -> str: ... - def quadkey(self) -> str: ... - def fmt_zxy(self, sep: str | None) -> str: ... - def fmt_zxy_ext(self, ext: str, sep: str | None) -> str: ... - # def __getitem__(self, index: int) -> bool: ... - - @overload - def __getitem__(self, index: int) -> int: ... - @overload - def __getitem__(self, index: slice) -> tuple[int, ...]: ... - def __gt__(self, other: Tile) -> bool: ... - def __invert__(self) -> Tile: ... - def __le__(self, other: Tile) -> bool: ... - def __len__(self) -> int: ... - def __lt__(self, other: Tile) -> bool: ... - def __ne__(self, other: Any) -> bool: ... - def __hash__(self) -> int: ... - def tuple(self) -> tuple[int, int, int]: ... - def __iter__(self) -> Iterator[int]: ... - def valid(self) -> bool: ... - def asdict(self) -> TileDict: ... - def feature( - self, - fid: str | None = ..., - props: dict[Any, Any] | None = ..., - projected: str | None = ..., - buffer: float | None = ..., - precision: int | None = ..., - ) -> Any: ... - -TileLike = Union[Tile, Tuple[int, int, int], int] -# Bbox = Tuple[float, float, float, float] - -def _parse_tile_arg(*args: TileLike) -> Tile: ... -def _xy( - lng: float, lat: float, truncate: Optional[bool] = None -) -> Tuple[float, float]: ... -def bounding_tile( - *bbox: Bbox | tuple[float, float] | float, truncate: bool = ... -) -> Tile: ... -def bounds(tile: TileLike) -> LngLatBbox: ... -def children(*tile: TileLike, zoom: int | None = ...) -> list[Tile]: ... -def feature( - tile: TileLike, - fid: str | None = ..., - props: dict[Any, Any] | None = ..., - projected: str | None = ..., - buffer: float | None = ..., - precision: int | None = ..., -) -> Any: ... -def from_tuple(tile: Tuple[int, int, int]) -> Tile: ... -def lnglat(lng: float, lat: float, truncate: Optional[bool] = None) -> LngLat: ... -def minmax(zoom: int) -> tuple[int, int]: ... -def neighbors(*tile: TileLike) -> list[Tile]: ... -def parent(*tile: TileLike, zoom: int | None = ...) -> Tile | None: ... -def parse_tile_arg(*args: TileLike) -> Tile: ... -def quadkey(*tile: TileLike) -> str: ... -def quadkey2xyz(qk: str) -> Tile: ... -def quadkey_to_tile(qk: str) -> Tile: ... -def tile( - lng: float, lat: float, zoom: int, truncate: Optional[bool] = None -) -> Tile: ... -def tiles( - west: float, - south: float, - east: float, - north: float, - zooms: list[int] | tuple[int, ...] | int, - truncate: bool = ..., -) -> Collection[Tile]: ... -def tiles_list( - west: float, - south: float, - east: float, - north: float, - zooms: list[int] | tuple[int, ...] | int, - truncate: bool = ..., -) -> list[Tile]: ... -def tiles_count( - west: float, - south: float, - east: float, - north: float, - zooms: list[int] | tuple[int, ...] | int, - truncate: bool = ..., -) -> int: ... -def tiletype(buf: bytes) -> int: ... -def tiletype2headers(tiletype_int: int) -> list[tuple[str, str]]: ... -def tiletype_str(buf: bytes) -> str: ... -def ul(*tile: TileLike) -> LngLat: ... -def xy( - lng: float, lat: float, truncate: Optional[bool] = None -) -> Tuple[float, float]: ... -def xy_bounds(*tile: TileLike) -> Bbox: ... -def xyz2quadkey(x: int, y: int, z: int) -> Any: ... -def xyz(x: int, y: int, z: int) -> Tile: ... -def parse_tiles(tilesish: Sequence[TileLike]) -> list[Tile]: ... -def truncate_lnglat(lng: float, lat: float) -> Tuple[float, float]: ... -def simplify(tiles: Sequence[TileLike]) -> Set[Tile]: ... -def coords(obj: Any) -> Iterable[Tuple[float, float]]: ... -def _coords(obj: Any) -> Iterable[Tuple[float, float]]: ... -def geojson_bounds(obj: Any) -> LngLatBbox: ... -def pmtileid(*tile: TileLike) -> int: ... -def from_pmtileid(pmtileid: int) -> Tile: ... - -# CLI -def ut_cli(args: list[str]) -> None: ... diff --git a/tests/test_rust_cli.py b/tests/test_rust_cli.py index b2be87ec..96da499a 100644 --- a/tests/test_rust_cli.py +++ b/tests/test_rust_cli.py @@ -1,12 +1,11 @@ """Utiles rust cli tests""" +import json import sys -from subprocess import run from json import dumps as stringify -import json +from subprocess import run import pytest - # def test_cli_shapes_failure() -> None: # runner = CliRunner() # result = runner.invoke(cli, ["shapes"], "0") @@ -153,8 +152,6 @@ def _run_cli( text=True, shell=False, ) - print(res.stdout) - print(res.stderr) return res @@ -163,13 +160,6 @@ def test_rust_cli_help() -> None: assert "(rust)" in res.stdout -# -# def test_rust_cli_help() -> None: -# runner = CliRunner() -# res = runner.invoke(cli, ["--help"]) -# print(res) -# assert False - class TestTiles: def test_cli_tiles_bad_bounds(self) -> None: @@ -197,14 +187,11 @@ def test_cli_tiles_multi_bounds_seq(self) -> None: ["--debug", "tiles", "14"], "\x1e\n[-105, 39.99, -104.99, 40]\n\x1e\n[-105, 39.99, -104.99, 40]", ) - print(result.stdout) - print(result.stderr) assert result.returncode == 0 assert len(result.stdout.strip().split("\n")) == 4 def test_rust_cli_tiles_seq(self) -> None: result = _run_cli(["tiles", "14", "--seq", "[14.0859, 5.798]"]) - print(result) # runner = CliRunner() # result = runner.invoke(cli, ["tiles", "14", "--seq", # "[14.0859, 5.798]" @@ -215,7 +202,6 @@ def test_rust_cli_tiles_seq(self) -> None: def test_cli_tiles_points(self) -> None: result = _run_cli(["tiles", "14"], "[14.0859, 5.798]") - print(result) # j # runner = CliRunner() # result = runner.invoke(cli, ["tiles", "14"], "[14.0859, 5.798]") @@ -379,7 +365,6 @@ class TestNeighbors: def test_cli_neighbors(self) -> None: result = _run_cli(["neighbors"], "[243, 166, 9]") assert result.returncode == 0 - print(result.stdout) tiles_lines = result.stdout.strip().split("\n") tiles = [tuple(json.loads(t)) for t in tiles_lines] diff --git a/tests/test_tiletype.py b/tests/test_tiletype.py index 6aa03894..e2b2839f 100644 --- a/tests/test_tiletype.py +++ b/tests/test_tiletype.py @@ -15,7 +15,8 @@ def _repo_root() -> Path: _root = _root.parent if (_root / ".github").is_dir(): return _root - raise RuntimeError("Could not find repo root") + msg = "Could not find repo root" + raise RuntimeError(msg) REPO_ROOT = _repo_root() From 36e03f686a35617cdcaba1476fe028eda3597ecb Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 08:43:25 -0800 Subject: [PATCH 61/80] awesome --- python/utiles/_cli.py | 5 +- tests/test_mbtiles.py | 2 +- tests/test_rust_cli.py | 282 +++++++++++++++++++++-------------------- tests/test_tiletype.py | 5 +- tests/test_utiles.py | 1 - 5 files changed, 150 insertions(+), 145 deletions(-) diff --git a/python/utiles/_cli.py b/python/utiles/_cli.py index 94d59518..daf1efec 100644 --- a/python/utiles/_cli.py +++ b/python/utiles/_cli.py @@ -32,11 +32,10 @@ def cli() -> None: """Execute the main utiles command""" args = ["ut", *sys.argv[1:]] try: - res = ut_cli(args) - click.echo(res, err=True) + ut_cli(args) except Exception as e: logger.error(e) - raise click.BadParameter(str(e)) + raise click.BadParameter(str(e)) from e if __name__ == "__main__": diff --git a/tests/test_mbtiles.py b/tests/test_mbtiles.py index 0a9cc99d..5f08823c 100644 --- a/tests/test_mbtiles.py +++ b/tests/test_mbtiles.py @@ -1,2 +1,2 @@ -def test_todo(): +def test_todo() -> None: assert True diff --git a/tests/test_rust_cli.py b/tests/test_rust_cli.py index 96da499a..09138f53 100644 --- a/tests/test_rust_cli.py +++ b/tests/test_rust_cli.py @@ -2,155 +2,23 @@ import json import sys from json import dumps as stringify -from subprocess import run +from subprocess import CompletedProcess, run import pytest -# def test_cli_shapes_failure() -> None: -# runner = CliRunner() -# result = runner.invoke(cli, ["shapes"], "0") -# assert result.exit_code == 2 - - -# def test_cli_shapes() -> None: -# runner = CliRunner() -# result = runner.invoke(cli, ["shapes", "--precision", "6"], "[106, 193, 9]") -# assert result.exit_code == 0 -# assert ( -# result.output -# == '{"bbox": [-105.46875, 39.909736, -104.765625, 40.446947], "geometry": {"coordinates": [[[-105.46875, 39.909736], [-105.46875, 40.446947], [-104.765625, 40.446947], [-104.765625, 39.909736], [-105.46875, 39.909736]]], "type": "Polygon"}, "id": "(106, 193, 9)", "properties": {"title": "XYZ tile (106, 193, 9)"}, "type": "Feature"}\n' -# ) - -# def test_cli_shapes_arg() -> None: -# runner = CliRunner() -# result = runner.invoke(cli, ["shapes", "[106, 193, 9]", "--precision", "6"]) -# assert result.exit_code == 0 -# result_output_json = json.loads(result.output) - -# # '{"bbox": [-105.46875, 39.909736, -104.765625, 40.446947], "geometry": {"coordinates": [[[-105.46875, 39.909736], [-105.46875, 40.446947], [-104.765625, 40.446947], [-104.765625, 39.909736], [-105.46875, 39.909736]]], "type": "Polygon"}, "id": "(106, 193, 9)", "properties": {"title": "XYZ tile (106, 193, 9)"}, "type": "Feature"}\n' -# expected_dict = { -# "bbox": [-105.46875, 39.909736, -104.765625, 40.446947], -# "geometry": { -# "coordinates": [ -# [ -# [-105.46875, 39.909736], -# [-105.46875, 40.446947], -# [-104.765625, 40.446947], -# [-104.765625, 39.909736], -# [-105.46875, 39.909736], -# ] -# ], -# "type": "Polygon", -# }, -# "id": "(106, 193, 9)", -# "properties": {"title": "XYZ tile (106, 193, 9)"}, -# "type": "Feature", -# } - -# assert result_output_json == expected_dict - - -# def test_cli_shapes_buffer() -> None: -# runner = CliRunner() -# result = runner.invoke( -# cli, ["shapes", "[106, 193, 9]", "--buffer", "1.0", "--precision", "6"] -# ) -# assert result.exit_code == 0 -# assert ( -# result.output -# == '{"bbox": [-106.46875, 38.909736, -103.765625, 41.446947], "geometry": {"coordinates": [[[-106.46875, 38.909736], [-106.46875, 41.446947], [-103.765625, 41.446947], [-103.765625, 38.909736], [-106.46875, 38.909736]]], "type": "Polygon"}, "id": "(106, 193, 9)", "properties": {"title": "XYZ tile (106, 193, 9)"}, "type": "Feature"}\n' -# ) - - -# def test_cli_shapes_compact() -> None: -# """Output is compact.""" -# runner = CliRunner() -# result = runner.invoke(cli, ["shapes", "--compact"], "[106, 193, 9]") -# assert result.exit_code == 0 -# assert '"type":"Feature"' in result.output.strip() - - -# def test_cli_shapes_indentation() -> None: -# """Output is indented.""" -# runner = CliRunner() -# result = runner.invoke(cli, ["shapes", "--indent", "8"], "[106, 193, 9]") -# assert result.exit_code == 0 -# assert ' "type": "Feature"' in result.output.strip() - - -# def test_cli_shapes_collect() -> None: -# """Shapes are collected into a feature collection.""" -# runner = CliRunner() -# result = runner.invoke(cli, ["shapes", "--collect", "--feature"], "[106, 193, 9]") -# assert result.exit_code == 0 -# assert "FeatureCollection" in result.output - - -# def test_cli_shapes_extents() -> None: -# runner = CliRunner() -# result = runner.invoke( -# cli, ["shapes", "[106, 193, 9]", "--extents", "--mercator", "--precision", "3"] -# ) -# assert result.exit_code == 0 -# assert result.output == "-11740727.545 4852834.052 -11662456.028 4931105.569\n" - - -# def test_cli_shapes_bbox() -> None: -# """JSON text sequences of bboxes are output.""" -# runner = CliRunner() -# result = runner.invoke( -# cli, -# [ -# "shapes", -# "[106, 193, 9]", -# "--seq", -# "--bbox", -# "--mercator", -# "--precision", -# "3", -# ], -# ) -# assert result.exit_code == 0 -# assert ( -# result.output -# == "\x1e\n[-11740727.545, 4852834.052, -11662456.028, 4931105.569]\n" -# ) - - -# def test_cli_shapes_props_fid() -> None: -# runner = CliRunner() -# result = runner.invoke( -# cli, -# [ -# "shapes", -# '{"tile": [106, 193, 9], "properties": {"title": "foo"}, "id": "42"}', -# ], -# ) -# assert result.exit_code == 0 -# assert '"title": "foo"' in result.output -# assert '"id": "42"' in result.output - - -# def test_cli_strict_overlap_contain() -> None: -# runner = CliRunner() -# result1 = runner.invoke(cli, ["shapes"], "[2331,1185,12]") -# assert result1.exit_code == 0 -# result2 = runner.invoke(cli, ["tiles", "12"], result1.output) -# assert result2.exit_code == 0 -# assert result2.output == "[2331, 1185, 12]\n" - def _run_cli( args: list[str] | None, input: str | None = None, -): +) -> CompletedProcess[str]: _python = sys.executable + _args = args or [] res = run( - [_python, "-m", "utiles._cli", *args], + [_python, "-m", "utiles._cli", *_args], input=input, capture_output=True, text=True, - shell=False, + shell=False, # noqa: S603 ) return res @@ -160,7 +28,6 @@ def test_rust_cli_help() -> None: assert "(rust)" in res.stdout - class TestTiles: def test_cli_tiles_bad_bounds(self) -> None: """Bounds of len 3 are bad.""" @@ -279,7 +146,7 @@ def test_cli_quadkey_from_mixed(self) -> None: def test_cli_quadkey_failure(self) -> None: """Abort when an invalid quadkey is passed""" result = _run_cli(["quadkey"], "lolwut") - assert result.returncode !=0 + assert result.returncode != 0 assert "lolwut" in result.stdout @@ -415,3 +282,140 @@ def test_cli_children(self) -> None: result.stdout == "[486, 332, 10]\n[487, 332, 10]\n[487, 333, 10]\n[486, 333, 10]\n" ) + +# =================== +# SHAPES TESTS (TODO) +# =================== + +# def test_cli_shapes_failure() -> None: +# runner = CliRunner() +# result = runner.invoke(cli, ["shapes"], "0") +# assert result.exit_code == 2 + + +# def test_cli_shapes() -> None: +# runner = CliRunner() +# result = runner.invoke(cli, ["shapes", "--precision", "6"], "[106, 193, 9]") +# assert result.exit_code == 0 +# assert ( +# result.output +# == '{"bbox": [-105.46875, 39.909736, -104.765625, 40.446947], "geometry": {"coordinates": [[[-105.46875, 39.909736], [-105.46875, 40.446947], [-104.765625, 40.446947], [-104.765625, 39.909736], [-105.46875, 39.909736]]], "type": "Polygon"}, "id": "(106, 193, 9)", "properties": {"title": "XYZ tile (106, 193, 9)"}, "type": "Feature"}\n' +# ) + +# def test_cli_shapes_arg() -> None: +# runner = CliRunner() +# result = runner.invoke(cli, ["shapes", "[106, 193, 9]", "--precision", "6"]) +# assert result.exit_code == 0 +# result_output_json = json.loads(result.output) + +# # '{"bbox": [-105.46875, 39.909736, -104.765625, 40.446947], "geometry": {"coordinates": [[[-105.46875, 39.909736], [-105.46875, 40.446947], [-104.765625, 40.446947], [-104.765625, 39.909736], [-105.46875, 39.909736]]], "type": "Polygon"}, "id": "(106, 193, 9)", "properties": {"title": "XYZ tile (106, 193, 9)"}, "type": "Feature"}\n' +# expected_dict = { +# "bbox": [-105.46875, 39.909736, -104.765625, 40.446947], +# "geometry": { +# "coordinates": [ +# [ +# [-105.46875, 39.909736], +# [-105.46875, 40.446947], +# [-104.765625, 40.446947], +# [-104.765625, 39.909736], +# [-105.46875, 39.909736], +# ] +# ], +# "type": "Polygon", +# }, +# "id": "(106, 193, 9)", +# "properties": {"title": "XYZ tile (106, 193, 9)"}, +# "type": "Feature", +# } + +# assert result_output_json == expected_dict + + +# def test_cli_shapes_buffer() -> None: +# runner = CliRunner() +# result = runner.invoke( +# cli, ["shapes", "[106, 193, 9]", "--buffer", "1.0", "--precision", "6"] +# ) +# assert result.exit_code == 0 +# assert ( +# result.output +# == '{"bbox": [-106.46875, 38.909736, -103.765625, 41.446947], "geometry": {"coordinates": [[[-106.46875, 38.909736], [-106.46875, 41.446947], [-103.765625, 41.446947], [-103.765625, 38.909736], [-106.46875, 38.909736]]], "type": "Polygon"}, "id": "(106, 193, 9)", "properties": {"title": "XYZ tile (106, 193, 9)"}, "type": "Feature"}\n' +# ) + + +# def test_cli_shapes_compact() -> None: +# """Output is compact.""" +# runner = CliRunner() +# result = runner.invoke(cli, ["shapes", "--compact"], "[106, 193, 9]") +# assert result.exit_code == 0 +# assert '"type":"Feature"' in result.output.strip() + + +# def test_cli_shapes_indentation() -> None: +# """Output is indented.""" +# runner = CliRunner() +# result = runner.invoke(cli, ["shapes", "--indent", "8"], "[106, 193, 9]") +# assert result.exit_code == 0 +# assert ' "type": "Feature"' in result.output.strip() + + +# def test_cli_shapes_collect() -> None: +# """Shapes are collected into a feature collection.""" +# runner = CliRunner() +# result = runner.invoke(cli, ["shapes", "--collect", "--feature"], "[106, 193, 9]") +# assert result.exit_code == 0 +# assert "FeatureCollection" in result.output + + +# def test_cli_shapes_extents() -> None: +# runner = CliRunner() +# result = runner.invoke( +# cli, ["shapes", "[106, 193, 9]", "--extents", "--mercator", "--precision", "3"] +# ) +# assert result.exit_code == 0 +# assert result.output == "-11740727.545 4852834.052 -11662456.028 4931105.569\n" + + +# def test_cli_shapes_bbox() -> None: +# """JSON text sequences of bboxes are output.""" +# runner = CliRunner() +# result = runner.invoke( +# cli, +# [ +# "shapes", +# "[106, 193, 9]", +# "--seq", +# "--bbox", +# "--mercator", +# "--precision", +# "3", +# ], +# ) +# assert result.exit_code == 0 +# assert ( +# result.output +# == "\x1e\n[-11740727.545, 4852834.052, -11662456.028, 4931105.569]\n" +# ) + + +# def test_cli_shapes_props_fid() -> None: +# runner = CliRunner() +# result = runner.invoke( +# cli, +# [ +# "shapes", +# '{"tile": [106, 193, 9], "properties": {"title": "foo"}, "id": "42"}', +# ], +# ) +# assert result.exit_code == 0 +# assert '"title": "foo"' in result.output +# assert '"id": "42"' in result.output + + +# def test_cli_strict_overlap_contain() -> None: +# runner = CliRunner() +# result1 = runner.invoke(cli, ["shapes"], "[2331,1185,12]") +# assert result1.exit_code == 0 +# result2 = runner.invoke(cli, ["tiles", "12"], result1.output) +# assert result2.exit_code == 0 +# assert result2.output == "[2331, 1185, 12]\n" diff --git a/tests/test_tiletype.py b/tests/test_tiletype.py index e2b2839f..52bd972c 100644 --- a/tests/test_tiletype.py +++ b/tests/test_tiletype.py @@ -9,15 +9,17 @@ PWD = Path(__file__).parent + def _repo_root() -> Path: _root = PWD - for _i in range(5): + for _i in range(5): _root = _root.parent if (_root / ".github").is_dir(): return _root msg = "Could not find repo root" raise RuntimeError(msg) + REPO_ROOT = _repo_root() # go up and find dir with sub dir ".github" @@ -96,6 +98,7 @@ def tiletype(buffer: bytes) -> Extensions: "tile-obj.json": "json", } + def test_found_test_files() -> None: assert len(TEST_TILES_BYTES) == len(TEST_TILE_NAME2TYPE) diff --git a/tests/test_utiles.py b/tests/test_utiles.py index 94298a6c..b76e6346 100644 --- a/tests/test_utiles.py +++ b/tests/test_utiles.py @@ -26,7 +26,6 @@ def test_version() -> None: assert utiles.__version__ == pyproject_version - @pytest.mark.parametrize( "tile,quadkey", [ From 2ad7e7e095ec6ecd4e5416d647d2e51456eaf9ba Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 08:43:36 -0800 Subject: [PATCH 62/80] black the stuff --- tests/test_rust_cli.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_rust_cli.py b/tests/test_rust_cli.py index 09138f53..a36f08ac 100644 --- a/tests/test_rust_cli.py +++ b/tests/test_rust_cli.py @@ -283,6 +283,7 @@ def test_cli_children(self) -> None: == "[486, 332, 10]\n[487, 332, 10]\n[487, 333, 10]\n[486, 333, 10]\n" ) + # =================== # SHAPES TESTS (TODO) # =================== From 3d19433f07471165d3a20c9e16912964aa2049aa Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 08:45:53 -0800 Subject: [PATCH 63/80] weirdness on armv7 --- .github/workflows/dev.yml | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 6abf8738..5d063b99 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -41,21 +41,21 @@ jobs: pip install -r requirements/dev.txt pytest - - name: pytest - if: ${{ !startsWith(matrix.target, 'x86') && matrix.target != 'ppc64' }} - uses: uraimo/run-on-arch-action@v2.5.0 - with: - arch: ${{ matrix.target }} - distro: ubuntu22.04 - githubToken: ${{ github.token }} - install: | - apt-get update - apt-get install -y --no-install-recommends python3 python3-pip - pip3 install -U pip pytest - pip3 install -r requirements/dev.txt - run: | - set -e - pip install click - pip3 install utiles --no-index --no-deps --find-links dist --force-reinstall - pip3 install -r requirements/dev.txt - pytest + # - name: pytest + # if: ${{ !startsWith(matrix.target, 'x86') && matrix.target != 'ppc64' }} + # uses: uraimo/run-on-arch-action@v2.5.0 + # with: + # arch: ${{ matrix.target }} + # distro: ubuntu22.04 + # githubToken: ${{ github.token }} + # install: | + # apt-get update + # apt-get install -y --no-install-recommends python3 python3-pip + # pip3 install -U pip pytest + # pip3 install -r requirements/dev.txt + # run: | + # set -e + # pip install click + # pip3 install utiles --no-index --no-deps --find-links dist --force-reinstall + # pip3 install -r requirements/dev.txt + # pytest From 89bbbf9f58f542a667558e48c05e5dbd8c99a8f9 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 10:01:20 -0800 Subject: [PATCH 64/80] click! --- python/utiles/_cli.py | 24 ++---------------------- python/utiles/_click.py | 41 +++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 +- src/pyutiles/pytile.rs | 2 +- 4 files changed, 45 insertions(+), 24 deletions(-) create mode 100644 python/utiles/_click.py diff --git a/python/utiles/_cli.py b/python/utiles/_cli.py index daf1efec..54a5a5e3 100644 --- a/python/utiles/_cli.py +++ b/python/utiles/_cli.py @@ -4,38 +4,18 @@ import logging import sys -import click - -from utiles import __version__, ut_cli +from utiles import ut_cli logger = logging.getLogger(__name__) -class NoHelpCommand(click.Command): - def get_help_option(self, _ctx: click.Context) -> None: - return None - - -# The CLI command group. -@click.command( - name="utiles", - cls=NoHelpCommand, - help="utiles cli (python-rust)", - no_args_is_help=False, - context_settings={ - "ignore_unknown_options": True, - "allow_extra_args": True, - }, -) -@click.version_option(version=__version__, message="%(version)s") def cli() -> None: - """Execute the main utiles command""" args = ["ut", *sys.argv[1:]] try: ut_cli(args) except Exception as e: logger.error(e) - raise click.BadParameter(str(e)) from e + raise e from e if __name__ == "__main__": diff --git a/python/utiles/_click.py b/python/utiles/_click.py new file mode 100644 index 00000000..3f94d4b9 --- /dev/null +++ b/python/utiles/_click.py @@ -0,0 +1,41 @@ +"""Utiles cli wrapped w/ click""" +from __future__ import annotations + +import logging + +import click + +from utiles import __version__ +from utiles._cli import cli + +logger = logging.getLogger(__name__) + + +class NoHelpCommand(click.Command): + def get_help_option(self, _ctx: click.Context) -> None: + return None + + +# The CLI command group. +@click.command( + name="utiles", + cls=NoHelpCommand, + help="utiles cli (python-rust)", + no_args_is_help=False, + context_settings={ + "ignore_unknown_options": True, + "allow_extra_args": True, + }, +) +@click.version_option(version=__version__, message="%(version)s") +def cli_click() -> None: + """Execute the main utiles command""" + try: + cli() + except Exception as e: + logger.error(e) + raise click.BadParameter(str(e)) from e + + +if __name__ == "__main__": + cli_click() diff --git a/src/lib.rs b/src/lib.rs index 230bd2ba..be76c19a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -764,7 +764,7 @@ fn lib_constants(_py: Python<'_>, m: &PyModule) -> PyResult<()> { /// Utiles python module #[pymodule] -#[pyo3(name="_utiles")] +#[pyo3(name = "_utiles")] fn libutiles(_py: Python<'_>, m: &PyModule) -> PyResult<()> { // lib constants lib_constants(_py, m)?; diff --git a/src/pyutiles/pytile.rs b/src/pyutiles/pytile.rs index 32380cac..444bc1eb 100644 --- a/src/pyutiles/pytile.rs +++ b/src/pyutiles/pytile.rs @@ -13,7 +13,7 @@ use pyo3::types::PyType; use pyo3::exceptions::PyValueError; use pyo3::{ - exceptions, IntoPy, Py, PyAny, pyclass, PyErr, pymethods, PyObject, PyRef, + exceptions, pyclass, pymethods, IntoPy, Py, PyAny, PyErr, PyObject, PyRef, PyResult, Python, }; use serde::{Deserialize, Serialize}; From 6c3ec2a6640172733640b102d28822331a2fcad5 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 12:15:07 -0800 Subject: [PATCH 65/80] aha --- crates/utiles-cli/src/bin.rs | 1 + crates/utiles-cli/src/cli.rs | 69 +++++++++++++++++++++++++++++------- crates/utiles-cli/src/lib.rs | 1 + crates/utiles/src/lib.rs | 6 ++-- crates/utiles/src/tile.rs | 60 +++++++++++++++++++++++++++++-- tests/test_rust_cli.py | 1 + 6 files changed, 120 insertions(+), 18 deletions(-) diff --git a/crates/utiles-cli/src/bin.rs b/crates/utiles-cli/src/bin.rs index 22709723..4a0cbdbb 100644 --- a/crates/utiles-cli/src/bin.rs +++ b/crates/utiles-cli/src/bin.rs @@ -1,5 +1,6 @@ mod cli; mod stdinterator; +mod shapes; #[tokio::main] async fn main() { diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index ef6f703c..40edeb76 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -11,6 +11,7 @@ use utiles::zoom::ZoomOrZooms; use utiles::{bounding_tile, Tile}; use utilesqlite::mbtiles::Mbtiles; +use crate::shapes::{shapes_main, ShapesArgs}; use crate::stdinterator::StdInterator; /// A fictional versioning CLI @@ -43,6 +44,7 @@ pub struct InputAndSequenceArgs { #[arg(required = false, long, action = clap::ArgAction::SetTrue)] seq: bool, } +// #[group(ArgGroup::new("projected").args(&["geographic", "mercator"]).required(false))] #[derive(Debug, Subcommand)] pub enum Commands { @@ -63,11 +65,9 @@ pub enum Commands { min: bool, }, - // ======================================================================== // TILE CLI UTILS - MERCANTILE LIKE CLI // ======================================================================== - #[command(name = "tiles", about = "echo tiles of bbox", long_about = None)] Tiles { #[arg(required = true)] @@ -128,13 +128,60 @@ pub enum Commands { #[arg(required = false, long, default_value = "1")] depth: u8, }, - #[command(name = "shapes", about = "echo shapes of tiles as geojson", long_about = None)] - Shapes { - #[arg(required = true)] - input: String, - seq: bool, - }, + #[command(name = "shapes", about = "echo shapes of tile(s) as GeoJSON", long_about = None)] + Shapes (ShapesArgs), + // { + // #[arg(required = false)] + // input: Option, + + // #[arg(required = false, long, action = clap::ArgAction::SetTrue)] + // seq: bool, + + // /// Decimal precision of coordinates. + // #[arg(long, value_parser)] + // precision: Option, + + // /// Indentation level for JSON output. + // #[arg(long, value_parser)] + // indent: Option, + + // /// Use compact separators (',', ':'). + // #[arg(long, action)] + // compact: bool, + + // /// Output in geographic coordinates (the default). + // #[arg(long, group = "projected")] + // geographic: bool, + + // /// Output in Web Mercator coordinates. + // #[arg(long, group = "projected")] + // mercator: bool, + + // // /// Write a RS-delimited JSON sequence (default is LF). + // // #[clap(long, action)] + // // seq: bool, + // /// Output as sequence of GeoJSON features (the default). + // // #[clap(long, group = "output_mode")] + // #[arg(required = false, long, action = clap::ArgAction::SetTrue)] + // feature: bool, + + // /// Output as sequence of GeoJSON bbox arrays. + // #[arg(long, group = "output_mode")] + // bbox: bool, + + // /// Output as a GeoJSON feature collections. + // #[arg(long, action)] + // collect: bool, + + // /// Write shape extents as ws-separated strings (default is False). + // #[arg(long, action)] + // extents: bool, + + // /// Shift shape x and y values by a constant number. + // #[arg(long, value_parser)] + // buffer: Option, + // }, } #[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq)] @@ -408,7 +455,6 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { .filter(|l| l.as_ref().unwrap() != "\x1e"); let tiles = lines.map(|l| Tile::from_json(&l.unwrap())); for tile in tiles { - let nup = tile.z as i32 - depth as i32; if nup < 0 { // error @@ -419,9 +465,8 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { println!("{}{}", rs, parent.json_arr()); } } - - _ => { - println!("NOT IMPLEMENTED YET"); + Commands::Shapes( args) => { + shapes_main(args); } } } diff --git a/crates/utiles-cli/src/lib.rs b/crates/utiles-cli/src/lib.rs index 1e2f04de..86e9f519 100644 --- a/crates/utiles-cli/src/lib.rs +++ b/crates/utiles-cli/src/lib.rs @@ -1,2 +1,3 @@ pub mod cli; pub mod stdinterator; +pub mod shapes; diff --git a/crates/utiles/src/lib.rs b/crates/utiles/src/lib.rs index 93f02973..4cd4e98e 100644 --- a/crates/utiles/src/lib.rs +++ b/crates/utiles/src/lib.rs @@ -7,6 +7,7 @@ use constants::{EARTH_CIRCUMFERENCE, EARTH_RADIUS, LL_EPSILON}; use geo_types::coord; pub use lnglat::LngLat; +use serde::Deserialize; use sibling_relationship::SiblingRelationship; pub use tile::Tile; use tile_range::{TileRange, TileRanges}; @@ -26,6 +27,7 @@ pub mod tile_range; pub mod tilejson; pub mod traits; pub mod zoom; +mod tile_tuple; /// Tile macro to create a new tile. /// - do you need this? probably not @@ -37,10 +39,6 @@ macro_rules! utile { }; } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[allow(clippy::upper_case_acronyms)] -pub struct XYZ(pub u32, pub u32, pub u8); - pub fn ul(x: u32, y: u32, z: u8) -> LngLat { let (lon_deg, lat_deg) = ult(x, y, z); LngLat { diff --git a/crates/utiles/src/tile.rs b/crates/utiles/src/tile.rs index 55e8b851..ed35f50f 100644 --- a/crates/utiles/src/tile.rs +++ b/crates/utiles/src/tile.rs @@ -1,16 +1,18 @@ use std::cmp::Ordering; use std::error::Error; use std::str::FromStr; - +use serde_json::Map; use serde::{Deserialize, Serialize}; +use serde_json::Value; use crate::bbox::BBox; use crate::constants::EPSILON; use crate::lnglat::LngLat; use crate::{ bounds, children, flipy, ll, lr, neighbors, parent, pmtiles, quadkey2tile, - siblings, traits, ul, ur, xyz2quadkey, XYZ, + siblings, traits, ul, ur, xyz2quadkey, }; +use crate::tile_tuple::XYZ; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Tile { @@ -393,6 +395,60 @@ impl Tile { } } +impl From> for Tile{ + fn from(map: Map) -> Self { + let x = map["x"].as_u64().unwrap() as u32; + let y = map["y"].as_u64().unwrap() as u32; + let z = map["z"].as_u64().unwrap() as u8; + Tile::new(x, y, z) + } +} + +impl From> for Tile { + fn from(arr: Vec) -> Self { + let x = arr[0].as_u64().unwrap() as u32; + let y = arr[1].as_u64().unwrap() as u32; + let z = arr[2].as_u64().unwrap() as u8; + Tile::new(x, y, z) + } +} + +impl From for Tile { + fn from(val: Value) -> Self { + // is array? [x, y, z] + match val { + Value::Array(v) => { + if v.len() < 3 { + panic!("Invalid json value: {}", serde_json::to_string(&v).unwrap()); + } + Tile::from(v) + // let tuple = serde_json::from_value::(val).unwrap(); + // return Tile::from(tuple); + } + Value::Object(v) => { + // if it has a "tile" key, use that + if v["tile"].is_array() && v["tile"].as_array().unwrap().len() == 3 { + let tuple = serde_json::from_value::(v["tile"].clone()).unwrap(); + return Tile::from(tuple); + } + return Tile::from(v); + }, + _ => { + panic!("Invalid json value: {val}"); + } + } + + // if val.is_array() { + // return Self::from_json_arr(&val.to_string()); + // } + // let arr = val.as_array().unwrap(); + // let x = arr[0].as_u64().unwrap() as u32; + // let y = arr[1].as_u64().unwrap() as u32; + // let z = arr[2].as_u64().unwrap() as u8; + // Tile::new(x, y, z) + } +} + #[cfg(test)] mod tests { diff --git a/tests/test_rust_cli.py b/tests/test_rust_cli.py index a36f08ac..b2c65036 100644 --- a/tests/test_rust_cli.py +++ b/tests/test_rust_cli.py @@ -1,5 +1,6 @@ """Utiles rust cli tests""" import json +import os import sys from json import dumps as stringify from subprocess import CompletedProcess, run From 1da29db511e24291f792884706f97a73540f4ca0 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 12:15:14 -0800 Subject: [PATCH 66/80] shapes --- crates/utiles-cli/src/shapes.rs | 124 ++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 crates/utiles-cli/src/shapes.rs diff --git a/crates/utiles-cli/src/shapes.rs b/crates/utiles-cli/src/shapes.rs new file mode 100644 index 00000000..48cb2e8b --- /dev/null +++ b/crates/utiles-cli/src/shapes.rs @@ -0,0 +1,124 @@ +use clap::{Args, Parser}; +use crate::stdinterator::StdInterator; +use utiles::Tile; + +// #[group(required = false, id="projected")] +#[derive(Args, Debug)] +#[group(required =false , multiple = false, id = "project")] +pub struct ShapesProject { + /// Output in geographic coordinates (the default). + #[arg(long , default_value = "false", conflicts_with = "mercator", action = clap::ArgAction::SetTrue)] + geographic: bool, + + /// Output in Web Mercator coordinates. + #[arg(long , default_value = "false", conflicts_with = "geographic", action = clap::ArgAction::SetTrue)] + mercator: bool, +} + +impl Default for ShapesProject { + fn default() -> Self { + ShapesProject { + geographic: true, + mercator: false, + } + } +} + +#[derive(Args, Debug)] +#[group(required =false , multiple = false, id = "output-mode")] +pub struct ShapesOutputMode{ + #[arg(long , default_value = "false", conflicts_with = "bbox", action = clap::ArgAction::SetTrue)] + feature: bool, + + /// Output in Web Mercator coordinates. + #[arg(long , default_value = "false", conflicts_with = "feature", action = clap::ArgAction::SetTrue)] + bbox: bool, +} + +impl Default for ShapesOutputMode { + fn default() -> Self { + ShapesOutputMode { + feature: true, + bbox: false, + } + } +} + + +#[derive(Debug, Parser)] // requires `derive` feature +#[command(name = "shapes", about = "echo shapes of tile(s) as GeoJSON", long_about = None)] +pub struct ShapesArgs { + + + #[arg(required = false)] + input: Option, + + #[arg(required = false, long, action = clap::ArgAction::SetTrue)] + seq: bool, + + /// Decimal precision of coordinates. + #[arg(long, value_parser)] + precision: Option, + + /// Indentation level for JSON output. + #[arg(long, value_parser)] + indent: Option, + + /// Use compact separators (',', ':'). + #[arg(long, action)] + compact: bool, + + + #[command(flatten)] + project: Option, + + #[command(flatten)] + output_mode: Option, + + /// Output as a GeoJSON feature collections. + #[arg(long, action)] + collect: bool, + + /// Write shape extents as ws-separated strings (default is False). + #[arg(long, default_value = "false", action = clap::ArgAction::SetTrue)] + extents: bool, + + /// Shift shape x and y values by a constant number. + #[arg(long, value_parser)] + buffer: Option, +} + +impl Default for ShapesArgs { + fn default() -> Self { + ShapesArgs { + input: None, + seq: false, + precision: None, + indent: None, + compact: false, + project: Option::Some(ShapesProject::default()), + output_mode: Option::Some(ShapesOutputMode::default()), + collect: false, + extents: false, + buffer: None, + } + } +} + +pub fn shapes_main(args: ShapesArgs) { + println!("{:?}", args); + let input_lines = StdInterator::new(args.input).unwrap(); + let lines = input_lines + .filter(|l| !l.is_err()) + .filter(|l| !l.as_ref().unwrap().is_empty()) + .filter(|l| l.as_ref().unwrap() != "\x1e"); + let tiles = lines.map(|l| { + // Tile::from_json(&l.unwrap()) + let val = l.unwrap(); + + }); + + for tile in tiles { + println!("{:?}", tile); + } +} \ No newline at end of file From 6e9da953c4867b69825a1bcba396e34af51b7a9e Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 13:10:33 -0800 Subject: [PATCH 67/80] save --- crates/utiles/src/lib.rs | 2 +- crates/utiles/src/libtiletype.rs | 2 +- crates/utiles/src/parsing.rs | 2 +- crates/utiles/src/tile.rs | 91 ++++++++++++++++++++++---------- crates/utiles/src/tile_tuple.rs | 11 ++++ 5 files changed, 78 insertions(+), 30 deletions(-) create mode 100644 crates/utiles/src/tile_tuple.rs diff --git a/crates/utiles/src/lib.rs b/crates/utiles/src/lib.rs index 4cd4e98e..81874065 100644 --- a/crates/utiles/src/lib.rs +++ b/crates/utiles/src/lib.rs @@ -24,10 +24,10 @@ pub mod pmtiles; pub mod sibling_relationship; pub mod tile; pub mod tile_range; +mod tile_tuple; pub mod tilejson; pub mod traits; pub mod zoom; -mod tile_tuple; /// Tile macro to create a new tile. /// - do you need this? probably not diff --git a/crates/utiles/src/libtiletype.rs b/crates/utiles/src/libtiletype.rs index ae448e10..96326ff7 100644 --- a/crates/utiles/src/libtiletype.rs +++ b/crates/utiles/src/libtiletype.rs @@ -59,7 +59,7 @@ pub fn tiletype(buffer: &[u8]) -> TileType { } else if buffer[0] == 0x1f && buffer[1] == 0x8b { return TileType::Pbfgz; // if starts with '{' or '[' json - } else if buffer[0] == 0x7b || buffer[0] == 0x5b { + } else if buffer[0] == 0x7b || buffer[0] == 0x5b { return TileType::Json; } } diff --git a/crates/utiles/src/parsing.rs b/crates/utiles/src/parsing.rs index 2d92cd53..068a27bc 100644 --- a/crates/utiles/src/parsing.rs +++ b/crates/utiles/src/parsing.rs @@ -1,8 +1,8 @@ use crate::bbox::BBox; use crate::geojson::geojson_coords; use geo_types::Coord; -use tracing::debug; use serde_json::Value; +use tracing::debug; // pub fn parse_bbox(s: &str) -> serde_json::Result { pub fn parse_bbox(s: &str) -> serde_json::Result { diff --git a/crates/utiles/src/tile.rs b/crates/utiles/src/tile.rs index ed35f50f..eb777f2f 100644 --- a/crates/utiles/src/tile.rs +++ b/crates/utiles/src/tile.rs @@ -1,18 +1,20 @@ +use serde::{Deserialize, Serialize}; +use serde_json::Map; +use serde_json::Value; use std::cmp::Ordering; use std::error::Error; use std::str::FromStr; -use serde_json::Map; -use serde::{Deserialize, Serialize}; -use serde_json::Value; + +use crate::utile; use crate::bbox::BBox; use crate::constants::EPSILON; use crate::lnglat::LngLat; +use crate::tile_tuple::XYZ; use crate::{ bounds, children, flipy, ll, lr, neighbors, parent, pmtiles, quadkey2tile, siblings, traits, ul, ur, xyz2quadkey, }; -use crate::tile_tuple::XYZ; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Tile { @@ -92,7 +94,8 @@ impl FromStr for Tile { fn from_str(s: &str) -> Result { // if it starts with '{' assume json obj - if s.starts_with('{'){ // if '{' assume its an obj + if s.starts_with('{') { + // if '{' assume its an obj return Ok(Tile::from_json_obj(s)); } else if s.starts_with('[') { return Ok(Tile::from_json_arr(s)); @@ -395,21 +398,33 @@ impl Tile { } } -impl From> for Tile{ +impl From<(u32, u32, u8)> for Tile { + fn from(tuple: (u32, u32, u8)) -> Self { + XYZ::from(tuple).into() + } +} + +impl From> for Tile { fn from(map: Map) -> Self { let x = map["x"].as_u64().unwrap() as u32; let y = map["y"].as_u64().unwrap() as u32; let z = map["z"].as_u64().unwrap() as u8; - Tile::new(x, y, z) + utile!(x, y, z) } } impl From> for Tile { fn from(arr: Vec) -> Self { + if arr.len() < 3 { + panic!( + "Invalid json value: {}", + serde_json::to_string(&arr).unwrap() + ); + } let x = arr[0].as_u64().unwrap() as u32; let y = arr[1].as_u64().unwrap() as u32; let z = arr[2].as_u64().unwrap() as u8; - Tile::new(x, y, z) + Tile::from((x, y, z)) } } @@ -417,9 +432,12 @@ impl From for Tile { fn from(val: Value) -> Self { // is array? [x, y, z] match val { - Value::Array(v) => { - if v.len() < 3 { - panic!("Invalid json value: {}", serde_json::to_string(&v).unwrap()); + Value::Array(v) => { + if v.len() < 3 { + panic!( + "Invalid json value: {}", + serde_json::to_string(&v).unwrap() + ); } Tile::from(v) // let tuple = serde_json::from_value::(val).unwrap(); @@ -427,29 +445,24 @@ impl From for Tile { } Value::Object(v) => { // if it has a "tile" key, use that - if v["tile"].is_array() && v["tile"].as_array().unwrap().len() == 3 { - let tuple = serde_json::from_value::(v["tile"].clone()).unwrap(); - return Tile::from(tuple); + // if has 'tile' key, use that + if v.contains_key("tile") { + if v["tile"].is_array() && v["tile"].as_array().unwrap().len() == 3 + { + let tuple = + serde_json::from_value::(v["tile"].clone()).unwrap(); + return Tile::from(tuple); + } } return Tile::from(v); - }, + } _ => { panic!("Invalid json value: {val}"); } } - - // if val.is_array() { - // return Self::from_json_arr(&val.to_string()); - // } - // let arr = val.as_array().unwrap(); - // let x = arr[0].as_u64().unwrap() as u32; - // let y = arr[1].as_u64().unwrap() as u32; - // let z = arr[2].as_u64().unwrap() as u8; - // Tile::new(x, y, z) } } - #[cfg(test)] mod tests { use super::*; @@ -471,7 +484,31 @@ mod tests { #[test] fn parse_quadkey() { let quadkey = "023010203"; - let tile = quadkey.parse::(); + let tile = quadkey.parse::(); assert_eq!(tile.unwrap(), Tile::new(81, 197, 9)); } -} \ No newline at end of file + + #[test] + fn tile_from_value_obj() { + let json_obj = r#"{"x": 1, "y": 2, "z": 3}"#; + let val_obj = serde_json::from_str::(json_obj).unwrap(); + let tile_from_obj = Tile::from(val_obj); + assert_eq!(tile_from_obj, Tile::new(1, 2, 3)); + } + + #[test] + fn tile_from_value_arr() { + let json_arr = r#"[1, 2, 3]"#; + let val_arr = serde_json::from_str::(json_arr).unwrap(); + let tile_from_arr = Tile::from(val_arr); + assert_eq!(tile_from_arr, Tile::new(1, 2, 3)); + } + #[test] + fn tile_from_value_obj_with_array() { + let json_obj_with_tile_array = r#"{"tile": [1, 2, 3]}"#; + let val_obj_with_tile_array = + serde_json::from_str::(json_obj_with_tile_array).unwrap(); + let tile_from_obj_with_tile_array = Tile::from(val_obj_with_tile_array); + assert_eq!(tile_from_obj_with_tile_array, Tile::new(1, 2, 3)); + } +} diff --git a/crates/utiles/src/tile_tuple.rs b/crates/utiles/src/tile_tuple.rs new file mode 100644 index 00000000..d55076cd --- /dev/null +++ b/crates/utiles/src/tile_tuple.rs @@ -0,0 +1,11 @@ +use serde::Deserialize; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize)] +#[allow(clippy::upper_case_acronyms)] +pub struct XYZ(pub u32, pub u32, pub u8); + +impl From<(u32, u32, u8)> for XYZ { + fn from(xyz: (u32, u32, u8)) -> Self { + XYZ(xyz.0, xyz.1, xyz.2) + } +} \ No newline at end of file From e469830d627962986c8c6e98f2823a628619c898 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 15:36:16 -0800 Subject: [PATCH 68/80] nice --- Cargo.lock | 2 + crates/utiles-cli/Cargo.lock | 2 + crates/utiles-cli/Cargo.toml | 2 + crates/utiles-cli/src/bin.rs | 2 +- crates/utiles-cli/src/cli.rs | 4 +- crates/utiles-cli/src/lib.rs | 2 +- crates/utiles-cli/src/shapes.rs | 160 +++++++++++++++--- crates/utiles/src/lib.rs | 2 +- crates/utiles/src/projection.rs | 28 +++ crates/utiles/src/tile.rs | 261 +++++++++++++++++++++++++++- crates/utiles/src/tile_tuple.rs | 2 +- tests/test_rust_cli.py | 290 +++++++++++++++++--------------- 12 files changed, 582 insertions(+), 175 deletions(-) create mode 100644 crates/utiles/src/projection.rs diff --git a/Cargo.lock b/Cargo.lock index e2514590..eee74b7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -990,6 +990,8 @@ dependencies = [ "clap", "clap-verbosity-flag", "rusqlite", + "serde", + "serde_json", "tokio", "tracing", "tracing-subscriber", diff --git a/crates/utiles-cli/Cargo.lock b/crates/utiles-cli/Cargo.lock index 68834a2e..0b35d7dd 100644 --- a/crates/utiles-cli/Cargo.lock +++ b/crates/utiles-cli/Cargo.lock @@ -886,6 +886,8 @@ dependencies = [ "clap", "clap-verbosity-flag", "rusqlite", + "serde", + "serde_json", "tokio", "tracing", "tracing-subscriber", diff --git a/crates/utiles-cli/Cargo.toml b/crates/utiles-cli/Cargo.toml index 54910cfb..696427ad 100644 --- a/crates/utiles-cli/Cargo.toml +++ b/crates/utiles-cli/Cargo.toml @@ -20,3 +20,5 @@ tracing = { version = "0.1.40", features = [] } tracing-subscriber = { version = "0.3.17", features = ["serde", "serde_json", "env-filter"] } utiles = { path = "../utiles" } utilesqlite = { path = "../utilesqlite" } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.96" \ No newline at end of file diff --git a/crates/utiles-cli/src/bin.rs b/crates/utiles-cli/src/bin.rs index 4a0cbdbb..3345218e 100644 --- a/crates/utiles-cli/src/bin.rs +++ b/crates/utiles-cli/src/bin.rs @@ -1,6 +1,6 @@ mod cli; -mod stdinterator; mod shapes; +mod stdinterator; #[tokio::main] async fn main() { diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index 40edeb76..23981b2b 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -130,7 +130,7 @@ pub enum Commands { }, #[command(name = "shapes", about = "echo shapes of tile(s) as GeoJSON", long_about = None)] - Shapes (ShapesArgs), + Shapes(ShapesArgs), // { // #[arg(required = false)] // input: Option, @@ -465,7 +465,7 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { println!("{}{}", rs, parent.json_arr()); } } - Commands::Shapes( args) => { + Commands::Shapes(args) => { shapes_main(args); } } diff --git a/crates/utiles-cli/src/lib.rs b/crates/utiles-cli/src/lib.rs index 86e9f519..97033478 100644 --- a/crates/utiles-cli/src/lib.rs +++ b/crates/utiles-cli/src/lib.rs @@ -1,3 +1,3 @@ pub mod cli; -pub mod stdinterator; pub mod shapes; +pub mod stdinterator; diff --git a/crates/utiles-cli/src/shapes.rs b/crates/utiles-cli/src/shapes.rs index 48cb2e8b..a9be7dc3 100644 --- a/crates/utiles-cli/src/shapes.rs +++ b/crates/utiles-cli/src/shapes.rs @@ -1,10 +1,14 @@ -use clap::{Args, Parser}; use crate::stdinterator::StdInterator; -use utiles::Tile; +use clap::{Args, Parser}; +use serde_json::{Map, Value}; +use tracing::debug; +use utiles::projection::Projection; +use utiles::tile::FeatureOptions; +use utiles::{tile, Tile}; // #[group(required = false, id="projected")] #[derive(Args, Debug)] -#[group(required =false , multiple = false, id = "project")] +#[group(required = false, multiple = false, id = "project")] pub struct ShapesProject { /// Output in geographic coordinates (the default). #[arg(long , default_value = "false", conflicts_with = "mercator", action = clap::ArgAction::SetTrue)] @@ -25,8 +29,8 @@ impl Default for ShapesProject { } #[derive(Args, Debug)] -#[group(required =false , multiple = false, id = "output-mode")] -pub struct ShapesOutputMode{ +#[group(required = false, multiple = false, id = "output-mode")] +pub struct ShapesOutputMode { #[arg(long , default_value = "false", conflicts_with = "bbox", action = clap::ArgAction::SetTrue)] feature: bool, @@ -44,12 +48,9 @@ impl Default for ShapesOutputMode { } } - #[derive(Debug, Parser)] // requires `derive` feature #[command(name = "shapes", about = "echo shapes of tile(s) as GeoJSON", long_about = None)] pub struct ShapesArgs { - - #[arg(required = false)] input: Option, @@ -60,15 +61,6 @@ pub struct ShapesArgs { #[arg(long, value_parser)] precision: Option, - /// Indentation level for JSON output. - #[arg(long, value_parser)] - indent: Option, - - /// Use compact separators (',', ':'). - #[arg(long, action)] - compact: bool, - - #[command(flatten)] project: Option, @@ -94,10 +86,8 @@ impl Default for ShapesArgs { input: None, seq: false, precision: None, - indent: None, - compact: false, project: Option::Some(ShapesProject::default()), - output_mode: Option::Some(ShapesOutputMode::default()), + output_mode: Option::Some(ShapesOutputMode::default()), collect: false, extents: false, buffer: None, @@ -105,20 +95,134 @@ impl Default for ShapesArgs { } } +struct TileWithProperties { + tile: Tile, + id: Option, + properties: Option>, +} + pub fn shapes_main(args: ShapesArgs) { - println!("{:?}", args); + debug!("{:?}", args); let input_lines = StdInterator::new(args.input).unwrap(); let lines = input_lines .filter(|l| !l.is_err()) .filter(|l| !l.as_ref().unwrap().is_empty()) .filter(|l| l.as_ref().unwrap() != "\x1e"); - let tiles = lines.map(|l| { - // Tile::from_json(&l.unwrap()) - let val = l.unwrap(); + let parsed_lines = lines.map(|l| { + let ln = l.unwrap(); + let val: Value = serde_json::from_str::(&ln).unwrap(); + let properties: Option> = match val["properties"].is_object() + { + true => { + let properties = val["properties"].as_object().unwrap().clone(); + Option::from(properties) + } + false => None, + }; + let id: Option = match val["id"].is_string() { + true => { + let id = val["id"].as_str().unwrap().to_string(); + Option::from(id) + } + false => None, + }; + let t = Tile::from(&val); + TileWithProperties { + tile: t, + id, + properties, + } + // Tile::from_json_loose(&ln) }); - - for tile in tiles { - println!("{:?}", tile); + let feature_options: FeatureOptions = FeatureOptions { + fid: None, + projection: match args.project { + Some(project) => match project { + ShapesProject { + geographic: true, + mercator: false, + } => Projection::Geographic, + ShapesProject { + geographic: false, + mercator: true, + } => Projection::Mercator, + _ => Projection::Geographic, + }, + None => Projection::Geographic, + }, + props: None, + buffer: Option::from(args.buffer), + precision: args.precision, + }; + + if args.collect { + println!("{{"); + println!("\"type\": \"FeatureCollection\","); + println!("\"features\": ["); + } + let mut lons: Vec = Vec::new(); + let mut lats: Vec = Vec::new(); + let output_bbox = match args.output_mode { + Some(output_mode) => match output_mode { + ShapesOutputMode { + feature: true, + bbox: false, + } => false, + ShapesOutputMode { + feature: false, + bbox: true, + } => true, + _ => false, + }, + None => false, + }; + + let mut first = true; + + for tile_n_properties in parsed_lines { + let tile = tile_n_properties.tile; + let properties = tile_n_properties.properties; + let mut f = tile.feature(&feature_options).unwrap(); + + if let Some(properties) = properties { + f.properties.extend(properties); + } + if let Some(id) = tile_n_properties.id { + f.id = id; + } + lons.extend(f.bbox_lons()); + lats.extend(f.bbox_lats()); + if args.extents { + println!("{}", f.extents_string()); + } else if args.collect { + if !first { + println!(","); + } + println!("{}", f.to_json()); + first = false; + } else { + if args.seq { + println!("\x1e"); + } + if output_bbox { + println!("{}", f.bbox_json()); + } else { + println!("{}", f.to_json()); + } + } } -} \ No newline at end of file + if args.collect { + println!("]"); + println!("}}"); + } + // for tile in tiles { + // let f = tile.feature( + // &feature_options + // ).unwrap(); + // lons.extend(f.bbox_lons()); + // lats.extend(f.bbox_lats()); + // + // println!("{}", f.to_json()); + // } +} diff --git a/crates/utiles/src/lib.rs b/crates/utiles/src/lib.rs index 81874065..b5170669 100644 --- a/crates/utiles/src/lib.rs +++ b/crates/utiles/src/lib.rs @@ -7,7 +7,6 @@ use constants::{EARTH_CIRCUMFERENCE, EARTH_RADIUS, LL_EPSILON}; use geo_types::coord; pub use lnglat::LngLat; -use serde::Deserialize; use sibling_relationship::SiblingRelationship; pub use tile::Tile; use tile_range::{TileRange, TileRanges}; @@ -21,6 +20,7 @@ pub mod lnglat; pub mod mbtiles; pub mod parsing; pub mod pmtiles; +pub mod projection; pub mod sibling_relationship; pub mod tile; pub mod tile_range; diff --git a/crates/utiles/src/projection.rs b/crates/utiles/src/projection.rs new file mode 100644 index 00000000..3432cc8b --- /dev/null +++ b/crates/utiles/src/projection.rs @@ -0,0 +1,28 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub enum Projection { + Geographic, + Mercator, +} + +impl Projection { + pub fn to_string(&self) -> String { + match self { + Projection::Geographic => "geographic".to_string(), + Projection::Mercator => "mercator".to_string(), + } + } +} + +impl From for Projection { + fn from(s: String) -> Self { + match s.as_str() { + "mercator" => Projection::Mercator, + "geographic" => Projection::Geographic, + _ => { + panic!("Invalid projection: {s}"); + } + } + } +} diff --git a/crates/utiles/src/tile.rs b/crates/utiles/src/tile.rs index eb777f2f..5e1334c4 100644 --- a/crates/utiles/src/tile.rs +++ b/crates/utiles/src/tile.rs @@ -1,6 +1,5 @@ use serde::{Deserialize, Serialize}; -use serde_json::Map; -use serde_json::Value; +use serde_json::{Map, Value}; use std::cmp::Ordering; use std::error::Error; use std::str::FromStr; @@ -10,12 +9,81 @@ use crate::utile; use crate::bbox::BBox; use crate::constants::EPSILON; use crate::lnglat::LngLat; +use crate::projection::Projection; use crate::tile_tuple::XYZ; use crate::{ bounds, children, flipy, ll, lr, neighbors, parent, pmtiles, quadkey2tile, - siblings, traits, ul, ur, xyz2quadkey, + siblings, traits, ul, ur, xy, xyz2quadkey, }; +#[derive(Debug, Serialize, Deserialize)] +pub struct TileFeatureGeometry { + #[serde(rename = "type")] + pub type_: String, + pub coordinates: Vec>>, +} + +#[derive(Debug, Serialize)] +pub struct FeatureOptions { + pub fid: Option, // feature id + pub props: Option>, + pub projection: Projection, + pub buffer: Option, + pub precision: Option, +} + +impl Default for FeatureOptions { + fn default() -> Self { + FeatureOptions { + fid: None, + props: None, + projection: Projection::Geographic, + buffer: None, + precision: None, + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct TileFeature { + pub id: String, + + #[serde(rename = "type")] + pub type_: String, + + pub geometry: TileFeatureGeometry, + pub bbox: (f64, f64, f64, f64), + pub properties: Map, +} + +impl TileFeature { + pub fn to_json(&self) -> String { + serde_json::to_string(self).unwrap() + } + + pub fn bbox_lons(&self) -> Vec { + vec![self.bbox.0, self.bbox.2] + } + + pub fn bbox_lats(&self) -> Vec { + vec![self.bbox.1, self.bbox.3] + } + + pub fn extents_string(&self) -> String { + format!( + "{} {} {} {}", + self.bbox.0, self.bbox.1, self.bbox.2, self.bbox.3 + ) + } + + pub fn bbox_json(&self) -> String { + format!( + "[{},{},{},{}]", + self.bbox.0, self.bbox.1, self.bbox.2, self.bbox.3 + ) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Tile { pub x: u32, @@ -211,6 +279,11 @@ impl Tile { Self::from_json_obj(json) } + pub fn from_json_loose(json: &str) -> Self { + let v = serde_json::from_str::(json).unwrap(); + Self::from(v) + } + pub fn quadkey(&self) -> String { xyz2quadkey(self.x, self.y, self.z) } @@ -396,6 +469,148 @@ impl Tile { pub fn json_obj(&self) -> String { serde_json::to_string(self).unwrap() } + + pub fn tuple_string(&self) -> String { + format!("({}, {}, {})", self.x, self.y, self.z) + } + + pub fn feature( + &self, + opts: &FeatureOptions, + // fid: Option, // feature id + // props: Option>, + // projected: Option, + // buffer: Option, + // precision: Option, + ) -> Result> { + // Convert the arguments to Rust values + // let pytile: PyTile = tile.into(); + // let tile = pytile.tuple(); + // let opts = options.unwrap_or_default(); + + // let (x, y, z) = self.tuple(); + // let fid = opts.fid.unwrap_or_default(); + // let props = opts.props; + // let projection = opts.projection; + // .unwrap_or_else( Projection::Geographic); + // let projected = opts.projected.unwrap_or_else(|| "geographic".to_string()); + let buffer = opts.buffer.unwrap_or(0.0); + let precision = opts.precision.unwrap_or(-1); + + // Compute the bounds + let (west, south, east, north) = self.bbox(); + + // Handle projected coordinates + let (mut west, mut south, mut east, mut north) = match opts.projection { + // Projection::Geographic=> (west, south, east, north), + Projection::Mercator => { + // let (east_merc, north_merc) = utiles::xy(east, north, Some(false)); + let (west_merc, south_merc) = xy(west, south, None); + let (east_merc, north_merc) = xy(east, north, None); + (west_merc, south_merc, east_merc, north_merc) + } + _ => (west, south, east, north), + }; + + // Apply buffer + west -= buffer; + south -= buffer; + east += buffer; + north += buffer; + + // Apply precision + if precision >= 0 { + let precision_factor = 10_f64.powi(precision); + west = (west * precision_factor).round() / precision_factor; + south = (south * precision_factor).round() / precision_factor; + east = (east * precision_factor).round() / precision_factor; + north = (north * precision_factor).round() / precision_factor; + } + + // Compute bbox and geometry + let bbox = ( + west.min(east), + south.min(north), + west.max(east), + south.max(north), + ); + // let bbox = [ + // west.min(east), + // south.min(north), + // west.max(east), + // south.max(north), + // ]; + let geometry_coordinates = vec![vec![ + vec![west, south], + vec![west, north], + vec![east, north], + vec![east, south], + vec![west, south], + ]]; + // + // let geometry_items = vec![ + // ("type".to_string(), "Polygon".to_object(py)), + // ( + // "coordinates".to_string(), + // geometry_coordinates.to_object(py), + // ), + // ] + // .into_iter() + // .collect::>(); + + let xyz = self.tuple_string(); + // let properties_vec= vec![ + // ("title".to_string(), Value::from(format!("XYZ tile {xyz}"))), + // // ("west".to_string(), west(py)), + // // ("south".to_string(), south.into_py(py)), + // // ("east".to_string(), east.into_py(py)), + // // ("north".to_string(), north.into_py(py)), + // ]; + let mut properties: Map = Map::new(); + properties.insert("title".to_string(), Value::from(format!("XYZ tile {xyz}"))); + properties.extend(opts.props.clone().unwrap_or_default()); + let id = match opts.fid.clone() { + Some(fid) => fid, + None => xyz, + }; + let tile_feature = TileFeature { + id: id, + type_: "Feature".to_string(), + geometry: TileFeatureGeometry { + type_: "Polygon".to_string(), + coordinates: geometry_coordinates, + }, + bbox: bbox, + properties: properties, + }; + + // Create the feature dictionary + // let mut feature_dict = HashMap::new(); + // feature_dict.insert("type".to_string(), "Feature".to_object(py)); + // feature_dict.insert("bbox".to_string(), bbox.to_object(py)); + // feature_dict.insert("id".to_string(), xyz.to_object(py)); + // feature_dict.insert("geometry".to_string(), geometry_items.to_object(py)); + + // Create the properties dictionary + // let mut properties_dict: HashMap> = HashMap::new(); + // properties_dict + // .insert("title".to_string(), format!("XYZ tile {xyz}").into_py(py)); + // if !props.is_empty() { + // let props: PyResult)>> = props + // .into_iter() + // .map(|(k, v)| Ok((k, v.into_py(py)))) + // .collect(); + // properties_dict.extend(props?); + // } + // feature_dict.insert("properties".to_string(), properties_dict.to_object(py)); + + // Add the feature id if provided + // if !fid.is_empty() { + // feature_dict.insert("id".to_string(), fid.to_object(py)); + // } + // Ok(feature_dict) + Ok(tile_feature) + } } impl From<(u32, u32, u8)> for Tile { @@ -404,8 +619,8 @@ impl From<(u32, u32, u8)> for Tile { } } -impl From> for Tile { - fn from(map: Map) -> Self { +impl From<&Map> for Tile { + fn from(map: &Map) -> Self { let x = map["x"].as_u64().unwrap() as u32; let y = map["y"].as_u64().unwrap() as u32; let z = map["z"].as_u64().unwrap() as u8; @@ -413,8 +628,8 @@ impl From> for Tile { } } -impl From> for Tile { - fn from(arr: Vec) -> Self { +impl From<&Vec> for Tile { + fn from(arr: &Vec) -> Self { if arr.len() < 3 { panic!( "Invalid json value: {}", @@ -427,9 +642,25 @@ impl From> for Tile { Tile::from((x, y, z)) } } +impl From> for Tile { + fn from(arr: Vec) -> Self { + Tile::from(&arr) + // + // if arr.len() < 3 { + // panic!( + // "Invalid json value: {}", + // serde_json::to_string(&arr).unwrap() + // ); + // } + // let x = arr[0].as_u64().unwrap() as u32; + // let y = arr[1].as_u64().unwrap() as u32; + // let z = arr[2].as_u64().unwrap() as u8; + // Tile::from((x, y, z)) + } +} -impl From for Tile { - fn from(val: Value) -> Self { +impl From<&Value> for Tile { + fn from(val: &Value) -> Self { // is array? [x, y, z] match val { Value::Array(v) => { @@ -463,6 +694,18 @@ impl From for Tile { } } +impl From for Tile { + fn from(val: Value) -> Self { + Tile::from(&val) + } +} + +impl From<&str> for Tile { + fn from(s: &str) -> Self { + Tile::from_json(s) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/utiles/src/tile_tuple.rs b/crates/utiles/src/tile_tuple.rs index d55076cd..3fbf2b87 100644 --- a/crates/utiles/src/tile_tuple.rs +++ b/crates/utiles/src/tile_tuple.rs @@ -8,4 +8,4 @@ impl From<(u32, u32, u8)> for XYZ { fn from(xyz: (u32, u32, u8)) -> Self { XYZ(xyz.0, xyz.1, xyz.2) } -} \ No newline at end of file +} diff --git a/tests/test_rust_cli.py b/tests/test_rust_cli.py index b2c65036..49857c62 100644 --- a/tests/test_rust_cli.py +++ b/tests/test_rust_cli.py @@ -289,135 +289,161 @@ def test_cli_children(self) -> None: # SHAPES TESTS (TODO) # =================== -# def test_cli_shapes_failure() -> None: -# runner = CliRunner() -# result = runner.invoke(cli, ["shapes"], "0") -# assert result.exit_code == 2 - - -# def test_cli_shapes() -> None: -# runner = CliRunner() -# result = runner.invoke(cli, ["shapes", "--precision", "6"], "[106, 193, 9]") -# assert result.exit_code == 0 -# assert ( -# result.output -# == '{"bbox": [-105.46875, 39.909736, -104.765625, 40.446947], "geometry": {"coordinates": [[[-105.46875, 39.909736], [-105.46875, 40.446947], [-104.765625, 40.446947], [-104.765625, 39.909736], [-105.46875, 39.909736]]], "type": "Polygon"}, "id": "(106, 193, 9)", "properties": {"title": "XYZ tile (106, 193, 9)"}, "type": "Feature"}\n' -# ) - -# def test_cli_shapes_arg() -> None: -# runner = CliRunner() -# result = runner.invoke(cli, ["shapes", "[106, 193, 9]", "--precision", "6"]) -# assert result.exit_code == 0 -# result_output_json = json.loads(result.output) - -# # '{"bbox": [-105.46875, 39.909736, -104.765625, 40.446947], "geometry": {"coordinates": [[[-105.46875, 39.909736], [-105.46875, 40.446947], [-104.765625, 40.446947], [-104.765625, 39.909736], [-105.46875, 39.909736]]], "type": "Polygon"}, "id": "(106, 193, 9)", "properties": {"title": "XYZ tile (106, 193, 9)"}, "type": "Feature"}\n' -# expected_dict = { -# "bbox": [-105.46875, 39.909736, -104.765625, 40.446947], -# "geometry": { -# "coordinates": [ -# [ -# [-105.46875, 39.909736], -# [-105.46875, 40.446947], -# [-104.765625, 40.446947], -# [-104.765625, 39.909736], -# [-105.46875, 39.909736], -# ] -# ], -# "type": "Polygon", -# }, -# "id": "(106, 193, 9)", -# "properties": {"title": "XYZ tile (106, 193, 9)"}, -# "type": "Feature", -# } - -# assert result_output_json == expected_dict - - -# def test_cli_shapes_buffer() -> None: -# runner = CliRunner() -# result = runner.invoke( -# cli, ["shapes", "[106, 193, 9]", "--buffer", "1.0", "--precision", "6"] -# ) -# assert result.exit_code == 0 -# assert ( -# result.output -# == '{"bbox": [-106.46875, 38.909736, -103.765625, 41.446947], "geometry": {"coordinates": [[[-106.46875, 38.909736], [-106.46875, 41.446947], [-103.765625, 41.446947], [-103.765625, 38.909736], [-106.46875, 38.909736]]], "type": "Polygon"}, "id": "(106, 193, 9)", "properties": {"title": "XYZ tile (106, 193, 9)"}, "type": "Feature"}\n' -# ) - - -# def test_cli_shapes_compact() -> None: -# """Output is compact.""" -# runner = CliRunner() -# result = runner.invoke(cli, ["shapes", "--compact"], "[106, 193, 9]") -# assert result.exit_code == 0 -# assert '"type":"Feature"' in result.output.strip() - - -# def test_cli_shapes_indentation() -> None: -# """Output is indented.""" -# runner = CliRunner() -# result = runner.invoke(cli, ["shapes", "--indent", "8"], "[106, 193, 9]") -# assert result.exit_code == 0 -# assert ' "type": "Feature"' in result.output.strip() - - -# def test_cli_shapes_collect() -> None: -# """Shapes are collected into a feature collection.""" -# runner = CliRunner() -# result = runner.invoke(cli, ["shapes", "--collect", "--feature"], "[106, 193, 9]") -# assert result.exit_code == 0 -# assert "FeatureCollection" in result.output - - -# def test_cli_shapes_extents() -> None: -# runner = CliRunner() -# result = runner.invoke( -# cli, ["shapes", "[106, 193, 9]", "--extents", "--mercator", "--precision", "3"] -# ) -# assert result.exit_code == 0 -# assert result.output == "-11740727.545 4852834.052 -11662456.028 4931105.569\n" - - -# def test_cli_shapes_bbox() -> None: -# """JSON text sequences of bboxes are output.""" -# runner = CliRunner() -# result = runner.invoke( -# cli, -# [ -# "shapes", -# "[106, 193, 9]", -# "--seq", -# "--bbox", -# "--mercator", -# "--precision", -# "3", -# ], -# ) -# assert result.exit_code == 0 -# assert ( -# result.output -# == "\x1e\n[-11740727.545, 4852834.052, -11662456.028, 4931105.569]\n" -# ) - - -# def test_cli_shapes_props_fid() -> None: -# runner = CliRunner() -# result = runner.invoke( -# cli, -# [ -# "shapes", -# '{"tile": [106, 193, 9], "properties": {"title": "foo"}, "id": "42"}', -# ], -# ) -# assert result.exit_code == 0 -# assert '"title": "foo"' in result.output -# assert '"id": "42"' in result.output - - -# def test_cli_strict_overlap_contain() -> None: -# runner = CliRunner() -# result1 = runner.invoke(cli, ["shapes"], "[2331,1185,12]") -# assert result1.exit_code == 0 -# result2 = runner.invoke(cli, ["tiles", "12"], result1.output) -# assert result2.exit_code == 0 -# assert result2.output == "[2331, 1185, 12]\n" + +def test_cli_shapes_failure() -> None: + result = _run_cli(["shapes"], "0") + assert result.returncode != 0 + + +def test_cli_shapes() -> None: + result = _run_cli(["shapes", "--precision", "6"], "[106, 193, 9]") + assert result.returncode == 0 + expected = { + "bbox": [-105.46875, 39.909736, -104.765625, 40.446947], + "geometry": { + "coordinates": [ + [ + [-105.46875, 39.909736], + [-105.46875, 40.446947], + [-104.765625, 40.446947], + [-104.765625, 39.909736], + [-105.46875, 39.909736], + ] + ], + "type": "Polygon", + }, + "id": "(106, 193, 9)", + "properties": {"title": "XYZ tile (106, 193, 9)"}, + "type": "Feature", + } + assert json.loads(result.stdout) == expected + + +def test_cli_shapes_arg() -> None: + # runner = CliRunner() + # result = runner.invoke(cli, ["shapes", "[106, 193, 9]", "--precision", "6"]) + result = _run_cli(["shapes", "[106, 193, 9]", "--precision", "6"]) + assert result.returncode == 0 + result_output_json = json.loads(result.stdout) + + # '{"bbox": [-105.46875, 39.909736, -104.765625, 40.446947], "geometry": {"coordinates": [[[-105.46875, 39.909736], [-105.46875, 40.446947], [-104.765625, 40.446947], [-104.765625, 39.909736], [-105.46875, 39.909736]]], "type": "Polygon"}, "id": "(106, 193, 9)", "properties": {"title": "XYZ tile (106, 193, 9)"}, "type": "Feature"}\n' + expected_dict = { + "bbox": [-105.46875, 39.909736, -104.765625, 40.446947], + "geometry": { + "coordinates": [ + [ + [-105.46875, 39.909736], + [-105.46875, 40.446947], + [-104.765625, 40.446947], + [-104.765625, 39.909736], + [-105.46875, 39.909736], + ] + ], + "type": "Polygon", + }, + "id": "(106, 193, 9)", + "properties": {"title": "XYZ tile (106, 193, 9)"}, + "type": "Feature", + } + + assert result_output_json == expected_dict + + +def test_cli_shapes_buffer() -> None: + result = _run_cli( + ["shapes", "[106, 193, 9]", "--buffer", "1.0", "--precision", "6"] + ) + assert result.returncode == 0 + # assert ( + # result.stdout + # == '{"bbox": [-106.46875, 38.909736, -103.765625, 41.446947], "geometry": {"coordinates": [[[-106.46875, 38.909736], [-106.46875, 41.446947], [-103.765625, 41.446947], [-103.765625, 38.909736], [-106.46875, 38.909736]]], "type": "Polygon"}, "id": "(106, 193, 9)", "properties": {"title": "XYZ tile (106, 193, 9)"}, "type": "Feature"}\n' + # ) + expected = { + "bbox": [-106.46875, 38.909736, -103.765625, 41.446947], + "geometry": { + "coordinates": [ + [ + [-106.46875, 38.909736], + [-106.46875, 41.446947], + [-103.765625, 41.446947], + [-103.765625, 38.909736], + [-106.46875, 38.909736], + ] + ], + "type": "Polygon", + }, + "id": "(106, 193, 9)", + "properties": {"title": "XYZ tile (106, 193, 9)"}, + "type": "Feature", + } + assert json.loads(result.stdout) == expected + + +@pytest.mark.skip(reason="not implemented") +def test_cli_shapes_compact() -> None: + """Output is compact.""" + result = _run_cli(["shapes", "--compact"], "[106, 193, 9]") + assert result.returncode == 0 + assert '"type":"Feature"' in result.stdout.strip() + + +@pytest.mark.skip(reason="not implemented b/c why would I/anyone ever need that...") +def test_cli_shapes_indentation() -> None: + """Output is indented.""" + result = _run_cli(["shapes", "--indent", "8"], "[106, 193, 9]") + assert result.returncode == 0 + assert ' "type": "Feature"' in result.stdout.strip() + + +def test_cli_shapes_collect() -> None: + """Shapes are collected into a feature collection.""" + result = _run_cli(["shapes", "--collect", "--feature"], "[106, 193, 9]") + assert result.returncode == 0 + assert "FeatureCollection" in result.stdout + + +def test_cli_shapes_extents() -> None: + result = _run_cli( + ["shapes", "[106, 193, 9]", "--extents", "--mercator", "--precision", "3"] + ) + assert result.returncode == 0 + assert result.stdout == "-11740727.545 4852834.052 -11662456.028 4931105.569\n" + + +def test_cli_shapes_bbox() -> None: + """JSON text sequences of bboxes are output.""" + result = _run_cli( + [ + "shapes", + "[106, 193, 9]", + "--seq", + "--bbox", + "--mercator", + "--precision", + "3", + ], + ) + assert result.returncode == 0 + assert ( + result.stdout == "\x1e\n[-11740727.545,4852834.052,-11662456.028,4931105.569]\n" + ) + + +def test_cli_shapes_props_fid() -> None: + result = _run_cli( + [ + "shapes", + '{"tile": [106, 193, 9], "properties": {"title": "foo"}, "id": "42"}', + ], + ) + assert result.returncode == 0 + assert '"title":"foo"' in result.stdout + assert '"id":"42"' in result.stdout + + +def test_cli_strict_overlap_contain() -> None: + result1 = _run_cli(["shapes"], "[2331,1185,12]") + assert result1.returncode == 0 + result2 = _run_cli(["tiles", "12"], result1.stdout) + assert result2.returncode == 0 + assert result2.stdout == "[2331, 1185, 12]\n" From f1d2f45f8568e571c94b6b82979a1b1b54dba644 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 15:38:13 -0800 Subject: [PATCH 69/80] tiny fix --- crates/utiles-cli/src/shapes.rs | 2 +- tests/test_rust_cli.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/utiles-cli/src/shapes.rs b/crates/utiles-cli/src/shapes.rs index a9be7dc3..547da228 100644 --- a/crates/utiles-cli/src/shapes.rs +++ b/crates/utiles-cli/src/shapes.rs @@ -4,7 +4,7 @@ use serde_json::{Map, Value}; use tracing::debug; use utiles::projection::Projection; use utiles::tile::FeatureOptions; -use utiles::{tile, Tile}; +use utiles::{Tile}; // #[group(required = false, id="projected")] #[derive(Args, Debug)] diff --git a/tests/test_rust_cli.py b/tests/test_rust_cli.py index 49857c62..5c8922c8 100644 --- a/tests/test_rust_cli.py +++ b/tests/test_rust_cli.py @@ -1,6 +1,5 @@ """Utiles rust cli tests""" import json -import os import sys from json import dumps as stringify from subprocess import CompletedProcess, run From d9c9debe2374e7cf08f28a1eed457eb90ca86d9e Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 15:42:09 -0800 Subject: [PATCH 70/80] AHA! --- tests/{test_rust_cli.py => cli/test_mt.py} | 281 ++++++++++----------- 1 file changed, 139 insertions(+), 142 deletions(-) rename tests/{test_rust_cli.py => cli/test_mt.py} (66%) diff --git a/tests/test_rust_cli.py b/tests/cli/test_mt.py similarity index 66% rename from tests/test_rust_cli.py rename to tests/cli/test_mt.py index 5c8922c8..965d07d9 100644 --- a/tests/test_rust_cli.py +++ b/tests/cli/test_mt.py @@ -288,161 +288,158 @@ def test_cli_children(self) -> None: # SHAPES TESTS (TODO) # =================== +class TestShapes: -def test_cli_shapes_failure() -> None: - result = _run_cli(["shapes"], "0") - assert result.returncode != 0 - - -def test_cli_shapes() -> None: - result = _run_cli(["shapes", "--precision", "6"], "[106, 193, 9]") - assert result.returncode == 0 - expected = { - "bbox": [-105.46875, 39.909736, -104.765625, 40.446947], - "geometry": { - "coordinates": [ - [ - [-105.46875, 39.909736], - [-105.46875, 40.446947], - [-104.765625, 40.446947], - [-104.765625, 39.909736], - [-105.46875, 39.909736], - ] - ], - "type": "Polygon", - }, - "id": "(106, 193, 9)", - "properties": {"title": "XYZ tile (106, 193, 9)"}, - "type": "Feature", - } - assert json.loads(result.stdout) == expected - - -def test_cli_shapes_arg() -> None: - # runner = CliRunner() - # result = runner.invoke(cli, ["shapes", "[106, 193, 9]", "--precision", "6"]) - result = _run_cli(["shapes", "[106, 193, 9]", "--precision", "6"]) - assert result.returncode == 0 - result_output_json = json.loads(result.stdout) - - # '{"bbox": [-105.46875, 39.909736, -104.765625, 40.446947], "geometry": {"coordinates": [[[-105.46875, 39.909736], [-105.46875, 40.446947], [-104.765625, 40.446947], [-104.765625, 39.909736], [-105.46875, 39.909736]]], "type": "Polygon"}, "id": "(106, 193, 9)", "properties": {"title": "XYZ tile (106, 193, 9)"}, "type": "Feature"}\n' - expected_dict = { - "bbox": [-105.46875, 39.909736, -104.765625, 40.446947], - "geometry": { - "coordinates": [ - [ - [-105.46875, 39.909736], - [-105.46875, 40.446947], - [-104.765625, 40.446947], - [-104.765625, 39.909736], - [-105.46875, 39.909736], - ] - ], - "type": "Polygon", - }, - "id": "(106, 193, 9)", - "properties": {"title": "XYZ tile (106, 193, 9)"}, - "type": "Feature", - } - assert result_output_json == expected_dict + def test_cli_shapes_failure(self) -> None: + result = _run_cli(["shapes"], "0") + assert result.returncode != 0 -def test_cli_shapes_buffer() -> None: - result = _run_cli( - ["shapes", "[106, 193, 9]", "--buffer", "1.0", "--precision", "6"] - ) - assert result.returncode == 0 - # assert ( - # result.stdout - # == '{"bbox": [-106.46875, 38.909736, -103.765625, 41.446947], "geometry": {"coordinates": [[[-106.46875, 38.909736], [-106.46875, 41.446947], [-103.765625, 41.446947], [-103.765625, 38.909736], [-106.46875, 38.909736]]], "type": "Polygon"}, "id": "(106, 193, 9)", "properties": {"title": "XYZ tile (106, 193, 9)"}, "type": "Feature"}\n' - # ) - expected = { - "bbox": [-106.46875, 38.909736, -103.765625, 41.446947], - "geometry": { - "coordinates": [ - [ - [-106.46875, 38.909736], - [-106.46875, 41.446947], - [-103.765625, 41.446947], - [-103.765625, 38.909736], - [-106.46875, 38.909736], - ] - ], - "type": "Polygon", - }, - "id": "(106, 193, 9)", - "properties": {"title": "XYZ tile (106, 193, 9)"}, - "type": "Feature", - } - assert json.loads(result.stdout) == expected + def test_cli_shapes(self) -> None: + result = _run_cli(["shapes", "--precision", "6"], "[106, 193, 9]") + assert result.returncode == 0 + expected = { + "bbox": [-105.46875, 39.909736, -104.765625, 40.446947], + "geometry": { + "coordinates": [ + [ + [-105.46875, 39.909736], + [-105.46875, 40.446947], + [-104.765625, 40.446947], + [-104.765625, 39.909736], + [-105.46875, 39.909736], + ] + ], + "type": "Polygon", + }, + "id": "(106, 193, 9)", + "properties": {"title": "XYZ tile (106, 193, 9)"}, + "type": "Feature", + } + assert json.loads(result.stdout) == expected -@pytest.mark.skip(reason="not implemented") -def test_cli_shapes_compact() -> None: - """Output is compact.""" - result = _run_cli(["shapes", "--compact"], "[106, 193, 9]") - assert result.returncode == 0 - assert '"type":"Feature"' in result.stdout.strip() + def test_cli_shapes_arg(self) -> None: + # runner = CliRunner() + # result = runner.invoke(cli, ["shapes", "[106, 193, 9]", "--precision", "6"]) + result = _run_cli(["shapes", "[106, 193, 9]", "--precision", "6"]) + assert result.returncode == 0 + result_output_json = json.loads(result.stdout) + + expected_dict = { + "bbox": [-105.46875, 39.909736, -104.765625, 40.446947], + "geometry": { + "coordinates": [ + [ + [-105.46875, 39.909736], + [-105.46875, 40.446947], + [-104.765625, 40.446947], + [-104.765625, 39.909736], + [-105.46875, 39.909736], + ] + ], + "type": "Polygon", + }, + "id": "(106, 193, 9)", + "properties": {"title": "XYZ tile (106, 193, 9)"}, + "type": "Feature", + } + assert result_output_json == expected_dict -@pytest.mark.skip(reason="not implemented b/c why would I/anyone ever need that...") -def test_cli_shapes_indentation() -> None: - """Output is indented.""" - result = _run_cli(["shapes", "--indent", "8"], "[106, 193, 9]") - assert result.returncode == 0 - assert ' "type": "Feature"' in result.stdout.strip() + def test_cli_shapes_buffer(self) -> None: + result = _run_cli( + ["shapes", "[106, 193, 9]", "--buffer", "1.0", "--precision", "6"] + ) + assert result.returncode == 0 + expected = { + "bbox": [-106.46875, 38.909736, -103.765625, 41.446947], + "geometry": { + "coordinates": [ + [ + [-106.46875, 38.909736], + [-106.46875, 41.446947], + [-103.765625, 41.446947], + [-103.765625, 38.909736], + [-106.46875, 38.909736], + ] + ], + "type": "Polygon", + }, + "id": "(106, 193, 9)", + "properties": {"title": "XYZ tile (106, 193, 9)"}, + "type": "Feature", + } + assert json.loads(result.stdout) == expected -def test_cli_shapes_collect() -> None: - """Shapes are collected into a feature collection.""" - result = _run_cli(["shapes", "--collect", "--feature"], "[106, 193, 9]") - assert result.returncode == 0 - assert "FeatureCollection" in result.stdout + @pytest.mark.skip(reason="not implemented") + def test_cli_shapes_compact(self) -> None: + """Output is compact.""" + result = _run_cli(["shapes", "--compact"], "[106, 193, 9]") + assert result.returncode == 0 + assert '"type":"Feature"' in result.stdout.strip() -def test_cli_shapes_extents() -> None: - result = _run_cli( - ["shapes", "[106, 193, 9]", "--extents", "--mercator", "--precision", "3"] - ) - assert result.returncode == 0 - assert result.stdout == "-11740727.545 4852834.052 -11662456.028 4931105.569\n" - - -def test_cli_shapes_bbox() -> None: - """JSON text sequences of bboxes are output.""" - result = _run_cli( - [ - "shapes", - "[106, 193, 9]", - "--seq", - "--bbox", - "--mercator", - "--precision", - "3", - ], - ) - assert result.returncode == 0 - assert ( - result.stdout == "\x1e\n[-11740727.545,4852834.052,-11662456.028,4931105.569]\n" - ) + @pytest.mark.skip(reason="not implemented b/c why would I/anyone ever need that...") + def test_cli_shapes_indentation(self) -> None: + """Output is indented.""" + result = _run_cli(["shapes", "--indent", "8"], "[106, 193, 9]") + assert result.returncode == 0 + assert ' "type": "Feature"' in result.stdout.strip() -def test_cli_shapes_props_fid() -> None: - result = _run_cli( - [ - "shapes", - '{"tile": [106, 193, 9], "properties": {"title": "foo"}, "id": "42"}', - ], - ) - assert result.returncode == 0 - assert '"title":"foo"' in result.stdout - assert '"id":"42"' in result.stdout + + def test_cli_shapes_collect(self) -> None: + """Shapes are collected into a feature collection.""" + result = _run_cli(["shapes", "--collect", "--feature"], "[106, 193, 9]") + assert result.returncode == 0 + assert "FeatureCollection" in result.stdout + + + def test_cli_shapes_extents(self) -> None: + result = _run_cli( + ["shapes", "[106, 193, 9]", "--extents", "--mercator", "--precision", "3"] + ) + assert result.returncode == 0 + assert result.stdout == "-11740727.545 4852834.052 -11662456.028 4931105.569\n" + + + def test_cli_shapes_bbox(self) -> None: + """JSON text sequences of bboxes are output.""" + result = _run_cli( + [ + "shapes", + "[106, 193, 9]", + "--seq", + "--bbox", + "--mercator", + "--precision", + "3", + ], + ) + assert result.returncode == 0 + assert ( + result.stdout == "\x1e\n[-11740727.545,4852834.052,-11662456.028,4931105.569]\n" + ) + + + def test_cli_shapes_props_fid(self) -> None: + result = _run_cli( + [ + "shapes", + '{"tile": [106, 193, 9], "properties": {"title": "foo"}, "id": "42"}', + ], + ) + assert result.returncode == 0 + assert '"title":"foo"' in result.stdout + assert '"id":"42"' in result.stdout -def test_cli_strict_overlap_contain() -> None: - result1 = _run_cli(["shapes"], "[2331,1185,12]") - assert result1.returncode == 0 - result2 = _run_cli(["tiles", "12"], result1.stdout) - assert result2.returncode == 0 - assert result2.stdout == "[2331, 1185, 12]\n" + def test_cli_strict_overlap_contain(self) -> None: + result1 = _run_cli(["shapes"], "[2331,1185,12]") + assert result1.returncode == 0 + result2 = _run_cli(["tiles", "12"], result1.stdout) + assert result2.returncode == 0 + assert result2.stdout == "[2331, 1185, 12]\n" From 25cb90a03a7c357380c121ea45b1c20fb254feed Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 15:46:20 -0800 Subject: [PATCH 71/80] moved tile feature to own thing --- crates/utiles/src/lib.rs | 1 + crates/utiles/src/tile.rs | 111 +----------------------------- crates/utiles/src/tile_feature.rs | 43 ++++++++++++ 3 files changed, 46 insertions(+), 109 deletions(-) create mode 100644 crates/utiles/src/tile_feature.rs diff --git a/crates/utiles/src/lib.rs b/crates/utiles/src/lib.rs index b5170669..bf15734f 100644 --- a/crates/utiles/src/lib.rs +++ b/crates/utiles/src/lib.rs @@ -28,6 +28,7 @@ mod tile_tuple; pub mod tilejson; pub mod traits; pub mod zoom; +mod tile_feature; /// Tile macro to create a new tile. /// - do you need this? probably not diff --git a/crates/utiles/src/tile.rs b/crates/utiles/src/tile.rs index 5e1334c4..b3a86506 100644 --- a/crates/utiles/src/tile.rs +++ b/crates/utiles/src/tile.rs @@ -15,6 +15,7 @@ use crate::{ bounds, children, flipy, ll, lr, neighbors, parent, pmtiles, quadkey2tile, siblings, traits, ul, ur, xy, xyz2quadkey, }; +use crate::tile_feature::TileFeature; #[derive(Debug, Serialize, Deserialize)] pub struct TileFeatureGeometry { @@ -44,46 +45,6 @@ impl Default for FeatureOptions { } } -#[derive(Debug, Serialize, Deserialize)] -pub struct TileFeature { - pub id: String, - - #[serde(rename = "type")] - pub type_: String, - - pub geometry: TileFeatureGeometry, - pub bbox: (f64, f64, f64, f64), - pub properties: Map, -} - -impl TileFeature { - pub fn to_json(&self) -> String { - serde_json::to_string(self).unwrap() - } - - pub fn bbox_lons(&self) -> Vec { - vec![self.bbox.0, self.bbox.2] - } - - pub fn bbox_lats(&self) -> Vec { - vec![self.bbox.1, self.bbox.3] - } - - pub fn extents_string(&self) -> String { - format!( - "{} {} {} {}", - self.bbox.0, self.bbox.1, self.bbox.2, self.bbox.3 - ) - } - - pub fn bbox_json(&self) -> String { - format!( - "[{},{},{},{}]", - self.bbox.0, self.bbox.1, self.bbox.2, self.bbox.3 - ) - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Tile { pub x: u32, @@ -477,29 +438,11 @@ impl Tile { pub fn feature( &self, opts: &FeatureOptions, - // fid: Option, // feature id - // props: Option>, - // projected: Option, - // buffer: Option, - // precision: Option, ) -> Result> { - // Convert the arguments to Rust values - // let pytile: PyTile = tile.into(); - // let tile = pytile.tuple(); - // let opts = options.unwrap_or_default(); - - // let (x, y, z) = self.tuple(); - // let fid = opts.fid.unwrap_or_default(); - // let props = opts.props; - // let projection = opts.projection; - // .unwrap_or_else( Projection::Geographic); - // let projected = opts.projected.unwrap_or_else(|| "geographic".to_string()); let buffer = opts.buffer.unwrap_or(0.0); let precision = opts.precision.unwrap_or(-1); - // Compute the bounds let (west, south, east, north) = self.bbox(); - // Handle projected coordinates let (mut west, mut south, mut east, mut north) = match opts.projection { // Projection::Geographic=> (west, south, east, north), @@ -534,12 +477,7 @@ impl Tile { west.max(east), south.max(north), ); - // let bbox = [ - // west.min(east), - // south.min(north), - // west.max(east), - // south.max(north), - // ]; + let xyz = self.tuple_string(); let geometry_coordinates = vec![vec![ vec![west, south], vec![west, north], @@ -547,25 +485,6 @@ impl Tile { vec![east, south], vec![west, south], ]]; - // - // let geometry_items = vec![ - // ("type".to_string(), "Polygon".to_object(py)), - // ( - // "coordinates".to_string(), - // geometry_coordinates.to_object(py), - // ), - // ] - // .into_iter() - // .collect::>(); - - let xyz = self.tuple_string(); - // let properties_vec= vec![ - // ("title".to_string(), Value::from(format!("XYZ tile {xyz}"))), - // // ("west".to_string(), west(py)), - // // ("south".to_string(), south.into_py(py)), - // // ("east".to_string(), east.into_py(py)), - // // ("north".to_string(), north.into_py(py)), - // ]; let mut properties: Map = Map::new(); properties.insert("title".to_string(), Value::from(format!("XYZ tile {xyz}"))); properties.extend(opts.props.clone().unwrap_or_default()); @@ -583,32 +502,6 @@ impl Tile { bbox: bbox, properties: properties, }; - - // Create the feature dictionary - // let mut feature_dict = HashMap::new(); - // feature_dict.insert("type".to_string(), "Feature".to_object(py)); - // feature_dict.insert("bbox".to_string(), bbox.to_object(py)); - // feature_dict.insert("id".to_string(), xyz.to_object(py)); - // feature_dict.insert("geometry".to_string(), geometry_items.to_object(py)); - - // Create the properties dictionary - // let mut properties_dict: HashMap> = HashMap::new(); - // properties_dict - // .insert("title".to_string(), format!("XYZ tile {xyz}").into_py(py)); - // if !props.is_empty() { - // let props: PyResult)>> = props - // .into_iter() - // .map(|(k, v)| Ok((k, v.into_py(py)))) - // .collect(); - // properties_dict.extend(props?); - // } - // feature_dict.insert("properties".to_string(), properties_dict.to_object(py)); - - // Add the feature id if provided - // if !fid.is_empty() { - // feature_dict.insert("id".to_string(), fid.to_object(py)); - // } - // Ok(feature_dict) Ok(tile_feature) } } diff --git a/crates/utiles/src/tile_feature.rs b/crates/utiles/src/tile_feature.rs new file mode 100644 index 00000000..50d5aca1 --- /dev/null +++ b/crates/utiles/src/tile_feature.rs @@ -0,0 +1,43 @@ +use serde::{Deserialize, Serialize}; +use serde_json::{Map, Value}; +use crate::tile::TileFeatureGeometry; + +#[derive(Debug, Serialize, Deserialize)] +pub struct TileFeature { + pub id: String, + + #[serde(rename = "type")] + pub type_: String, + + pub geometry: TileFeatureGeometry, + pub bbox: (f64, f64, f64, f64), + pub properties: Map, +} + +impl TileFeature { + pub fn to_json(&self) -> String { + serde_json::to_string(self).unwrap() + } + + pub fn bbox_lons(&self) -> Vec { + vec![self.bbox.0, self.bbox.2] + } + + pub fn bbox_lats(&self) -> Vec { + vec![self.bbox.1, self.bbox.3] + } + + pub fn extents_string(&self) -> String { + format!( + "{} {} {} {}", + self.bbox.0, self.bbox.1, self.bbox.2, self.bbox.3 + ) + } + + pub fn bbox_json(&self) -> String { + format!( + "[{},{},{},{}]", + self.bbox.0, self.bbox.1, self.bbox.2, self.bbox.3 + ) + } +} From e10d4ae5f220cf7d106a0473af0b13d6d8469b5f Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Thu, 9 Nov 2023 15:55:10 -0800 Subject: [PATCH 72/80] metadata dump --- crates/utiles-cli/src/cli.rs | 74 +++++++++++++---------- crates/utiles-cli/src/shapes.rs | 2 +- crates/utiles/src/mbtiles/metadata_row.rs | 4 +- 3 files changed, 45 insertions(+), 35 deletions(-) diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index 23981b2b..5bdf1fbe 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -2,8 +2,9 @@ use std::io::{self, Write}; use std::path::Path; use clap::{Parser, Subcommand, ValueEnum}; -use tracing::debug; +use tracing::{debug, error}; use tracing_subscriber::EnvFilter; +use utiles::mbtiles::metadata_row::MbtilesMetadataRow; use utiles::parsing::parse_bbox; use utiles::tilejson::tilejson_stringify; use utiles::tiles; @@ -65,6 +66,17 @@ pub enum Commands { min: bool, }, + #[command(name = "meta", about = "echo metadata (table) as json", long_about = None)] + Meta { + #[arg(required = true, help = "mbtiles filepath")] + filepath: String, + + #[arg(required = false, short, long, help= "compact json", action = clap::ArgAction::SetTrue)] + min: bool, + // #[arg(required = false, short, long, help= "compact json", action = clap::ArgAction::SetTrue)] + // raw: bool, + }, + // ======================================================================== // TILE CLI UTILS - MERCANTILE LIKE CLI // ======================================================================== @@ -80,9 +92,6 @@ pub enum Commands { seq: bool, }, - // =================== - // NOT IMPLEMENTED YET - // =================== #[command(name = "quadkey", visible_alias = "qk", about = "convert xyz <-> quadkey", long_about = None)] Quadkey { #[arg(required = false)] @@ -244,6 +253,33 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { // throw not implemented error panic!("not implemented (yet)") } + Commands::Meta { filepath, min } => { + debug!("meta: {filepath}"); + // check that filepath exists and is file + let filepath = Path::new(&filepath); + if !filepath.exists() { + panic!("File does not exist: {}", filepath.display()); + } + if !filepath.is_file() { + panic!("Not a file: {filepath}", filepath = filepath.display()); + } + let mbtiles: Mbtiles = Mbtiles::from(filepath); + // let mbtiles = Mbtiles::from_filepath(&filepath).unwrap(); + let metadata_rows = mbtiles.metadata().unwrap(); + if min { + let s = + serde_json::to_string::>(&metadata_rows) + .unwrap(); + println!("{s}"); + } else { + let s = serde_json::to_string_pretty::>( + &metadata_rows, + ) + .unwrap(); + println!("{s}"); + } + } + Commands::Tilejson { filepath, min } => { debug!("tilejson: {filepath}"); // check that filepath exists and is file @@ -263,10 +299,6 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { // mercantile cli like Commands::Quadkey { input } => { - // let thingy = StdInterator::new(quadkey.quadkey).unwrap(); - // for line in thingy { - // println!("Line from stdin: `{}`", line.unwrap()); - // } let input_lines = StdInterator::new(input).unwrap(); let lines = input_lines .filter(|l| !l.is_err()) @@ -279,23 +311,16 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { // treat as tile let tile = Tile::from_json_arr(&lstr); println!("{}", tile.quadkey()); - // let qk = utiles::xyz2quadkey(t.west, t.south, t.zoom); - // println!("{}", qk); - // let tile = parse_bbox(&lstr).unwrap(); - // let qk = utiles::xyz2quadkey(tile.west, tile.south, tile.zoom); - // println!("{}", qk); } else { // treat as quadkey let qk = lstr; let tile = Tile::from_quadkey(&qk); if tile.is_err() { + error!("Invalid quadkey: {qk}"); println!("Invalid quadkey: {qk}"); } else { println!("{}", tile.unwrap().json_arr()); } - - // let (x, y, z) = utiles::quadkey2xyz(&qk); - // println!("{} {} {}", x, y, z); } } } @@ -317,23 +342,6 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { let rs = if seq { "\x1e\n" } else { "" }; println!("{}{}", rs, tile.json_arr()); } - // - // - // .flat_map(|b| tiles( - // (b.west, b.south, b.east, b.north), - // ZoomOrZooms::Zoom(zoom), - // )).enumerate(); - // // let bboxes = lines - // for (i, tile) in tiles { - // let rs = if seq { "\x1e\n" } else { "" }; - // println!("{}{}", rs, tile.json_arr()); - // // call loop_fn if it's defined every 1000 iterations for signal break - // if i % 1024 == 0 { - // if let Some(f) = loop_fn { - // f(); - // } - // } - // } } Commands::Tiles { zoom, input, seq } => { let input_lines = StdInterator::new(input).unwrap(); diff --git a/crates/utiles-cli/src/shapes.rs b/crates/utiles-cli/src/shapes.rs index 547da228..4f2bcd9b 100644 --- a/crates/utiles-cli/src/shapes.rs +++ b/crates/utiles-cli/src/shapes.rs @@ -4,7 +4,7 @@ use serde_json::{Map, Value}; use tracing::debug; use utiles::projection::Projection; use utiles::tile::FeatureOptions; -use utiles::{Tile}; +use utiles::Tile; // #[group(required = false, id="projected")] #[derive(Args, Debug)] diff --git a/crates/utiles/src/mbtiles/metadata_row.rs b/crates/utiles/src/mbtiles/metadata_row.rs index 3c941d85..e5a53ed0 100644 --- a/crates/utiles/src/mbtiles/metadata_row.rs +++ b/crates/utiles/src/mbtiles/metadata_row.rs @@ -1,4 +1,6 @@ -#[derive(Debug)] +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] pub struct MbtilesMetadataRow { pub name: String, pub value: String, From 09b4d1e689d392f6a662403492ffe88e3c1f6efe Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Fri, 10 Nov 2023 11:51:07 -0800 Subject: [PATCH 73/80] should be good to go I think --- crates/utiles-cli/src/cli.rs | 195 ++++---------------------- crates/utiles-cli/src/stdinterator.rs | 73 ++-------- python/utiles/__main__.py | 1 + tests/cli/test_mt.py | 16 +-- 4 files changed, 39 insertions(+), 246 deletions(-) diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index 5bdf1fbe..7a207281 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -1,15 +1,15 @@ use std::io::{self, Write}; use std::path::Path; -use clap::{Parser, Subcommand, ValueEnum}; +use clap::{Parser, Subcommand}; use tracing::{debug, error}; use tracing_subscriber::EnvFilter; +use utiles::{bounding_tile, Tile}; use utiles::mbtiles::metadata_row::MbtilesMetadataRow; use utiles::parsing::parse_bbox; use utiles::tilejson::tilejson_stringify; use utiles::tiles; use utiles::zoom::ZoomOrZooms; -use utiles::{bounding_tile, Tile}; use utilesqlite::mbtiles::Mbtiles; use crate::shapes::{shapes_main, ShapesArgs}; @@ -25,11 +25,11 @@ pub struct Cli { // debug flag #[arg( - long, - short, - global = true, - default_value = "false", - help = "debug mode" + long, + short, + global = true, + default_value = "false", + help = "debug mode" )] debug: bool, // #[command(flatten , help="verbosity level (-v, -vv, -vvv, -vvvv)" )] @@ -62,7 +62,7 @@ pub enum Commands { #[arg(required = true, help = "mbtiles filepath")] filepath: String, - #[arg(required = false, short, long, help= "compact json", action = clap::ArgAction::SetTrue)] + #[arg(required = false, short, long, help = "compact json", action = clap::ArgAction::SetTrue)] min: bool, }, @@ -71,7 +71,7 @@ pub enum Commands { #[arg(required = true, help = "mbtiles filepath")] filepath: String, - #[arg(required = false, short, long, help= "compact json", action = clap::ArgAction::SetTrue)] + #[arg(required = false, short, long, help = "compact json", action = clap::ArgAction::SetTrue)] min: bool, // #[arg(required = false, short, long, help= "compact json", action = clap::ArgAction::SetTrue)] // raw: bool, @@ -140,73 +140,16 @@ pub enum Commands { #[command(name = "shapes", about = "echo shapes of tile(s) as GeoJSON", long_about = None)] Shapes(ShapesArgs), - // { - // #[arg(required = false)] - // input: Option, - - // #[arg(required = false, long, action = clap::ArgAction::SetTrue)] - // seq: bool, - - // /// Decimal precision of coordinates. - // #[arg(long, value_parser)] - // precision: Option, - - // /// Indentation level for JSON output. - // #[arg(long, value_parser)] - // indent: Option, - - // /// Use compact separators (',', ':'). - // #[arg(long, action)] - // compact: bool, - - // /// Output in geographic coordinates (the default). - // #[arg(long, group = "projected")] - // geographic: bool, - - // /// Output in Web Mercator coordinates. - // #[arg(long, group = "projected")] - // mercator: bool, - - // // /// Write a RS-delimited JSON sequence (default is LF). - // // #[clap(long, action)] - // // seq: bool, - // /// Output as sequence of GeoJSON features (the default). - // // #[clap(long, group = "output_mode")] - // #[arg(required = false, long, action = clap::ArgAction::SetTrue)] - // feature: bool, - - // /// Output as sequence of GeoJSON bbox arrays. - // #[arg(long, group = "output_mode")] - // bbox: bool, - - // /// Output as a GeoJSON feature collections. - // #[arg(long, action)] - // collect: bool, - - // /// Write shape extents as ws-separated strings (default is False). - // #[arg(long, action)] - // extents: bool, - - // /// Shift shape x and y values by a constant number. - // #[arg(long, value_parser)] - // buffer: Option, - // }, -} - -#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq)] -pub enum ColorWhen { - Always, - Auto, - Never, } -impl std::fmt::Display for ColorWhen { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.to_possible_value() - .expect("no values are skipped") - .get_name() - .fmt(f) - } +fn stdin_filtered(input: Option) -> Box>> { + let input_lines = StdInterator::new(input).unwrap(); + let filtered_lines = input_lines + .filter(|l| !l.is_err()) + .filter(|l| !l.as_ref().unwrap().is_empty()) + .filter(|l| l.as_ref().unwrap() != "\x1e"); + let boxed = Box::new(filtered_lines); + boxed } pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { @@ -216,32 +159,17 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { None => std::env::args().collect::>(), }; let args = Cli::parse_from(&argv); - // level is info by default and debug if --debug is passed - // let level = if args.debug { - // tracing::Level::DEBUG - // } else { - // tracing::Level::WARN - // }; - - // install global collector configured based on RUST_LOG env var. - // tracing_subscriber::fmt() - // .with_max_level(level) - // .with_writer(std::io::stderr) - // .finish() - // .init(); - // Configure the filter - let filter = if args.debug { EnvFilter::new("DEBUG") } else { EnvFilter::from_default_env() }; - // Install the global collector configured based on the filter. tracing_subscriber::fmt() .with_env_filter(filter) .with_writer(std::io::stderr) .init(); + debug!("args: {:?}", std::env::args().collect::>()); debug!("argv: {:?}", argv); @@ -275,7 +203,7 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { let s = serde_json::to_string_pretty::>( &metadata_rows, ) - .unwrap(); + .unwrap(); println!("{s}"); } } @@ -299,10 +227,7 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { // mercantile cli like Commands::Quadkey { input } => { - let input_lines = StdInterator::new(input).unwrap(); - let lines = input_lines - .filter(|l| !l.is_err()) - .filter(|l| !l.as_ref().unwrap().is_empty()); + let lines = stdin_filtered(input); for line in lines { // if the line bgins w '[' treat as tile // otherwise treat as quadkey @@ -325,12 +250,7 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { } } Commands::BoundingTile { input, seq } => { - let input_lines = StdInterator::new(input).unwrap(); - let lines = input_lines - .filter(|l| !l.is_err()) - .filter(|l| !l.as_ref().unwrap().is_empty()) - .filter(|l| l.as_ref().unwrap() != "\x1e"); - + let lines = stdin_filtered(input); let bboxes = lines.map(|l| { let s = l.unwrap(); debug!("l: {:?}", s); @@ -344,13 +264,8 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { } } Commands::Tiles { zoom, input, seq } => { - let input_lines = StdInterator::new(input).unwrap(); - let lines = input_lines - .filter(|l| !l.is_err()) - .filter(|l| !l.as_ref().unwrap().is_empty()) - .filter(|l| l.as_ref().unwrap() != "\x1e"); + let lines = stdin_filtered(input); let mut stdout = io::stdout(); - let tiles = lines .map(|l| { let s = l.unwrap(); @@ -375,60 +290,10 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { } } stdout.flush().unwrap(); - - // for tile in tiles { - // let tstr = tile.json_arr(); - // // RS char if seq else "" - // let rs = if seq { "\x1e\n" } else { "" }; - // println!("{}{}", rs, tstr); - // // println!("{}", tile.json_arr()); - // - // // call loop_fn if it's defined - // niter += 1; - // - // // call fn every 1000 iterations - // if niter % 1000 == 0 { - // if let Some(f) = loop_fn { - // f(); - // } - // } - // } - // for line in input_lines - // .filter(|l| !l.is_err()) - // .filter(|l| !l.as_ref().unwrap().is_empty()) - // { - // let lstr = line.unwrap(); - // let thingy = parse_bbox( - // &lstr, - // ).unwrap(); - // for tile in tiles( - // (thingy.west, thingy.south, thingy.east, thingy.north), - // ZoomOrZooms::Zoom(zoom), - // ) { - // let tstr = tile.json_arr(); - // // RS char if seq else "" - // let rs = if seq { "\x1e\n" } else { "" }; - // println!("{}{}", rs, tstr); - // // println!("{}", tile.json_arr()); - // - // // call loop_fn if it's defined - // niter += 1; - // - // // call fn every 1000 iterations - // if niter % 1000 == 0 { - // if let Some(f) = loop_fn { - // f(); - // } - // } - // } - // } } + Commands::Neighbors { input, seq } => { - let input_lines = StdInterator::new(input).unwrap(); - let lines = input_lines - .filter(|l| !l.is_err()) - .filter(|l| !l.as_ref().unwrap().is_empty()) - .filter(|l| l.as_ref().unwrap() != "\x1e"); + let lines = stdin_filtered(input); let tiles = lines.map(|l| Tile::from_json(&l.unwrap())); for tile in tiles { let neighbors = tile.neighbors(); @@ -440,11 +305,7 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { } Commands::Children { input, seq, depth } => { - let input_lines = StdInterator::new(input).unwrap(); - let lines = input_lines - .filter(|l| !l.is_err()) - .filter(|l| !l.as_ref().unwrap().is_empty()) - .filter(|l| l.as_ref().unwrap() != "\x1e"); + let lines = stdin_filtered(input); let tiles = lines.map(|l| Tile::from_json(&l.unwrap())); for tile in tiles { let children = tile.children(Option::from(tile.z + depth)); @@ -456,11 +317,7 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { } Commands::Parent { input, seq, depth } => { - let input_lines = StdInterator::new(input).unwrap(); - let lines = input_lines - .filter(|l| !l.is_err()) - .filter(|l| !l.as_ref().unwrap().is_empty()) - .filter(|l| l.as_ref().unwrap() != "\x1e"); + let lines = stdin_filtered(input); let tiles = lines.map(|l| Tile::from_json(&l.unwrap())); for tile in tiles { let nup = tile.z as i32 - depth as i32; diff --git a/crates/utiles-cli/src/stdinterator.rs b/crates/utiles-cli/src/stdinterator.rs index ae2f6b61..05624444 100644 --- a/crates/utiles-cli/src/stdinterator.rs +++ b/crates/utiles-cli/src/stdinterator.rs @@ -1,71 +1,8 @@ -// use std::io; -// use std::io::BufRead; -// use clap::builder::Str; -// use tracing::debug; -// -// pub enum StdInteratorSource { -// Args(Vec<&str>), -// StdinRead(Box), -// } -// -// pub struct StdInterator { -// source: StdInteratorSource, -// } -// -// impl StdInterator { -// pub fn new(input: Option) -> io::Result { -// let source = match input { -// Some(file_content) => { -// if file_content == "-" { -// debug!("reading from stdin - got '-'"); -// let reader = Box::new(io::BufReader::new(io::stdin())); -// StdInteratorSource::StdinRead(reader) -// } else { -// debug!("reading from args: {:?}", file_content); -// StdInteratorSource::Args( -// file_content.splitn( -// file_content.matches(char::is_whitespace).count() + 1, -// char::is_whitespace, -// ).collect::>() -// // ).map(|s| s.to_string()).collect(), -// ) -// } -// } -// None => { -// let reader = Box::new(io::BufReader::new(io::stdin())); -// debug!("reading from stdin - no args"); -// StdInteratorSource::StdinRead(reader) -// } -// }; -// Ok(Self { source }) -// } -// } -// -// impl Iterator for StdInterator { -// type Item = io::Result< >; -// fn next(&mut self) -> Option { -// match &mut self.source { -// StdInteratorSource::Args(content) => { -// -// } -// StdInteratorSource::StdinRead(reader) => { -// let mut line = String::new(); -// match reader.read_line(&mut line) { -// Ok(0) => None, // EOF -// Ok(_) => { -// let l -// Some(Ok(line.trim_end().to_string())) -// }, -// Err(e) => Some(Err(e)), -// } -// } -// } -// } -// } use std::collections::VecDeque; use std::io; use std::io::BufRead; use tracing::debug; +use std::str::FromStr; pub enum StdInteratorSource { Args(VecDeque), @@ -104,6 +41,14 @@ impl StdInterator { } } +impl FromStr for StdInterator { + type Err = io::Error; + + fn from_str(s: &str) -> Result { + Self::new(Some(s.to_string())) + } +} + impl Iterator for StdInterator { type Item = io::Result; diff --git a/python/utiles/__main__.py b/python/utiles/__main__.py index 8d0155e2..ca3cdf6e 100644 --- a/python/utiles/__main__.py +++ b/python/utiles/__main__.py @@ -69,6 +69,7 @@ def _utiles_ext_info() -> Dict[str, Union[str, int]]: "abspath": os.path.abspath(libutiles.__file__), "fsize": size, "fsize_str": _nbytes_str(size), + "build_profile": libutiles.__build_profile__, } diff --git a/tests/cli/test_mt.py b/tests/cli/test_mt.py index 965d07d9..f3b90681 100644 --- a/tests/cli/test_mt.py +++ b/tests/cli/test_mt.py @@ -288,14 +288,12 @@ def test_cli_children(self) -> None: # SHAPES TESTS (TODO) # =================== -class TestShapes: - +class TestShapes: def test_cli_shapes_failure(self) -> None: result = _run_cli(["shapes"], "0") assert result.returncode != 0 - def test_cli_shapes(self) -> None: result = _run_cli(["shapes", "--precision", "6"], "[106, 193, 9]") assert result.returncode == 0 @@ -319,7 +317,6 @@ def test_cli_shapes(self) -> None: } assert json.loads(result.stdout) == expected - def test_cli_shapes_arg(self) -> None: # runner = CliRunner() # result = runner.invoke(cli, ["shapes", "[106, 193, 9]", "--precision", "6"]) @@ -348,7 +345,6 @@ def test_cli_shapes_arg(self) -> None: assert result_output_json == expected_dict - def test_cli_shapes_buffer(self) -> None: result = _run_cli( ["shapes", "[106, 193, 9]", "--buffer", "1.0", "--precision", "6"] @@ -374,7 +370,6 @@ def test_cli_shapes_buffer(self) -> None: } assert json.loads(result.stdout) == expected - @pytest.mark.skip(reason="not implemented") def test_cli_shapes_compact(self) -> None: """Output is compact.""" @@ -382,7 +377,6 @@ def test_cli_shapes_compact(self) -> None: assert result.returncode == 0 assert '"type":"Feature"' in result.stdout.strip() - @pytest.mark.skip(reason="not implemented b/c why would I/anyone ever need that...") def test_cli_shapes_indentation(self) -> None: """Output is indented.""" @@ -390,14 +384,12 @@ def test_cli_shapes_indentation(self) -> None: assert result.returncode == 0 assert ' "type": "Feature"' in result.stdout.strip() - def test_cli_shapes_collect(self) -> None: """Shapes are collected into a feature collection.""" result = _run_cli(["shapes", "--collect", "--feature"], "[106, 193, 9]") assert result.returncode == 0 assert "FeatureCollection" in result.stdout - def test_cli_shapes_extents(self) -> None: result = _run_cli( ["shapes", "[106, 193, 9]", "--extents", "--mercator", "--precision", "3"] @@ -405,7 +397,6 @@ def test_cli_shapes_extents(self) -> None: assert result.returncode == 0 assert result.stdout == "-11740727.545 4852834.052 -11662456.028 4931105.569\n" - def test_cli_shapes_bbox(self) -> None: """JSON text sequences of bboxes are output.""" result = _run_cli( @@ -421,10 +412,10 @@ def test_cli_shapes_bbox(self) -> None: ) assert result.returncode == 0 assert ( - result.stdout == "\x1e\n[-11740727.545,4852834.052,-11662456.028,4931105.569]\n" + result.stdout + == "\x1e\n[-11740727.545,4852834.052,-11662456.028,4931105.569]\n" ) - def test_cli_shapes_props_fid(self) -> None: result = _run_cli( [ @@ -436,7 +427,6 @@ def test_cli_shapes_props_fid(self) -> None: assert '"title":"foo"' in result.stdout assert '"id":"42"' in result.stdout - def test_cli_strict_overlap_contain(self) -> None: result1 = _run_cli(["shapes"], "[2331,1185,12]") assert result1.returncode == 0 From 4a547de004a2d9b0f8b5216ac27fceb294a37e39 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Fri, 10 Nov 2023 13:33:01 -0800 Subject: [PATCH 74/80] fix cicd? --- .github/workflows/ci.yml | 10 ++++++---- crates/utiles-cli/src/cli.rs | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ff5550f..661be3d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,8 @@ jobs: shell: bash run: | set -e - pip install utiles --find-links dist --force-reinstall -vvv + pip install click + pip install utiles --no-index --no-deps --find-links dist --force-reinstall pip install -r requirements/dev.txt pytest # - name: pytest @@ -86,10 +87,10 @@ jobs: shell: bash run: | set -e - pip install utiles --find-links dist --force-reinstall + pip install click + pip install utiles --no-index --no-deps --find-links dist --force-reinstall pip install -r requirements/dev.txt pytest - macos: runs-on: macos-latest strategy: @@ -116,7 +117,8 @@ jobs: shell: bash run: | set -e - pip install utiles --find-links dist --force-reinstall + pip install click + pip install utiles --no-index --no-deps --find-links dist --force-reinstall pip install -r requirements/dev.txt pytest diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index 7a207281..b3bd0594 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -49,7 +49,7 @@ pub struct InputAndSequenceArgs { #[derive(Debug, Subcommand)] pub enum Commands { - #[command(name = "lint", about = "lint mbtiles file", long_about = None)] + #[command(name = "lint", about = "Lint mbtiles file(s)", long_about = None)] Lint { #[arg(required = true)] filepath: String, @@ -57,7 +57,7 @@ pub enum Commands { #[arg(required = false, long, action = clap::ArgAction::SetTrue)] fix: bool, }, - #[command(name = "tilejson", visible_alias = "tj", about = "echo tilejson", long_about = None)] + #[command(name = "tilejson", visible_alias = "tj", about = "Echo tileson for mbtiles file(s)", long_about = None)] Tilejson { #[arg(required = true, help = "mbtiles filepath")] filepath: String, @@ -66,7 +66,7 @@ pub enum Commands { min: bool, }, - #[command(name = "meta", about = "echo metadata (table) as json", long_about = None)] + #[command(name = "meta", about = "Echo metadata (table) as json", long_about = None)] Meta { #[arg(required = true, help = "mbtiles filepath")] filepath: String, @@ -80,7 +80,7 @@ pub enum Commands { // ======================================================================== // TILE CLI UTILS - MERCANTILE LIKE CLI // ======================================================================== - #[command(name = "tiles", about = "echo tiles of bbox", long_about = None)] + #[command(name = "tiles", about = "Echo tiles of bbox", long_about = None)] Tiles { #[arg(required = true)] zoom: u8, @@ -92,13 +92,13 @@ pub enum Commands { seq: bool, }, - #[command(name = "quadkey", visible_alias = "qk", about = "convert xyz <-> quadkey", long_about = None)] + #[command(name = "quadkey", visible_alias = "qk", about = "Convert to/from quadkey(s)", long_about = None)] Quadkey { #[arg(required = false)] input: Option, }, - #[command(name = "bounding-tile", about = "output tilejson", long_about = None)] + #[command(name = "bounding-tile", about = "Echo the bounding tile of a lonlat/bbox/GeoJSON", long_about = None)] BoundingTile { #[arg(required = false)] input: Option, @@ -106,7 +106,7 @@ pub enum Commands { #[arg(required = false, long, action = clap::ArgAction::SetTrue)] seq: bool, }, - #[command(name = "neighbors", about = "echo neighbors of tile(s)", long_about = None)] + #[command(name = "neighbors", about = "Echo neighbors of tile(s)", long_about = None)] Neighbors { #[arg(required = false)] input: Option, @@ -115,7 +115,7 @@ pub enum Commands { seq: bool, }, - #[command(name = "parent", about = "echo parent of tile(s)", long_about = None)] + #[command(name = "parent", about = "Echo parent of tile(s)", long_about = None)] Parent { #[arg(required = false)] input: Option, @@ -126,7 +126,7 @@ pub enum Commands { #[arg(required = false, long, default_value = "1")] depth: u8, }, - #[command(name = "children", about = "echo children of tile(s)", long_about = None)] + #[command(name = "children", about = "Echo children of tile(s)", long_about = None)] Children { #[arg(required = false)] input: Option, @@ -138,7 +138,7 @@ pub enum Commands { depth: u8, }, - #[command(name = "shapes", about = "echo shapes of tile(s) as GeoJSON", long_about = None)] + #[command(name = "shapes", about = "Echo shapes of tile(s) as GeoJSON", long_about = None)] Shapes(ShapesArgs), } From 9292d88dc853323ee8d7b92168bc42f8c34cb1f8 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Fri, 10 Nov 2023 13:56:14 -0800 Subject: [PATCH 75/80] some cleanup' --- crates/utiles-cli/src/bin.rs | 2 +- crates/utiles-cli/src/cli.rs | 2 +- crates/utiles-cli/src/shapes.rs | 6 +- crates/utiles/src/tile.rs | 6 +- crates/utilesqlite/src/bin.rs | 139 +------------------------ crates/utilesqlite/src/mbtiles.rs | 24 ++--- src/cli.rs | 4 +- src/lib.rs | 3 - src/purgatory/_coords.rs | 167 ------------------------------ 9 files changed, 24 insertions(+), 329 deletions(-) delete mode 100644 src/purgatory/_coords.rs diff --git a/crates/utiles-cli/src/bin.rs b/crates/utiles-cli/src/bin.rs index 3345218e..9c60ab64 100644 --- a/crates/utiles-cli/src/bin.rs +++ b/crates/utiles-cli/src/bin.rs @@ -4,5 +4,5 @@ mod stdinterator; #[tokio::main] async fn main() { - cli::cli_main(Option::None, Option::None) + cli::cli_main(None, None) } diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index b3bd0594..e8ff7e81 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -167,7 +167,7 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { // Install the global collector configured based on the filter. tracing_subscriber::fmt() .with_env_filter(filter) - .with_writer(std::io::stderr) + .with_writer(io::stderr) .init(); debug!("args: {:?}", std::env::args().collect::>()); diff --git a/crates/utiles-cli/src/shapes.rs b/crates/utiles-cli/src/shapes.rs index 4f2bcd9b..9c4c5d10 100644 --- a/crates/utiles-cli/src/shapes.rs +++ b/crates/utiles-cli/src/shapes.rs @@ -86,8 +86,8 @@ impl Default for ShapesArgs { input: None, seq: false, precision: None, - project: Option::Some(ShapesProject::default()), - output_mode: Option::Some(ShapesOutputMode::default()), + project: Some(ShapesProject::default()), + output_mode: Some(ShapesOutputMode::default()), collect: false, extents: false, buffer: None, @@ -152,7 +152,7 @@ pub fn shapes_main(args: ShapesArgs) { None => Projection::Geographic, }, props: None, - buffer: Option::from(args.buffer), + buffer: args.buffer, precision: args.precision, }; diff --git a/crates/utiles/src/tile.rs b/crates/utiles/src/tile.rs index b3a86506..3d9dbc38 100644 --- a/crates/utiles/src/tile.rs +++ b/crates/utiles/src/tile.rs @@ -493,14 +493,14 @@ impl Tile { None => xyz, }; let tile_feature = TileFeature { - id: id, + id, type_: "Feature".to_string(), geometry: TileFeatureGeometry { type_: "Polygon".to_string(), coordinates: geometry_coordinates, }, - bbox: bbox, - properties: properties, + bbox, + properties, }; Ok(tile_feature) } diff --git a/crates/utilesqlite/src/bin.rs b/crates/utilesqlite/src/bin.rs index 1ca935b5..57c78787 100644 --- a/crates/utilesqlite/src/bin.rs +++ b/crates/utilesqlite/src/bin.rs @@ -1,140 +1,5 @@ -use std::collections::HashMap; -use std::hash::Hash; -use serde_json; -use rusqlite; -use utilesqlite::mbtiles::Mbtiles; -// use mbtiles::MbtilesManager; -// use crate::mbtiles::Mbtiles; - -// mod mbtiles; - -// impl From for Error { -// fn from(e: tokio_rusqlite::Error) -> Error { -// Error::RusqliteError(e) -// } -// } - - #[tokio::main] async fn main() -> tokio_rusqlite::Result<()> { - // let c_res = Connection::open( - // "D:\\maps\\reptiles\\mbtiles\\osm\\planet_z0z14_2022_10_13.mbtiles" - // ).await; - - let filepath= "D:\\maps\\reptiles\\mbtiles\\osm\\planet_z0z14_2022_10_13.mbtiles"; - // "D:\\maps\\reptiles\\mbtiles\\osm\\planet_z0z14_2022_10_13.mbtiles", - // "D:\\maps\\reptiles\\mbtiles\\globallandcover.mbtiles", - // let mbt = MbtilesAsync::open( - // "D:\\maps\\reptiles\\mbtiles\\osm\\planet_z0z14_2022_10_13.mbtiles", - // ).await?; - // - // let mdata = mbt.metadata().await?; - // - // let mut metadataMap: HashMap> = HashMap::new(); - // - // for thing in mdata { - // println!("{}: {}", thing.name, thing.value); - // - // // if it does not exist, create empty vector - // // if it does exist, append to vector - // let mut v = metadataMap.entry(thing.name).or_insert(Vec::new()); - // v.push(thing.value); - // } - // - // println!("metadataMap: {:?}", metadataMap); - // - // println!("metadata_has_unique_index_name: {}", mbt.metadata_has_unique_index_name().await?); - // - - - let conn = rusqlite::Connection::open( - filepath - ).unwrap(); - - let mbtiles = Mbtiles::from_conn(conn); - - let mdata_arr = mbtiles.metadata().unwrap(); - - // print it - for thing in mdata_arr { - println!("{}: {}", thing.name, thing.value); - } - - - let tj = mbtiles.tilejson().unwrap(); - - let tj_str = serde_json::to_string_pretty(&tj).unwrap(); - // serialized - println!( "{}", tj_str - ); - - // - // let mut mbtiles_manager = MbtilesManager::new(); - // - // // Open the database connection - // mbtiles_manager.open( - // filepath - // ).unwrap(); - // - // let mapfn = |row: &rusqlite::Row| -> rusqlite::Result { - // Ok(row.get(0)?) - // }; - // - // let metadata = mbtiles_manager.metadata(); - // // Execute a query - // let result= mbtiles_manager.query("SELECT name, value FROM metadata", - // mapfn - // ); - // match result { - // Ok(rows) => { - // for row in rows { - // println!("{}", row); - // } - // } - // Err(err) => eprintln!("Query failed: {}", err), - // } - // - // println!("metadata: {:?}", metadata); - // // Close the database connection - // mbtiles_manager.close().unwrap(); - // - // // match c_res { - // // Ok(c) => println!("Connection opened"), - // // Err(e) => println!("Error opening connection: {}", e), - // // } - // let conn = match c_res { - // Ok(c) => c, - // Err(e) => return Err(e), - // }; - // - // let mdata = conn - // .call(|conn| { - // let mut stmt = conn.prepare("SELECT name, value FROM metadata")?; - // let mdata = stmt - // .query_map([], |row| { - // Ok( - // MetadataRow { - // name: row.get(0)?, - // value: row.get(1)?, - // } - // ) - // })? - // .collect::, rusqlite::Error>>()?; - // - // Ok::<_, rusqlite::Error>(mdata) - // }) - // .await?; - // - // - // - // for thing in mdata { - // println!("{}: {}", thing.name, thing.value); - // } - - Ok( - () - ) - - - // let mbt = Connection + println!("utilesqlite::main() ~ TBD"); + Ok(()) } diff --git a/crates/utilesqlite/src/mbtiles.rs b/crates/utilesqlite/src/mbtiles.rs index f2e30113..2df39905 100644 --- a/crates/utilesqlite/src/mbtiles.rs +++ b/crates/utilesqlite/src/mbtiles.rs @@ -9,13 +9,13 @@ use utiles::mbtiles::metadata2tilejson; use utiles::mbtiles::metadata_row::MbtilesMetadataRow; pub struct Mbtiles { - conn: rusqlite::Connection, + conn: Connection, } impl Mbtiles { - pub fn from_conn(conn: rusqlite::Connection) -> Mbtiles { + pub fn from_conn(conn: Connection) -> Mbtiles { Mbtiles { - conn: conn, + conn, } } @@ -37,10 +37,10 @@ impl Mbtiles { let metadata = self.metadata()?; let tj = metadata2tilejson(metadata); match tj { - Ok(t) => return Ok(t), + Ok(t) => Ok(t), Err(e) => { error!("Error parsing metadata to TileJSON: {}", e); - return Err(e.into()); + Err(e.into()) } } // return Ok(tj); @@ -48,17 +48,17 @@ impl Mbtiles { pub fn from_filepath(fspath: &str) -> RusqliteResult { - let conn = rusqlite::Connection::open(fspath)?; + let conn = Connection::open(fspath)?; let mbt = Mbtiles { - conn: conn, + conn, }; return Ok(mbt); } pub fn from_filepath_str(fspath: &str) -> Result> { - let conn = rusqlite::Connection::open(fspath)?; + let conn = Connection::open(fspath)?; let mbt = Mbtiles { - conn: conn, + conn, }; return Ok(mbt); } @@ -66,9 +66,9 @@ impl Mbtiles { impl From<&Path> for Mbtiles { fn from(path: &Path) -> Self { - let conn = rusqlite::Connection::open(path).unwrap(); + let conn = Connection::open(path).unwrap(); Mbtiles { - conn: conn, + conn, } } } @@ -79,7 +79,7 @@ pub struct MbtilesManager { } -pub fn mbtiles_metadata(conn: &rusqlite::Connection) -> RusqliteResult> { +pub fn mbtiles_metadata(conn: &Connection) -> RusqliteResult> { let mut stmt = conn.prepare("SELECT name, value FROM metadata")?; let mdata = stmt .query_map([], |row| { diff --git a/src/cli.rs b/src/cli.rs index a7e1520d..94747be1 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -8,8 +8,8 @@ pub fn ut_cli(py: Python, args: Option>) { None => std::env::args().collect(), }; cli_main( - Option::Some(argv), - Option::Some(&|| { + Some(argv), + Some(&|| { py.check_signals().unwrap(); }), ) diff --git a/src/lib.rs b/src/lib.rs index be76c19a..b45cb77b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,4 @@ use std::collections::{HashMap, HashSet}; - use pyo3::exceptions::{self, PyValueError}; use pyo3::prelude::*; use pyo3::types::{PyDict, PyTuple}; @@ -14,8 +13,6 @@ use utiles::zoom::ZoomOrZooms; mod cli; mod pyutiles; -// mod pyutilesqlite; -// mod utiles; #[derive(FromPyObject)] pub struct TileTuple(u32, u32, u8); diff --git a/src/purgatory/_coords.rs b/src/purgatory/_coords.rs deleted file mode 100644 index 53808eeb..00000000 --- a/src/purgatory/_coords.rs +++ /dev/null @@ -1,167 +0,0 @@ -/// def pycoords(obj): -/// if isinstance(obj, (tuple, list)): -/// coordinates = obj -/// elif "features" in obj: -/// coordinates = [feat["geometry"]["coordinates"] for feat in obj["features"]] -/// elif "geometry" in obj: -/// coordinates = obj["geometry"]["coordinates"] -/// else: -/// coordinates = obj.get("coordinates", obj) -/// for e in coordinates: -/// if isinstance(e, (float, int)): -/// return tuple(coordinates) -/// else: -/// for f in pycoords(e): -// #[pyfunction] -// fn _coords_slow(py: Python, obj: &PyAny) -> PyResult> { -// println!("obj: {:?}", obj); -// // let is_tuple = obj.is_instance(PyTuple)?; -// let tuple_type = py.get_type::(); -// let list_type = py.get_type::(); -// let float_type = py.get_type::(); -// let int_type = py.get_type::(); -// let dict_type = py.get_type::(); -// -// let is_tuple = obj.is_instance(tuple_type)?; -// let is_list = obj.is_instance(list_type)?; -// let is_dict = obj.is_instance(dict_type)?; -// -// let mut result: Vec<(f64, f64)> = Vec::new(); -// let coordinates: &PyAny; -// if is_tuple { -// println!("is tuple"); -// // let tuple_obj = obj.into_object(py); -// // let tuple_obj = tuple_obj.downcast::()?; -// // coordinates = obj.into(); -// // coordinates = obj; -// // println!("tuple_obj: {:?}", coordinates); -// let tuple_len = obj.len(); -// if let Ok(tlen) = tuple_len { -// if tlen == 2 { -// // try to extract as coords -// return Ok(vec![( -// obj.get_item(0)?.extract::()?, -// obj.get_item(1)?.extract::()?, -// )]); -// } else if tlen == 3 { -// // try to extract as coords -// // if first value is a number assume the thing is a coord -// if obj.get_item(0)?.is_instance(float_type)? -// || obj.get_item(0)?.is_instance(int_type)? -// { -// return Ok(vec![( -// obj.get_item(0)?.extract::()?, -// obj.get_item(1)?.extract::()?, -// )]); -// } else { -// // call _coords on each item -// let mut coordsvec: Vec<(f64, f64)> = Vec::new(); -// for item in obj.iter() { -// let c = _coords_slow(py, item)?; -// coordsvec.extend(c); -// } -// return Ok(coordsvec); -// } -// // return Ok(vec![(obj.get_item(0)?.extract::()?, obj.get_item(1)?.extract::()?)]); -// } -// } -// -// // let c = obj.extract -// } else if is_list { -// println!("is list"); -// // let tuple_obj = obj.into_object(py); -// // let tuple_obj = tuple_obj.downcast::()?; -// // coordinates = obj.into(); -// coordinates = obj; -// let c = obj.downcast::()?; -// for item in c.iter() { -// if item.is_instance(float_type)? || item.is_instance(int_type)? { -// println!("item: {:?}", item); -// let c = _coords_slow(py, item)?; -// println!("c: {:?}", c); -// let value = item.extract::()?; -// result.push((value, value)); -// } else { -// println!("item: {:?}", item); -// let c = _coords_slow(py, item)?; -// println!("c: {:?}", c); -// result.extend(c); -// } -// } -// println!("tuple_obj: {:?}", coordinates); -// } else if is_dict { -// println!("is dict"); -// match obj.contains("features") { -// Ok(true) => { -// let features = obj.get_item("features"); -// -// match features { -// Ok(val) => { -// println!("val: {:?}", val); -// let c = _coords_slow(py, val)?; -// println!("c: {:?}", c); -// result.extend(c); -// } -// Err(e) => { -// println!("e: {:?}", e); -// } -// } -// } -// _ => { -// println!("is not features"); -// } -// } -// -// match obj.contains("geometry") { -// Ok(true) => { -// let features = obj.get_item("geometry"); -// -// match features { -// Ok(val) => { -// println!("val: {:?}", val); -// let c = _coords_slow(py, val)?; -// println!("c: {:?}", c); -// result.extend(c); -// } -// Err(e) => { -// println!("e: {:?}", e); -// } -// } -// } -// _ => { -// println!("is not geometry"); -// } -// } -// -// match obj.contains("coordinates") { -// Ok(true) => { -// let features = obj.get_item("geometry"); -// -// match features { -// Ok(val) => { -// println!("val: {:?}", val); -// let c = _coords_slow(py, val)?; -// println!("c: {:?}", c); -// result.extend(c); -// } -// Err(e) => { -// println!("e: {:?}", e); -// } -// } -// } -// _ => { -// println!("is not geometry"); -// } -// } -// // println!("features: {:?}", features); -// } else { -// println!("is something else"); -// // dummy obj -// // coordinates = obj.getattr("coordinates")?.into(); -// } -// -// // Err -// // raise dummy error -// // Err(PyErr::new::( "the tile argument may have 1 or 4 values. Note that zoom is a keyword-only argument"))?; -// Ok(result) -// } From 9ca2975c4f88e03edb005bc0dbe889701a35834f Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Fri, 10 Nov 2023 14:21:52 -0800 Subject: [PATCH 76/80] fmts --- crates/utiles-cli/src/cli.rs | 22 ++++++++++++---------- crates/utiles-cli/src/stdinterator.rs | 2 +- crates/utiles-wasm/Cargo.lock | 8 ++++++++ crates/utiles-wasm/src/lib.rs | 7 +++---- crates/utiles/src/lib.rs | 4 ++++ crates/utiles/src/projection.rs | 19 ++++++++++--------- crates/utiles/src/tile.rs | 13 +++++-------- 7 files changed, 43 insertions(+), 32 deletions(-) diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs index e8ff7e81..3e377a27 100644 --- a/crates/utiles-cli/src/cli.rs +++ b/crates/utiles-cli/src/cli.rs @@ -4,12 +4,12 @@ use std::path::Path; use clap::{Parser, Subcommand}; use tracing::{debug, error}; use tracing_subscriber::EnvFilter; -use utiles::{bounding_tile, Tile}; use utiles::mbtiles::metadata_row::MbtilesMetadataRow; use utiles::parsing::parse_bbox; use utiles::tilejson::tilejson_stringify; use utiles::tiles; use utiles::zoom::ZoomOrZooms; +use utiles::{bounding_tile, Tile}; use utilesqlite::mbtiles::Mbtiles; use crate::shapes::{shapes_main, ShapesArgs}; @@ -25,11 +25,11 @@ pub struct Cli { // debug flag #[arg( - long, - short, - global = true, - default_value = "false", - help = "debug mode" + long, + short, + global = true, + default_value = "false", + help = "debug mode" )] debug: bool, // #[command(flatten , help="verbosity level (-v, -vv, -vvv, -vvvv)" )] @@ -142,14 +142,16 @@ pub enum Commands { Shapes(ShapesArgs), } -fn stdin_filtered(input: Option) -> Box>> { +fn stdin_filtered( + input: Option, +) -> Box>> { let input_lines = StdInterator::new(input).unwrap(); let filtered_lines = input_lines .filter(|l| !l.is_err()) .filter(|l| !l.as_ref().unwrap().is_empty()) .filter(|l| l.as_ref().unwrap() != "\x1e"); - let boxed = Box::new(filtered_lines); - boxed + + Box::new(filtered_lines) as _ } pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { @@ -203,7 +205,7 @@ pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { let s = serde_json::to_string_pretty::>( &metadata_rows, ) - .unwrap(); + .unwrap(); println!("{s}"); } } diff --git a/crates/utiles-cli/src/stdinterator.rs b/crates/utiles-cli/src/stdinterator.rs index 05624444..445df115 100644 --- a/crates/utiles-cli/src/stdinterator.rs +++ b/crates/utiles-cli/src/stdinterator.rs @@ -1,8 +1,8 @@ use std::collections::VecDeque; use std::io; use std::io::BufRead; -use tracing::debug; use std::str::FromStr; +use tracing::debug; pub enum StdInteratorSource { Args(VecDeque), diff --git a/crates/utiles-wasm/Cargo.lock b/crates/utiles-wasm/Cargo.lock index 2e488ac1..7c757ca2 100644 --- a/crates/utiles-wasm/Cargo.lock +++ b/crates/utiles-wasm/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + [[package]] name = "approx" version = "0.5.1" @@ -297,11 +303,13 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" name = "utiles" version = "0.0.1" dependencies = [ + "anyhow", "fast_hilbert", "geo-types", "geojson", "serde", "serde_json", + "thiserror", "tilejson", "tracing", ] diff --git a/crates/utiles-wasm/src/lib.rs b/crates/utiles-wasm/src/lib.rs index 0791d3c8..035e576b 100644 --- a/crates/utiles-wasm/src/lib.rs +++ b/crates/utiles-wasm/src/lib.rs @@ -1,6 +1,6 @@ -use wasm_bindgen::prelude::*; -use utiles::pmtiles::xyz2pmid as _xyz2pmid ; +use utiles::pmtiles::xyz2pmid as _xyz2pmid; use utiles::xyz2quadkey as uxyz2qk; +use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn add(left: usize, right: usize) -> usize { @@ -13,8 +13,7 @@ pub fn pmtileid(x: u32, y: u32, z: u8) -> u64 { } #[wasm_bindgen] -pub fn xyz2qk(x: u32, y: u32, z: u8) -> String{ - +pub fn xyz2qk(x: u32, y: u32, z: u8) -> String { uxyz2qk(x, y, z) } diff --git a/crates/utiles/src/lib.rs b/crates/utiles/src/lib.rs index bf15734f..dee12687 100644 --- a/crates/utiles/src/lib.rs +++ b/crates/utiles/src/lib.rs @@ -1,3 +1,7 @@ +#![deny(clippy::all)] +#![deny(clippy::perf)] +#![deny(clippy::style)] + use std::collections::{HashMap, HashSet}; use std::num::FpCategory; use std::{error::Error, f64::consts::PI}; diff --git a/crates/utiles/src/projection.rs b/crates/utiles/src/projection.rs index 3432cc8b..8b879afa 100644 --- a/crates/utiles/src/projection.rs +++ b/crates/utiles/src/projection.rs @@ -1,3 +1,4 @@ +use std::fmt; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] @@ -6,15 +7,6 @@ pub enum Projection { Mercator, } -impl Projection { - pub fn to_string(&self) -> String { - match self { - Projection::Geographic => "geographic".to_string(), - Projection::Mercator => "mercator".to_string(), - } - } -} - impl From for Projection { fn from(s: String) -> Self { match s.as_str() { @@ -26,3 +18,12 @@ impl From for Projection { } } } + +impl fmt::Display for Projection { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Projection::Geographic => write!(f, "geographic"), + Projection::Mercator => write!(f, "mercator"), + } + } +} diff --git a/crates/utiles/src/tile.rs b/crates/utiles/src/tile.rs index 3d9dbc38..d47b6021 100644 --- a/crates/utiles/src/tile.rs +++ b/crates/utiles/src/tile.rs @@ -570,15 +570,12 @@ impl From<&Value> for Tile { Value::Object(v) => { // if it has a "tile" key, use that // if has 'tile' key, use that - if v.contains_key("tile") { - if v["tile"].is_array() && v["tile"].as_array().unwrap().len() == 3 - { - let tuple = - serde_json::from_value::(v["tile"].clone()).unwrap(); - return Tile::from(tuple); - } + if v.contains_key("tile") && v["tile"].is_array() && v["tile"].as_array().unwrap().len() == 3 { + let tuple = + serde_json::from_value::(v["tile"].clone()).unwrap(); + return Tile::from(tuple); } - return Tile::from(v); + Tile::from(v) } _ => { panic!("Invalid json value: {val}"); From a0411a61ac9ce7237c12ff7960cda5f736316c9c Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Fri, 10 Nov 2023 14:24:41 -0800 Subject: [PATCH 77/80] clippying --- crates/utilesqlite/src/mbtiles.rs | 409 ++++++++++++++---------------- 1 file changed, 196 insertions(+), 213 deletions(-) diff --git a/crates/utilesqlite/src/mbtiles.rs b/crates/utilesqlite/src/mbtiles.rs index 2df39905..3fe480ec 100644 --- a/crates/utilesqlite/src/mbtiles.rs +++ b/crates/utilesqlite/src/mbtiles.rs @@ -14,13 +14,11 @@ pub struct Mbtiles { impl Mbtiles { pub fn from_conn(conn: Connection) -> Mbtiles { - Mbtiles { - conn, - } + Mbtiles { conn } } pub fn metadata(&self) -> RusqliteResult> { - return mbtiles_metadata(&self.conn); + mbtiles_metadata(&self.conn) // let mut stmt = self.conn.as_ref().unwrap().prepare("SELECT name, value FROM metadata")?; // let rows = stmt.query_map([], |row| { // Ok( @@ -40,254 +38,239 @@ impl Mbtiles { Ok(t) => Ok(t), Err(e) => { error!("Error parsing metadata to TileJSON: {}", e); - Err(e.into()) + Err(e) } } // return Ok(tj); } - pub fn from_filepath(fspath: &str) -> RusqliteResult { let conn = Connection::open(fspath)?; - let mbt = Mbtiles { - conn, - }; - return Ok(mbt); + let mbt = Mbtiles { conn }; + Ok(mbt) } pub fn from_filepath_str(fspath: &str) -> Result> { let conn = Connection::open(fspath)?; - let mbt = Mbtiles { - conn, - }; - return Ok(mbt); + let mbt = Mbtiles { conn }; + Ok(mbt) } } impl From<&Path> for Mbtiles { fn from(path: &Path) -> Self { let conn = Connection::open(path).unwrap(); - Mbtiles { - conn, - } + Mbtiles { conn } } } - -pub struct MbtilesManager { - conn: Option, -} - - pub fn mbtiles_metadata(conn: &Connection) -> RusqliteResult> { let mut stmt = conn.prepare("SELECT name, value FROM metadata")?; let mdata = stmt .query_map([], |row| { - Ok( - MbtilesMetadataRow { - name: row.get(0)?, - value: row.get(1)?, - } - ) + Ok(MbtilesMetadataRow { + name: row.get(0)?, + value: row.get(1)?, + }) })? .collect::, rusqlite::Error>>()?; - return Ok(mdata); + Ok(mdata) } -impl MbtilesManager { - // Create a new instance of the MbtilesManager - pub fn new() -> MbtilesManager { - MbtilesManager { conn: None } - } - - // Open a connection to the MBTiles SQLite database - pub fn open(&mut self, path: &str) -> RusqliteResult<()> { - self.conn = Some(Connection::open(path)?); - Ok(()) - } - - // Execute a query on the MBTiles database - pub fn query(&self, sql: &str, mut map_fn: F) -> RusqliteResult> - where - F: FnMut(&rusqlite::Row<'_>) -> RusqliteResult, - { - match &self.conn { - Some(conn) => { - let mut stmt = conn.prepare(sql)?; - let rows = stmt.query_map([], |row| map_fn(row))?; - rows.collect() - } - None => Err(rusqlite::Error::InvalidQuery), - } - } - - pub fn metadata(&self) -> RusqliteResult> { - return mbtiles_metadata(self.conn.as_ref().unwrap()); - // let mut stmt = self.conn.as_ref().unwrap().prepare("SELECT name, value FROM metadata")?; - // let rows = stmt.query_map([], |row| { - // Ok( - // MbtilesMetadataRow { - // name: row.get(0)?, - // value: row.get(1)?, - // } - // ) - // })?; - // rows.collect() - } - - // Close the connection to the MBTiles database - pub fn close(&mut self) -> RusqliteResult<()> { - if let Some(conn) = self.conn.take() { - conn.close().map_err(|(_, e)| e) - } else { - Ok(()) - } - } -} - -// fn main() { -// let mut mbtiles_manager = MbtilesManager::new(); -// -// // Open the database connection -// mbtiles_manager.open("path/to/your/mbtiles/database.mbtiles").unwrap(); -// -// // Execute a query -// let result: Result> = mbtiles_manager.query("SELECT name FROM some_table", |row| { -// Ok(row.get(0)?) -// }); -// match result { -// Ok(rows) => { -// for row in rows { -// println!("{}", row); -// } -// } -// Err(err) => eprintln!("Query failed: {}", err), +// impl MbtilesManager { +// // Create a new instance of the MbtilesManager +// pub fn new() -> MbtilesManager { +// MbtilesManager { conn: None } // } // -// // Close the database connection -// mbtiles_manager.close().unwrap(); -// } - -// #[derive(Debug)] -// pub struct Mbtiles<'a> { -// pub conn: &'a mut rusqlite::Connection, -// } -// #[derive(Debug)] -// pub struct MetadataRow { -// pub name: String, -// pub value: String, -// } -// -// impl Mbtiles<'_> { -// // impl Mbtiles { -// pub fn metadata<'a>(&'a self) -> rusqlite::Result> { -// // return all_metadata(self.conn); -// -// let mut stmt = self.conn.prepare("SELECT name, value FROM metadata")?; -// let mdata = stmt -// .query_map([], |row| { -// Ok( -// MetadataRow { -// name: row.get(0)?, -// value: row.get(1)?, -// } -// ) -// })? -// .collect::>>(); -// return Ok(mdata?); -// } -// -// pub fn open<'a>(fspath: &str) -> rusqlite::Result { -// let mut conn = rusqlite::Connection::open(fspath)?; -// let mbt = Mbtiles { -// conn: &mut conn, -// }; -// -// return Ok(mbt); -// +// // Open a connection to the MBTiles SQLite database +// pub fn open(&mut self, path: &str) -> RusqliteResult<()> { +// self.conn = Some(Connection::open(path)?); +// Ok(()) // } // -// pub fn from_conn<'a>(conn: &mut rusqlite::Connection) -> Mbtiles { -// Mbtiles { -// conn: conn, +// // Execute a query on the MBTiles database +// pub fn query(&self, sql: &str, mut map_fn: F) -> RusqliteResult> +// where +// F: FnMut(&rusqlite::Row<'_>) -> RusqliteResult, +// { +// match &self.conn { +// Some(conn) => { +// let mut stmt = conn.prepare(sql)?; +// let rows = stmt.query_map([], |row| map_fn(row))?; +// rows.collect() +// } +// None => Err(rusqlite::Error::InvalidQuery), // } // } -// } -// -// -// pub fn all_metadata (conn: &rusqlite::Connection) -> rusqlite::Result> { -// let mut stmt = conn.prepare("SELECT name, value FROM metadata")?; -// let mdata = stmt -// .query_map([], |row| { -// Ok( -// MetadataRow { -// name: row.get(0)?, -// value: row.get(1)?, -// } -// ) -// })? -// .collect::>>(); -// return Ok(mdata?); -// } - -// #[derive(Debug)] -// pub struct Mbtiles<'a> { -// pub conn: &'a mut rusqlite::Connection, -// } -// #[derive(Debug)] -// pub struct MetadataRow { -// pub name: String, -// pub value: String, -// } // -// impl Mbtiles<'_> { -// // impl Mbtiles { -// pub fn metadata<'a>(&'a self) -> rusqlite::Result> { -// // return all_metadata(self.conn); -// -// let mut stmt = self.conn.prepare("SELECT name, value FROM metadata")?; -// let mdata = stmt -// .query_map([], |row| { -// Ok( -// MetadataRow { -// name: row.get(0)?, -// value: row.get(1)?, -// } -// ) -// })? -// .collect::>>(); -// return Ok(mdata?); +// pub fn metadata(&self) -> RusqliteResult> { +// return mbtiles_metadata(self.conn.as_ref().unwrap()); +// // let mut stmt = self.conn.as_ref().unwrap().prepare("SELECT name, value FROM metadata")?; +// // let rows = stmt.query_map([], |row| { +// // Ok( +// // MbtilesMetadataRow { +// // name: row.get(0)?, +// // value: row.get(1)?, +// // } +// // ) +// // })?; +// // rows.collect() // } // -// pub fn open<'a>(fspath: &str) -> rusqlite::Result { -// let mut conn = rusqlite::Connection::open(fspath)?; -// let mbt = Mbtiles { -// conn: &mut conn, -// }; -// -// return Ok(mbt); -// -// } -// -// pub fn from_conn<'a>(conn: &mut rusqlite::Connection) -> Mbtiles { -// Mbtiles { -// conn: conn, +// // Close the connection to the MBTiles database +// pub fn close(&mut self) -> RusqliteResult<()> { +// if let Some(conn) = self.conn.take() { +// conn.close().map_err(|(_, e)| e) +// } else { +// Ok(()) // } // } // } // +// // fn main() { +// // let mut mbtiles_manager = MbtilesManager::new(); +// // +// // // Open the database connection +// // mbtiles_manager.open("path/to/your/mbtiles/database.mbtiles").unwrap(); +// // +// // // Execute a query +// // let result: Result> = mbtiles_manager.query("SELECT name FROM some_table", |row| { +// // Ok(row.get(0)?) +// // }); +// // match result { +// // Ok(rows) => { +// // for row in rows { +// // println!("{}", row); +// // } +// // } +// // Err(err) => eprintln!("Query failed: {}", err), +// // } +// // +// // // Close the database connection +// // mbtiles_manager.close().unwrap(); +// // } // -// pub fn all_metadata (conn: &rusqlite::Connection) -> rusqlite::Result> { -// let mut stmt = conn.prepare("SELECT name, value FROM metadata")?; -// let mdata = stmt -// .query_map([], |row| { -// Ok( -// MetadataRow { -// name: row.get(0)?, -// value: row.get(1)?, -// } -// ) -// })? -// .collect::>>(); -// return Ok(mdata?); -// } +// // #[derive(Debug)] +// // pub struct Mbtiles<'a> { +// // pub conn: &'a mut rusqlite::Connection, +// // } +// // #[derive(Debug)] +// // pub struct MetadataRow { +// // pub name: String, +// // pub value: String, +// // } +// // +// // impl Mbtiles<'_> { +// // // impl Mbtiles { +// // pub fn metadata<'a>(&'a self) -> rusqlite::Result> { +// // // return all_metadata(self.conn); +// // +// // let mut stmt = self.conn.prepare("SELECT name, value FROM metadata")?; +// // let mdata = stmt +// // .query_map([], |row| { +// // Ok( +// // MetadataRow { +// // name: row.get(0)?, +// // value: row.get(1)?, +// // } +// // ) +// // })? +// // .collect::>>(); +// // return Ok(mdata?); +// // } +// // +// // pub fn open<'a>(fspath: &str) -> rusqlite::Result { +// // let mut conn = rusqlite::Connection::open(fspath)?; +// // let mbt = Mbtiles { +// // conn: &mut conn, +// // }; +// // +// // return Ok(mbt); +// // +// // } +// // +// // pub fn from_conn<'a>(conn: &mut rusqlite::Connection) -> Mbtiles { +// // Mbtiles { +// // conn: conn, +// // } +// // } +// // } +// // +// // +// // pub fn all_metadata (conn: &rusqlite::Connection) -> rusqlite::Result> { +// // let mut stmt = conn.prepare("SELECT name, value FROM metadata")?; +// // let mdata = stmt +// // .query_map([], |row| { +// // Ok( +// // MetadataRow { +// // name: row.get(0)?, +// // value: row.get(1)?, +// // } +// // ) +// // })? +// // .collect::>>(); +// // return Ok(mdata?); +// // } +// +// // #[derive(Debug)] +// // pub struct Mbtiles<'a> { +// // pub conn: &'a mut rusqlite::Connection, +// // } +// // #[derive(Debug)] +// // pub struct MetadataRow { +// // pub name: String, +// // pub value: String, +// // } +// // +// // impl Mbtiles<'_> { +// // // impl Mbtiles { +// // pub fn metadata<'a>(&'a self) -> rusqlite::Result> { +// // // return all_metadata(self.conn); +// // +// // let mut stmt = self.conn.prepare("SELECT name, value FROM metadata")?; +// // let mdata = stmt +// // .query_map([], |row| { +// // Ok( +// // MetadataRow { +// // name: row.get(0)?, +// // value: row.get(1)?, +// // } +// // ) +// // })? +// // .collect::>>(); +// // return Ok(mdata?); +// // } +// // +// // pub fn open<'a>(fspath: &str) -> rusqlite::Result { +// // let mut conn = rusqlite::Connection::open(fspath)?; +// // let mbt = Mbtiles { +// // conn: &mut conn, +// // }; +// // +// // return Ok(mbt); +// // +// // } +// // +// // pub fn from_conn<'a>(conn: &mut rusqlite::Connection) -> Mbtiles { +// // Mbtiles { +// // conn: conn, +// // } +// // } +// // } +// // +// // +// // pub fn all_metadata (conn: &rusqlite::Connection) -> rusqlite::Result> { +// // let mut stmt = conn.prepare("SELECT name, value FROM metadata")?; +// // let mdata = stmt +// // .query_map([], |row| { +// // Ok( +// // MetadataRow { +// // name: row.get(0)?, +// // value: row.get(1)?, +// // } +// // ) +// // })? +// // .collect::>>(); +// // return Ok(mdata?); +// // } From 9daf73ed05859ce05f4d90a892295c15f939291c Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Fri, 10 Nov 2023 14:44:14 -0800 Subject: [PATCH 78/80] must_use? --- crates/utiles/src/bbox.rs | 24 ++++ crates/utiles/src/geojson/mod.rs | 2 + crates/utiles/src/lib.rs | 30 ++++- crates/utiles/src/libtiletype.rs | 5 + crates/utiles/src/lnglat.rs | 6 + crates/utiles/src/parsing.rs | 1 + crates/utiles/src/pmtiles.rs | 6 + crates/utiles/src/projection.rs | 2 +- crates/utiles/src/tile.rs | 50 ++++++- crates/utiles/src/tile_feature.rs | 2 +- crates/utiles/src/tile_range.rs | 11 ++ crates/utiles/src/tilejson.rs | 1 + crates/utilesqlite/src/lib.rs | 20 --- crates/utilesqlite/src/mbtiles.rs | 209 ------------------------------ 14 files changed, 135 insertions(+), 234 deletions(-) diff --git a/crates/utiles/src/bbox.rs b/crates/utiles/src/bbox.rs index 29dc5688..16c3ceed 100644 --- a/crates/utiles/src/bbox.rs +++ b/crates/utiles/src/bbox.rs @@ -63,6 +63,7 @@ impl From<(i32, i32, i32, i32)> for BBox { } impl BBox { + #[must_use] pub fn new(west: f64, south: f64, east: f64, north: f64) -> Self { BBox { west, @@ -72,6 +73,7 @@ impl BBox { } } + #[must_use] pub fn world_planet() -> Self { BBox { west: -180.0, @@ -81,6 +83,7 @@ impl BBox { } } + #[must_use] pub fn world_web() -> Self { BBox { west: -180.0, @@ -90,39 +93,50 @@ impl BBox { } } + #[must_use] pub fn crosses_antimeridian(&self) -> bool { self.west > self.east } + #[must_use] pub fn tuple(&self) -> (f64, f64, f64, f64) { (self.north, self.south, self.east, self.west) } + #[must_use] pub fn north(&self) -> f64 { self.north } + #[must_use] pub fn south(&self) -> f64 { self.south } + #[must_use] pub fn east(&self) -> f64 { self.east } + #[must_use] pub fn west(&self) -> f64 { self.west } + #[must_use] pub fn top(&self) -> f64 { self.north } + #[must_use] pub fn bottom(&self) -> f64 { self.south } + #[must_use] pub fn right(&self) -> f64 { self.east } + #[must_use] pub fn left(&self) -> f64 { self.west } + #[must_use] pub fn contains_lnglat(&self, lnglat: LngLat) -> bool { let lng = lnglat.lng(); let lat = lnglat.lat(); @@ -143,11 +157,13 @@ impl BBox { false } + #[must_use] pub fn contains_tile(&self, tile: Tile) -> bool { let bbox = tile.bbox(); self.contains_bbox(bbox.into()) } + #[must_use] pub fn contains_bbox(&self, other: BBox) -> bool { self.north >= other.north && self.south <= other.south @@ -155,6 +171,7 @@ impl BBox { && self.west <= other.west } + #[must_use] pub fn contains(&self, other: BBoxContainable) -> bool { match other { BBoxContainable::LngLat(lnglat) => self.contains_lnglat(lnglat), @@ -163,6 +180,7 @@ impl BBox { } } + #[must_use] pub fn is_within(&self, other: &BBox) -> bool { self.north <= other.north && self.south >= other.south @@ -170,6 +188,7 @@ impl BBox { && self.west >= other.west } + #[must_use] pub fn intersects(&self, other: &BBox) -> bool { self.north >= other.south && self.south <= other.north @@ -177,6 +196,7 @@ impl BBox { && self.west <= other.east } + #[must_use] pub fn bboxes(&self) -> Vec { if self.crosses_antimeridian() { let mut bboxes = Vec::new(); @@ -200,18 +220,22 @@ impl BBox { } } + #[must_use] pub fn ul(&self) -> LngLat { LngLat::new(self.west, self.north) } + #[must_use] pub fn ur(&self) -> LngLat { LngLat::new(self.east, self.north) } + #[must_use] pub fn lr(&self) -> LngLat { LngLat::new(self.east, self.south) } + #[must_use] pub fn ll(&self) -> LngLat { LngLat::new(self.west, self.south) } diff --git a/crates/utiles/src/geojson/mod.rs b/crates/utiles/src/geojson/mod.rs index 012d5f59..e850bf30 100644 --- a/crates/utiles/src/geojson/mod.rs +++ b/crates/utiles/src/geojson/mod.rs @@ -21,6 +21,7 @@ pub fn geojson_geometry_points(g: Geometry) -> Box> } } +#[must_use] pub fn geojson_geometry_coords(g: Geometry) -> Box> { let coord_vecs = geojson_geometry_points(g); Box::new(coord_vecs.into_iter().map(|v| { @@ -47,6 +48,7 @@ pub fn geojson_geometry_points_vec(g: Geometry) -> Vec> { } } +#[must_use] pub fn geojson_feature_coords(feature: Feature) -> Box> { let geometry = feature.geometry.unwrap(); geojson_geometry_coords(geometry) diff --git a/crates/utiles/src/lib.rs b/crates/utiles/src/lib.rs index dee12687..7f3bb022 100644 --- a/crates/utiles/src/lib.rs +++ b/crates/utiles/src/lib.rs @@ -27,12 +27,12 @@ pub mod pmtiles; pub mod projection; pub mod sibling_relationship; pub mod tile; +mod tile_feature; pub mod tile_range; mod tile_tuple; pub mod tilejson; pub mod traits; pub mod zoom; -mod tile_feature; /// Tile macro to create a new tile. /// - do you need this? probably not @@ -44,6 +44,7 @@ macro_rules! utile { }; } +#[must_use] pub fn ul(x: u32, y: u32, z: u8) -> LngLat { let (lon_deg, lat_deg) = ult(x, y, z); LngLat { @@ -51,17 +52,21 @@ pub fn ul(x: u32, y: u32, z: u8) -> LngLat { } } +#[must_use] pub fn ll(x: u32, y: u32, z: u8) -> LngLat { ul(x, y + 1, z) } +#[must_use] pub fn ur(x: u32, y: u32, z: u8) -> LngLat { ul(x + 1, y, z) } +#[must_use] pub fn lr(x: u32, y: u32, z: u8) -> LngLat { ul(x + 1, y + 1, z) } +#[must_use] pub fn ult(x: u32, y: u32, z: u8) -> (f64, f64) { let z2 = f64::from(2_u32.pow(u32::from(z))); let lon_deg = (f64::from(x) / z2) * 360.0 - 180.0; @@ -78,6 +83,7 @@ pub struct XY { } /// Truncate a bounding box to the valid range of longitude and latitude. +#[must_use] pub fn bbox_truncate( west: f64, south: f64, @@ -107,20 +113,24 @@ pub fn bbox_truncate( (west, south, east, north) } +#[must_use] pub fn minmax(zoom: u32) -> (u32, u32) { (0, 2_u32.pow(zoom) - 1) } +#[must_use] pub fn valid(x: u32, y: u32, z: u8) -> bool { let (minx, maxx) = minmax(u32::from(z)); let (miny, maxy) = minmax(u32::from(z)); x >= minx && x <= maxx && y >= miny && y <= maxy } +#[must_use] pub fn flipy(y: u32, z: u8) -> u32 { 2_u32.pow(u32::from(z)) - 1 - y } +#[must_use] pub fn bbox2zoom(bbox: (u32, u32, u32, u32)) -> u8 { let max_zoom = 28; let (west, south, east, north) = bbox; @@ -133,6 +143,7 @@ pub fn bbox2zoom(bbox: (u32, u32, u32, u32)) -> u8 { max_zoom } +#[must_use] pub fn bounds(x: u32, y: u32, z: u8) -> (f64, f64, f64, f64) { let ul_corner = ul(x, y, z); let lr_corner = ul(x + 1, y + 1, z); @@ -144,6 +155,7 @@ pub fn bounds(x: u32, y: u32, z: u8) -> (f64, f64, f64, f64) { ) } +#[must_use] pub fn truncate_lng(lng: f64) -> f64 { if lng > 180.0 { 180.0 @@ -154,6 +166,7 @@ pub fn truncate_lng(lng: f64) -> f64 { } } +#[must_use] pub fn truncate_lat(lat: f64) -> f64 { if lat > 90.0 { 90.0 @@ -164,6 +177,7 @@ pub fn truncate_lat(lat: f64) -> f64 { } } +#[must_use] pub fn truncate_lnglat(lnglat: &LngLat) -> LngLat { LngLat { xy: coord! {x: truncate_lng(lnglat.lng()), y: truncate_lat(lnglat.lat())}, @@ -225,6 +239,7 @@ pub fn _xy( // // y = 0.5 - 0.25 * math.log((1.0 + sinlat) / (1.0 - sinlat)) / math.pi // } +#[must_use] pub fn xy(lng: f64, lat: f64, truncate: Option) -> (f64, f64) { let trunc = truncate.unwrap_or(false); let mut lng = lng; @@ -245,6 +260,7 @@ pub fn xy(lng: f64, lat: f64, truncate: Option) -> (f64, f64) { (x, y) } +#[must_use] pub fn lnglat(x: f64, y: f64, truncate: Option) -> LngLat { let lng = x / EARTH_RADIUS * 180.0 / PI; let lat = (2.0 * (y / EARTH_RADIUS).exp().atan() - PI * 0.5) * 180.0 / PI; @@ -259,6 +275,7 @@ pub fn lnglat(x: f64, y: f64, truncate: Option) -> LngLat { } } +#[must_use] pub fn parent(x: u32, y: u32, z: u8, n: Option) -> Tile { let n = n.unwrap_or(0); if n == 0 { @@ -272,6 +289,7 @@ pub fn parent(x: u32, y: u32, z: u8, n: Option) -> Tile { } } +#[must_use] pub fn children(x: u32, y: u32, z: u8, zoom: Option) -> Vec { let zoom = zoom.unwrap_or(z + 1); let tile = Tile { x, y, z }; @@ -289,6 +307,7 @@ pub fn children(x: u32, y: u32, z: u8, zoom: Option) -> Vec { tiles } +#[must_use] pub fn siblings(x: u32, y: u32, z: u8) -> Vec { let sibrel = SiblingRelationship::from((x, y)); match sibrel { @@ -370,6 +389,7 @@ fn _neighbors_middle_tile(x: u32, y: u32, z: u8) -> Vec { ] } +#[must_use] pub fn neighbors(x: u32, y: u32, z: u8) -> Vec { if z == 0 { return Vec::new(); @@ -437,6 +457,7 @@ pub fn neighbors(x: u32, y: u32, z: u8) -> Vec { /// let quadkey = xyz2quadkey(486, 332, 10); /// assert_eq!(quadkey, "0313102310"); /// ``` +#[must_use] pub fn xyz2quadkey(x: u32, y: u32, z: u8) -> String { let mut quadkey = String::new(); for i in (0..z).rev() { @@ -493,6 +514,7 @@ impl From for (u32, u32, u8) { } } +#[must_use] pub fn xyz2bbox(x: u32, y: u32, z: u8) -> WebMercatorBbox { let tile_size = EARTH_CIRCUMFERENCE / 2.0_f64.powi(i32::from(z)); let left = f64::from(x) * tile_size - EARTH_CIRCUMFERENCE / 2.0; @@ -507,6 +529,7 @@ pub fn xyz2bbox(x: u32, y: u32, z: u8) -> WebMercatorBbox { } } +#[must_use] pub fn as_zooms(zoom_or_zooms: ZoomOrZooms) -> Vec { match zoom_or_zooms { ZoomOrZooms::Zoom(zoom) => { @@ -526,10 +549,12 @@ fn tiles_range_zoom( (minx..=maxx).flat_map(move |i| (miny..=maxy).map(move |j| (i, j, zoom))) } +#[must_use] pub fn tile(lng: f64, lat: f64, zoom: u8, truncate: Option) -> Tile { Tile::from_lnglat_zoom(lng, lat, zoom, truncate) } +#[must_use] pub fn bounding_tile(bbox: BBox, truncate: Option) -> Tile { let (west, south, east, north) = bbox_truncate(bbox.west, bbox.south, bbox.east, bbox.north, truncate); @@ -570,6 +595,7 @@ pub fn bounding_tile(bbox: BBox, truncate: Option) -> Tile { // (minx, miny, maxx, maxy) // } +#[must_use] pub fn tile_ranges(bounds: (f64, f64, f64, f64), zooms: ZoomOrZooms) -> TileRanges { let zooms = as_zooms(zooms); let bboxthing = BBox { @@ -628,6 +654,7 @@ pub fn tile_ranges(bounds: (f64, f64, f64, f64), zooms: ZoomOrZooms) -> TileRang TileRanges::from(ranges) } +#[must_use] pub fn tiles_count(bounds: (f64, f64, f64, f64), zooms: ZoomOrZooms) -> u64 { let ranges = tile_ranges(bounds, zooms); ranges.length() @@ -714,6 +741,7 @@ fn merge(merge_set: &HashSet) -> (HashSet, bool) { } #[allow(dead_code)] +#[must_use] pub fn simplify(tiles: HashSet) -> HashSet { // Parse tiles from the input sequence let mut _tiles = tiles.into_iter().collect::>(); diff --git a/crates/utiles/src/libtiletype.rs b/crates/utiles/src/libtiletype.rs index 96326ff7..c91cb974 100644 --- a/crates/utiles/src/libtiletype.rs +++ b/crates/utiles/src/libtiletype.rs @@ -18,6 +18,7 @@ pub const TILETYPE_PBFGZ: usize = 5; pub const TILETYPE_PNG: usize = 6; pub const TILETYPE_WEBP: usize = 7; +#[must_use] pub fn tiletype(buffer: &[u8]) -> TileType { if buffer.len() >= 8 { if buffer[0] == 0x89 @@ -66,6 +67,7 @@ pub fn tiletype(buffer: &[u8]) -> TileType { TileType::Unknown } +#[must_use] pub fn enum2const(tiletype: TileType) -> usize { match tiletype { TileType::Unknown => TILETYPE_UNKNOWN, @@ -79,6 +81,7 @@ pub fn enum2const(tiletype: TileType) -> usize { } } +#[must_use] pub fn const2enum(tiletype: usize) -> TileType { match tiletype { TILETYPE_UNKNOWN => TileType::Unknown, @@ -93,6 +96,7 @@ pub fn const2enum(tiletype: usize) -> TileType { } } +#[must_use] pub fn headers(tiletype: TileType) -> Vec<(&'static str, &'static str)> { match tiletype { TileType::Png => vec![("Content-Type", "image/png")], @@ -112,6 +116,7 @@ pub fn headers(tiletype: TileType) -> Vec<(&'static str, &'static str)> { } } +#[must_use] pub fn tiletype_str(buffer: &[u8]) -> String { let tiletype = tiletype(buffer); match tiletype { diff --git a/crates/utiles/src/lnglat.rs b/crates/utiles/src/lnglat.rs index d04d0476..e76123a6 100644 --- a/crates/utiles/src/lnglat.rs +++ b/crates/utiles/src/lnglat.rs @@ -18,28 +18,34 @@ impl From<(f64, f64)> for LngLat { } impl LngLat { + #[must_use] pub fn new(lng: f64, lat: f64) -> Self { LngLat { xy: coord! { x: lng, y: lat}, } } + #[must_use] pub fn lng(&self) -> f64 { self.xy.x } + #[must_use] pub fn lat(&self) -> f64 { self.xy.y } + #[must_use] pub fn lon(&self) -> f64 { self.xy.x } + #[must_use] pub fn x(&self) -> f64 { self.xy.x } + #[must_use] pub fn y(&self) -> f64 { self.xy.y } diff --git a/crates/utiles/src/parsing.rs b/crates/utiles/src/parsing.rs index 068a27bc..a3ecd5b3 100644 --- a/crates/utiles/src/parsing.rs +++ b/crates/utiles/src/parsing.rs @@ -65,6 +65,7 @@ where Some((min_x, min_y, max_x, max_y)) } +#[must_use] pub fn geojson_bounds(geojson_str: &str) -> BBox { let coords = geojson_coords(geojson_str); let bounds = coords2bounds(coords).unwrap(); diff --git a/crates/utiles/src/pmtiles.rs b/crates/utiles/src/pmtiles.rs index b27290c4..f1a6be19 100644 --- a/crates/utiles/src/pmtiles.rs +++ b/crates/utiles/src/pmtiles.rs @@ -1,3 +1,4 @@ +#[must_use] pub fn xyz2pmid(x: u32, y: u32, z: u8) -> u64 { if z == 0 { return 0; @@ -8,10 +9,12 @@ pub fn xyz2pmid(x: u32, y: u32, z: u8) -> u64 { } #[allow(dead_code)] +#[must_use] pub fn zxy2pmid(z: u8, x: u32, y: u32) -> u64 { xyz2pmid(x, y, z) } +#[must_use] pub fn calculate_h_o(i: u64) -> (u64, u8) { if i == 0 { return (0, 0); @@ -28,6 +31,7 @@ pub fn calculate_h_o(i: u64) -> (u64, u8) { } } +#[must_use] pub fn pmid2xyz(i: u64) -> (u32, u32, u8) { if i == 0 { return (0, 0, 0); @@ -38,12 +42,14 @@ pub fn pmid2xyz(i: u64) -> (u32, u32, u8) { } #[allow(dead_code)] +#[must_use] pub fn pmid2zxy(i: u64) -> (u8, u32, u32) { let (x, y, z) = pmid2xyz(i); (z, x, y) } // Fast parent ID calculation without converting to ZXY (ported from pmtiles go) +#[must_use] pub fn parent_id(i: u64) -> u64 { let mut acc: u64 = 0; let mut last_acc: u64 = 0; diff --git a/crates/utiles/src/projection.rs b/crates/utiles/src/projection.rs index 8b879afa..63787719 100644 --- a/crates/utiles/src/projection.rs +++ b/crates/utiles/src/projection.rs @@ -1,5 +1,5 @@ -use std::fmt; use serde::{Deserialize, Serialize}; +use std::fmt; #[derive(Debug, Serialize, Deserialize)] pub enum Projection { diff --git a/crates/utiles/src/tile.rs b/crates/utiles/src/tile.rs index d47b6021..b8f4ddba 100644 --- a/crates/utiles/src/tile.rs +++ b/crates/utiles/src/tile.rs @@ -10,12 +10,12 @@ use crate::bbox::BBox; use crate::constants::EPSILON; use crate::lnglat::LngLat; use crate::projection::Projection; +use crate::tile_feature::TileFeature; use crate::tile_tuple::XYZ; use crate::{ bounds, children, flipy, ll, lr, neighbors, parent, pmtiles, quadkey2tile, siblings, traits, ul, ur, xy, xyz2quadkey, }; -use crate::tile_feature::TileFeature; #[derive(Debug, Serialize, Deserialize)] pub struct TileFeatureGeometry { @@ -141,44 +141,54 @@ impl FromStr for Tile { } impl Tile { + #[must_use] pub fn new(x: u32, y: u32, z: u8) -> Self { Tile { x, y, z } } #[allow(dead_code)] + #[must_use] pub fn valid(&self) -> bool { crate::valid(self.x, self.y, self.z) } + #[must_use] pub fn x(&self) -> u32 { self.x } + #[must_use] pub fn y(&self) -> u32 { self.y } + #[must_use] pub fn z(&self) -> u8 { self.z } + #[must_use] pub fn zoom(&self) -> u8 { self.z } + #[must_use] pub fn bounds(&self) -> (f64, f64, f64, f64) { bounds(self.x, self.y, self.z) } + #[must_use] pub fn pmtileid(&self) -> u64 { pmtiles::xyz2pmid(self.x, self.y, self.z) } + #[must_use] pub fn from_pmtileid(id: u64) -> Self { let (x, y, z) = pmtiles::pmid2xyz(id); Tile::new(x, y, z) } + #[must_use] pub fn fmt_zxy(&self, sep: Option<&str>) -> String { match sep { Some(sep) => format!("{}{}{}{}{}", self.z, sep, self.x, sep, self.y), @@ -186,6 +196,7 @@ impl Tile { } } + #[must_use] pub fn fmt_zxy_ext(&self, ext: &str, sep: Option<&str>) -> String { match sep { Some(sep) => { @@ -195,6 +206,7 @@ impl Tile { } } + #[must_use] pub fn parent_id(&self) -> u64 { pmtiles::parent_id(self.pmtileid()) } @@ -203,6 +215,7 @@ impl Tile { quadkey2tile(quadkey) } + #[must_use] pub fn from_qk(qk: &str) -> Self { let res = quadkey2tile(qk); match res { @@ -213,6 +226,7 @@ impl Tile { } } + #[must_use] pub fn from_json_obj(json: &str) -> Self { let res = serde_json::from_str(json); match res { @@ -223,6 +237,7 @@ impl Tile { } } + #[must_use] pub fn from_json_arr(json: &str) -> Self { let res = serde_json::from_str(json); match res { @@ -233,6 +248,7 @@ impl Tile { } } + #[must_use] pub fn from_json(json: &str) -> Self { if json.starts_with('[') { return Self::from_json_arr(json); @@ -240,19 +256,23 @@ impl Tile { Self::from_json_obj(json) } + #[must_use] pub fn from_json_loose(json: &str) -> Self { let v = serde_json::from_str::(json).unwrap(); Self::from(v) } + #[must_use] pub fn quadkey(&self) -> String { xyz2quadkey(self.x, self.y, self.z) } + #[must_use] pub fn qk(&self) -> String { xyz2quadkey(self.x, self.y, self.z) } + #[must_use] pub fn from_lnglat_zoom( lng: f64, lat: f64, @@ -292,34 +312,41 @@ impl Tile { } } + #[must_use] pub fn ul(&self) -> LngLat { ul(self.x, self.y, self.z) } + #[must_use] pub fn ll(&self) -> LngLat { ll(self.x, self.y, self.z) } + #[must_use] pub fn ur(&self) -> LngLat { ur(self.x, self.y, self.z) } + #[must_use] pub fn lr(&self) -> LngLat { lr(self.x, self.y, self.z) } + #[must_use] pub fn bbox(&self) -> (f64, f64, f64, f64) { let ul = self.ul(); let lr = self.lr(); (ul.lng(), lr.lat(), lr.lng(), ul.lat()) } + #[must_use] pub fn center(&self) -> LngLat { let ul = self.ul(); let lr = self.lr(); LngLat::new((ul.lng() + lr.lng()) / 2.0, (ul.lat() + lr.lat()) / 2.0) } + #[must_use] pub fn up(&self) -> Self { Self { x: self.x + 1, @@ -328,6 +355,7 @@ impl Tile { } } + #[must_use] pub fn down(&self) -> Self { Self { x: self.x - 1, @@ -336,6 +364,7 @@ impl Tile { } } + #[must_use] pub fn left(&self) -> Self { Self { x: self.x, @@ -344,6 +373,7 @@ impl Tile { } } + #[must_use] pub fn right(&self) -> Self { Self { x: self.x, @@ -352,6 +382,7 @@ impl Tile { } } + #[must_use] pub fn up_left(&self) -> Self { Self { x: self.x + 1, @@ -360,6 +391,7 @@ impl Tile { } } + #[must_use] pub fn up_right(&self) -> Self { Self { x: self.x + 1, @@ -368,6 +400,7 @@ impl Tile { } } + #[must_use] pub fn down_left(&self) -> Self { Self { x: self.x - 1, @@ -376,6 +409,7 @@ impl Tile { } } + #[must_use] pub fn down_right(&self) -> Self { Self { x: self.x - 1, @@ -384,22 +418,27 @@ impl Tile { } } + #[must_use] pub fn neighbors(&self) -> Vec { neighbors(self.x, self.y, self.z) } + #[must_use] pub fn children(&self, zoom: Option) -> Vec { children(self.x, self.y, self.z, zoom) } + #[must_use] pub fn parent(&self, zoom: Option) -> Self { parent(self.x, self.y, self.z, zoom) } + #[must_use] pub fn siblings(&self) -> Vec { siblings(self.x, self.y, self.z) } + #[must_use] pub fn sql_where(&self, flip: Option) -> String { // classic mbtiles sqlite query: // 'SELECT tile_data FROM tiles WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?', @@ -419,18 +458,22 @@ impl Tile { } } + #[must_use] pub fn json_arr_min(&self) -> String { format!("[{},{},{}]", self.x, self.y, self.z) } + #[must_use] pub fn json_arr(&self) -> String { format!("[{}, {}, {}]", self.x, self.y, self.z) } + #[must_use] pub fn json_obj(&self) -> String { serde_json::to_string(self).unwrap() } + #[must_use] pub fn tuple_string(&self) -> String { format!("({}, {}, {})", self.x, self.y, self.z) } @@ -570,7 +613,10 @@ impl From<&Value> for Tile { Value::Object(v) => { // if it has a "tile" key, use that // if has 'tile' key, use that - if v.contains_key("tile") && v["tile"].is_array() && v["tile"].as_array().unwrap().len() == 3 { + if v.contains_key("tile") + && v["tile"].is_array() + && v["tile"].as_array().unwrap().len() == 3 + { let tuple = serde_json::from_value::(v["tile"].clone()).unwrap(); return Tile::from(tuple); diff --git a/crates/utiles/src/tile_feature.rs b/crates/utiles/src/tile_feature.rs index 50d5aca1..862ecc10 100644 --- a/crates/utiles/src/tile_feature.rs +++ b/crates/utiles/src/tile_feature.rs @@ -1,6 +1,6 @@ +use crate::tile::TileFeatureGeometry; use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; -use crate::tile::TileFeatureGeometry; #[derive(Debug, Serialize, Deserialize)] pub struct TileFeature { diff --git a/crates/utiles/src/tile_range.rs b/crates/utiles/src/tile_range.rs index 6949426f..a75a488c 100644 --- a/crates/utiles/src/tile_range.rs +++ b/crates/utiles/src/tile_range.rs @@ -10,6 +10,7 @@ pub struct TileRange { } impl TileRange { + #[must_use] pub fn new(minx: u32, maxx: u32, miny: u32, maxy: u32, zoom: u8) -> Self { Self { curx: minx, @@ -22,26 +23,33 @@ impl TileRange { } } + #[must_use] pub fn minx(&self) -> u32 { self.minx } + #[must_use] pub fn maxx(&self) -> u32 { self.maxx } + #[must_use] pub fn miny(&self) -> u32 { self.miny } + #[must_use] pub fn maxy(&self) -> u32 { self.maxy } + #[must_use] pub fn zoom(&self) -> u8 { self.zoom } + #[must_use] pub fn length(&self) -> u64 { ((self.maxx - self.minx + 1) * (self.maxy - self.miny + 1)) as u64 } + #[must_use] pub fn sql_where(&self, flip: Option) -> String { // classic mbtiles sqlite query: // 'SELECT tile_data FROM tiles WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?', @@ -89,16 +97,19 @@ pub struct TileRanges { } impl TileRanges { + #[must_use] pub fn new(minx: u32, maxx: u32, miny: u32, maxy: u32, zoom: u8) -> Self { Self { ranges: vec![TileRange::new(minx, maxx, miny, maxy, zoom)], } } + #[must_use] pub fn length(&self) -> u64 { self.ranges.iter().map(|r| r.length()).sum() } + #[must_use] pub fn sql_where(&self, flip: Option) -> String { self.ranges .iter() diff --git a/crates/utiles/src/tilejson.rs b/crates/utiles/src/tilejson.rs index 24c23d4c..5fdd7068 100644 --- a/crates/utiles/src/tilejson.rs +++ b/crates/utiles/src/tilejson.rs @@ -1,6 +1,7 @@ use serde_json; use tilejson::TileJSON; +#[must_use] pub fn tilejson_stringify(tj: &TileJSON, fmt: Option) -> String { match fmt { Some(false) => serde_json::to_string(&tj).unwrap(), diff --git a/crates/utilesqlite/src/lib.rs b/crates/utilesqlite/src/lib.rs index 95434d2e..454b6fd1 100644 --- a/crates/utilesqlite/src/lib.rs +++ b/crates/utilesqlite/src/lib.rs @@ -1,21 +1 @@ -// pub use crate::mbtiles::{Mbtiles, MetadataRow, all_metadata}; -// pub mod mbtiles; -// mod mbtiles; -// mod metadata_row; -// mod metadata2tilejson; pub mod mbtiles; - -pub fn add(left: usize, right: usize) -> usize { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} diff --git a/crates/utilesqlite/src/mbtiles.rs b/crates/utilesqlite/src/mbtiles.rs index 3fe480ec..02779ab5 100644 --- a/crates/utilesqlite/src/mbtiles.rs +++ b/crates/utilesqlite/src/mbtiles.rs @@ -4,7 +4,6 @@ use std::path::Path; use rusqlite::{Connection, Result as RusqliteResult}; use tilejson::TileJSON; use tracing::error; -// use crate::metadata_row::MbtilesMetadataRow; use utiles::mbtiles::metadata2tilejson; use utiles::mbtiles::metadata_row::MbtilesMetadataRow; @@ -19,16 +18,6 @@ impl Mbtiles { pub fn metadata(&self) -> RusqliteResult> { mbtiles_metadata(&self.conn) - // let mut stmt = self.conn.as_ref().unwrap().prepare("SELECT name, value FROM metadata")?; - // let rows = stmt.query_map([], |row| { - // Ok( - // MbtilesMetadataRow { - // name: row.get(0)?, - // value: row.get(1)?, - // } - // ) - // })?; - // rows.collect() } pub fn tilejson(&self) -> Result> { @@ -76,201 +65,3 @@ pub fn mbtiles_metadata(conn: &Connection) -> RusqliteResult, rusqlite::Error>>()?; Ok(mdata) } - -// impl MbtilesManager { -// // Create a new instance of the MbtilesManager -// pub fn new() -> MbtilesManager { -// MbtilesManager { conn: None } -// } -// -// // Open a connection to the MBTiles SQLite database -// pub fn open(&mut self, path: &str) -> RusqliteResult<()> { -// self.conn = Some(Connection::open(path)?); -// Ok(()) -// } -// -// // Execute a query on the MBTiles database -// pub fn query(&self, sql: &str, mut map_fn: F) -> RusqliteResult> -// where -// F: FnMut(&rusqlite::Row<'_>) -> RusqliteResult, -// { -// match &self.conn { -// Some(conn) => { -// let mut stmt = conn.prepare(sql)?; -// let rows = stmt.query_map([], |row| map_fn(row))?; -// rows.collect() -// } -// None => Err(rusqlite::Error::InvalidQuery), -// } -// } -// -// pub fn metadata(&self) -> RusqliteResult> { -// return mbtiles_metadata(self.conn.as_ref().unwrap()); -// // let mut stmt = self.conn.as_ref().unwrap().prepare("SELECT name, value FROM metadata")?; -// // let rows = stmt.query_map([], |row| { -// // Ok( -// // MbtilesMetadataRow { -// // name: row.get(0)?, -// // value: row.get(1)?, -// // } -// // ) -// // })?; -// // rows.collect() -// } -// -// // Close the connection to the MBTiles database -// pub fn close(&mut self) -> RusqliteResult<()> { -// if let Some(conn) = self.conn.take() { -// conn.close().map_err(|(_, e)| e) -// } else { -// Ok(()) -// } -// } -// } -// -// // fn main() { -// // let mut mbtiles_manager = MbtilesManager::new(); -// // -// // // Open the database connection -// // mbtiles_manager.open("path/to/your/mbtiles/database.mbtiles").unwrap(); -// // -// // // Execute a query -// // let result: Result> = mbtiles_manager.query("SELECT name FROM some_table", |row| { -// // Ok(row.get(0)?) -// // }); -// // match result { -// // Ok(rows) => { -// // for row in rows { -// // println!("{}", row); -// // } -// // } -// // Err(err) => eprintln!("Query failed: {}", err), -// // } -// // -// // // Close the database connection -// // mbtiles_manager.close().unwrap(); -// // } -// -// // #[derive(Debug)] -// // pub struct Mbtiles<'a> { -// // pub conn: &'a mut rusqlite::Connection, -// // } -// // #[derive(Debug)] -// // pub struct MetadataRow { -// // pub name: String, -// // pub value: String, -// // } -// // -// // impl Mbtiles<'_> { -// // // impl Mbtiles { -// // pub fn metadata<'a>(&'a self) -> rusqlite::Result> { -// // // return all_metadata(self.conn); -// // -// // let mut stmt = self.conn.prepare("SELECT name, value FROM metadata")?; -// // let mdata = stmt -// // .query_map([], |row| { -// // Ok( -// // MetadataRow { -// // name: row.get(0)?, -// // value: row.get(1)?, -// // } -// // ) -// // })? -// // .collect::>>(); -// // return Ok(mdata?); -// // } -// // -// // pub fn open<'a>(fspath: &str) -> rusqlite::Result { -// // let mut conn = rusqlite::Connection::open(fspath)?; -// // let mbt = Mbtiles { -// // conn: &mut conn, -// // }; -// // -// // return Ok(mbt); -// // -// // } -// // -// // pub fn from_conn<'a>(conn: &mut rusqlite::Connection) -> Mbtiles { -// // Mbtiles { -// // conn: conn, -// // } -// // } -// // } -// // -// // -// // pub fn all_metadata (conn: &rusqlite::Connection) -> rusqlite::Result> { -// // let mut stmt = conn.prepare("SELECT name, value FROM metadata")?; -// // let mdata = stmt -// // .query_map([], |row| { -// // Ok( -// // MetadataRow { -// // name: row.get(0)?, -// // value: row.get(1)?, -// // } -// // ) -// // })? -// // .collect::>>(); -// // return Ok(mdata?); -// // } -// -// // #[derive(Debug)] -// // pub struct Mbtiles<'a> { -// // pub conn: &'a mut rusqlite::Connection, -// // } -// // #[derive(Debug)] -// // pub struct MetadataRow { -// // pub name: String, -// // pub value: String, -// // } -// // -// // impl Mbtiles<'_> { -// // // impl Mbtiles { -// // pub fn metadata<'a>(&'a self) -> rusqlite::Result> { -// // // return all_metadata(self.conn); -// // -// // let mut stmt = self.conn.prepare("SELECT name, value FROM metadata")?; -// // let mdata = stmt -// // .query_map([], |row| { -// // Ok( -// // MetadataRow { -// // name: row.get(0)?, -// // value: row.get(1)?, -// // } -// // ) -// // })? -// // .collect::>>(); -// // return Ok(mdata?); -// // } -// // -// // pub fn open<'a>(fspath: &str) -> rusqlite::Result { -// // let mut conn = rusqlite::Connection::open(fspath)?; -// // let mbt = Mbtiles { -// // conn: &mut conn, -// // }; -// // -// // return Ok(mbt); -// // -// // } -// // -// // pub fn from_conn<'a>(conn: &mut rusqlite::Connection) -> Mbtiles { -// // Mbtiles { -// // conn: conn, -// // } -// // } -// // } -// // -// // -// // pub fn all_metadata (conn: &rusqlite::Connection) -> rusqlite::Result> { -// // let mut stmt = conn.prepare("SELECT name, value FROM metadata")?; -// // let mdata = stmt -// // .query_map([], |row| { -// // Ok( -// // MetadataRow { -// // name: row.get(0)?, -// // value: row.get(1)?, -// // } -// // ) -// // })? -// // .collect::>>(); -// // return Ok(mdata?); -// // } From e02d07369f00b78b3300024cb78b3d3b542e4a67 Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Fri, 10 Nov 2023 15:33:37 -0800 Subject: [PATCH 79/80] moved to the totally rust cli' --- Cargo.lock | 1 + crates/utiles/Cargo.toml | 1 + crates/utiles/src/parsing.rs | 9 +- pyproject.toml | 3 +- python/utiles/__main__.py | 4 + python/utiles/_cli.py | 22 -- python/utiles/_click.py | 48 +-- python/utiles/_legacy/__init__.py | 0 python/utiles/_legacy/cli.py | 581 ++++++++++++++++++++++++++++++ python/utiles/cli.py | 575 +---------------------------- python/utiles/rio_plugin.py | 9 +- src/lib.rs | 2 +- tests/cli/test_mt.py | 2 +- tests/mercantests/test_cli.py | 2 +- 14 files changed, 637 insertions(+), 622 deletions(-) delete mode 100644 python/utiles/_cli.py create mode 100644 python/utiles/_legacy/__init__.py create mode 100644 python/utiles/_legacy/cli.py diff --git a/Cargo.lock b/Cargo.lock index eee74b7a..d06f001a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -976,6 +976,7 @@ dependencies = [ "fast_hilbert", "geo-types", "geojson", + "log", "serde", "serde_json", "thiserror", diff --git a/crates/utiles/Cargo.toml b/crates/utiles/Cargo.toml index 75c319a1..51092d48 100644 --- a/crates/utiles/Cargo.toml +++ b/crates/utiles/Cargo.toml @@ -23,3 +23,4 @@ serde_json = "1.0.96" thiserror = "1.0.50" tilejson = "0.3.2" tracing = { version = "0.1.40", features = [] } +log = "0.4.20" diff --git a/crates/utiles/src/parsing.rs b/crates/utiles/src/parsing.rs index a3ecd5b3..0c636764 100644 --- a/crates/utiles/src/parsing.rs +++ b/crates/utiles/src/parsing.rs @@ -21,11 +21,12 @@ pub fn parse_bbox(s: &str) -> serde_json::Result { let v: Value = serde_json::from_str(s)?; + debug!("{}", v); // Assume a single pair of coordinates represents a CoordTuple // and a four-element array represents a BBoxTuple - match v.as_array().map(|arr| arr.len()) { + let bbox = match v.as_array().map(|arr| arr.len()) { Some(2) => { - let coord: (f64, f64) = serde_json::from_value(v)?; + let coord: (f64, f64) = serde_json::from_value::<(f64, f64)>(v)?; Ok(BBox::new(coord.0, coord.1, coord.0, coord.1)) } Some(4) => { @@ -33,7 +34,9 @@ pub fn parse_bbox(s: &str) -> serde_json::Result { Ok(BBox::from(bbox)) } _ => panic!("Expected a two-element array or a four-element array"), - } + }; + debug!("bbox: {:?}", bbox); + bbox } pub fn coords2bounds(mut coords: I) -> Option<(f64, f64, f64, f64)> diff --git a/pyproject.toml b/pyproject.toml index 6c656d9b..e67decb9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,8 @@ dependencies = [ [project.scripts] utiles = "utiles.cli:cli" -ut = "utiles._cli:cli" +ut = "utiles.cli:cli" +utilesv1 = "utiles._legacy.cli:cli" [project.entry-points."rasterio.rio_plugins"] utiles = "utiles.rio_plugin:rio_utiles" diff --git a/python/utiles/__main__.py b/python/utiles/__main__.py index ca3cdf6e..bc3aed63 100644 --- a/python/utiles/__main__.py +++ b/python/utiles/__main__.py @@ -92,3 +92,7 @@ def main() -> None: if __name__ == "__main__": if sys.argv[-1].endswith("__main__.py"): main() + else: + from utiles._cli import cli + + cli() diff --git a/python/utiles/_cli.py b/python/utiles/_cli.py deleted file mode 100644 index 54a5a5e3..00000000 --- a/python/utiles/_cli.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Utiles cli""" -from __future__ import annotations - -import logging -import sys - -from utiles import ut_cli - -logger = logging.getLogger(__name__) - - -def cli() -> None: - args = ["ut", *sys.argv[1:]] - try: - ut_cli(args) - except Exception as e: - logger.error(e) - raise e from e - - -if __name__ == "__main__": - cli() diff --git a/python/utiles/_click.py b/python/utiles/_click.py index 3f94d4b9..5729c3fb 100644 --- a/python/utiles/_click.py +++ b/python/utiles/_click.py @@ -6,7 +6,7 @@ import click from utiles import __version__ -from utiles._cli import cli +from utiles.cli import cli logger = logging.getLogger(__name__) @@ -17,25 +17,31 @@ def get_help_option(self, _ctx: click.Context) -> None: # The CLI command group. -@click.command( - name="utiles", - cls=NoHelpCommand, - help="utiles cli (python-rust)", - no_args_is_help=False, - context_settings={ - "ignore_unknown_options": True, - "allow_extra_args": True, - }, -) -@click.version_option(version=__version__, message="%(version)s") -def cli_click() -> None: - """Execute the main utiles command""" - try: - cli() - except Exception as e: - logger.error(e) - raise click.BadParameter(str(e)) from e - +def _click_cli(name: str) -> NoHelpCommand: + @click.command( + name=name, + cls=NoHelpCommand, + help="utiles cli (python-rust)", + no_args_is_help=False, + context_settings={ + "ignore_unknown_options": True, + "allow_extra_args": True, + }, + ) + @click.version_option(version=__version__, message="%(version)s") + def _cli_fn() -> None: + """Execute the main utiles command""" + try: + cli() + except Exception as e: + logger.error(e) + raise click.BadParameter(str(e)) from e + + return _cli_fn + + +utiles_click = _click_cli("utiles") +ut_click = _click_cli("ut") if __name__ == "__main__": - cli_click() + utiles_click() diff --git a/python/utiles/_legacy/__init__.py b/python/utiles/_legacy/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/python/utiles/_legacy/cli.py b/python/utiles/_legacy/cli.py new file mode 100644 index 00000000..ee7a19d1 --- /dev/null +++ b/python/utiles/_legacy/cli.py @@ -0,0 +1,581 @@ +"""Utiles cli""" +from __future__ import annotations + +import json +import logging +import sys +from typing import Any, Dict, Iterable, List, Optional, Tuple, Union + +import click + +import utiles + + +def configure_logging(verbosity: int) -> None: + """Configure logging level + + Parameters + ---------- + verbosity : int + The number of `-v` options from the command line. + + Returns + ------- + None + """ + log_level = max(10, 30 - 10 * verbosity) + logging.basicConfig(stream=sys.stderr, level=log_level) + + +logger = logging.getLogger(__name__) + +RS = "\x1e" + + +def normalize_input(input: str) -> List[str]: + """Normalize file or string input.""" + try: + src = click.open_file(input).readlines() + except OSError: + src = [input] + return src + + +def iter_lines(lines: List[str]) -> Iterable[str]: + """Iterate over lines of input, stripping and skipping.""" + for line in lines: + line_stripped = line.strip() + if line_stripped: + yield line_stripped + + +# The CLI command group. +@click.group(name="utiles", help="utiles cli (python)") +@click.option("--verbose", "-v", count=True, help="Increase verbosity.") +@click.option("--quiet", "-q", count=True, help="Decrease verbosity.") +@click.version_option(version=utiles.__version__, message="%(version)s") +@click.pass_context +def cli(ctx: click.Context, verbose: int, quiet: int) -> None: + """Execute the main utiles command""" + verbosity = verbose - quiet + configure_logging(verbosity) + ctx.obj = {} + ctx.obj["verbosity"] = verbosity + + +# Commands are below. + + +# The shapes command. +@cli.command(short_help="Print the shapes of tiles as GeoJSON.") +# This input is either a filename, stdin, or a string. +@click.argument("input", default="-", required=False) +# Coordinate precision option. +@click.option( + "--precision", type=int, default=None, help="Decimal precision of coordinates." +) +# JSON formatting options. +@click.option( + "--indent", default=None, type=int, help="Indentation level for JSON output" +) +@click.option( + "--compact/--no-compact", default=False, help="Use compact separators (',', ':')." +) +# Geographic (default) or Mercator switch. +@click.option( + "--geographic", + "projected", + flag_value="geographic", + default=True, + help="Output in geographic coordinates (the default).", +) +@click.option( + "--mercator", + "projected", + flag_value="mercator", + help="Output in Web Mercator coordinates.", +) +@click.option( + "--seq", + is_flag=True, + default=False, + help="Write a RS-delimited JSON sequence (default is LF).", +) +# GeoJSON feature (default) or collection switch. Meaningful only +# when --x-json-seq is used. +@click.option( + "--feature", + "output_mode", + flag_value="feature", + default=True, + help="Output as sequence of GeoJSON features (the default).", +) +@click.option( + "--bbox", + "output_mode", + flag_value="bbox", + help="Output as sequence of GeoJSON bbox arrays.", +) +@click.option( + "--collect", + is_flag=True, + default=False, + help="Output as a GeoJSON feature collections.", +) +# Optionally write out bboxen in a form that goes +# straight into GDAL utilities like gdalwarp. +@click.option( + "--extents/--no-extents", + default=False, + help="Write shape extents as ws-separated strings (default is False).", +) +# Optionally buffer the shapes by shifting the x and y values of each +# vertex by a constant number of decimal degrees or meters (depending +# on whether --geographic or --mercator is in effect). +@click.option( + "--buffer", + type=float, + default=None, + help="Shift shape x and y values by a constant number", +) +@click.pass_context +def shapes( + _ctx: click.Context, + input: str, + precision: Optional[int] = None, + indent: Optional[int] = None, + projected: str = "geographic", + output_mode: str = "feature", + buffer: Optional[float] = None, + compact: bool = False, + seq: bool = False, + collect: bool = False, + extents: bool = False, +) -> None: + """Print tiles as GeoJSON feature collections or sequences. + + Input may be a compact newline-delimited sequences of JSON or + a pretty-printed ASCII RS-delimited sequence of JSON (like + https://tools.ietf.org/html/rfc8142 and + https://tools.ietf.org/html/rfc7159). + + Tile descriptions may be either an [x, y, z] array or a JSON + object of the form + + {"tile": [x, y, z], "properties": {"name": "foo", ...}} + + In the latter case, the properties object will be used to update + the properties object of the output feature. + + Example: + + \b + echo "[486, 332, 10]" | utiles shapes --precision 4 --bbox + [-9.1406, 53.1204, -8.7891, 53.3309] + + """ + dump_kwds: Dict[str, Union[bool, int, Tuple[str, ...]]] = {"sort_keys": True} + if indent: + dump_kwds["indent"] = indent + if compact: + dump_kwds["separators"] = (",", ":") + + src = normalize_input(input) + features = [] + col_xs = [] + col_ys = [] + + for _i, line in enumerate(iter_lines(src)): + obj = json.loads(line) + if isinstance(obj, dict): + x, y, z = obj["tile"][:3] + props = obj.get("properties") + fid = obj.get("id") + elif isinstance(obj, list): + x, y, z = obj[:3] + props = {} + fid = None + else: + msg = f"{obj}" + raise click.BadParameter(msg, param=input, param_hint="input") # type: ignore + + feature = utiles.feature( + (x, y, z), + fid=fid, + props=props, + projected=projected, + buffer=buffer, + precision=precision, + ) + bbox = feature["bbox"] + w, s, e, n = bbox + col_xs.extend([w, e]) + col_ys.extend([s, n]) + + if collect: + features.append(feature) + elif extents: + click.echo(" ".join(map(str, bbox))) + else: + if seq: + click.echo(RS) + if output_mode == "bbox": + click.echo(json.dumps(bbox, **dump_kwds)) # type: ignore + elif output_mode == "feature": + click.echo(json.dumps(feature, **dump_kwds)) # type: ignore + + if collect and features: + bbox = [min(col_xs), min(col_ys), max(col_xs), max(col_ys)] + click.echo( + json.dumps( + {"type": "FeatureCollection", "bbox": bbox, "features": features}, + **dump_kwds, # type: ignore + ) + ) + + +# The tiles command. +@cli.command( + short_help="Print tiles that overlap or contain a lng/lat point, " + "bounding box, or GeoJSON objects." +) +# Mandatory Mercator zoom level argument. +@click.argument("zoom", type=int, default=-1) +# This input is either a filename, stdin, or a string. +# Has to follow the zoom arg. +@click.argument("input", default="-", required=False) +@click.option( + "--seq/--lf", + default=False, + help="Write a RS-delimited JSON sequence (default is LF).", +) +@click.pass_context +def tiles( + _ctx: click.Context, + zoom: int = -1, + input: str = "-", + seq: bool = False, +) -> None: + """Lists Web Mercator tiles at ZOOM level intersecting + GeoJSON [west, south, east, north] bounding boxen, features, or + collections read from stdin. Output is a JSON + [x, y, z] array. + + Input may be a compact newline-delimited sequences of JSON or + a pretty-printed ASCII RS-delimited sequence of JSON (like + https://tools.ietf.org/html/rfc8142 and + https://tools.ietf.org/html/rfc7159). + + Example: + + \b + $ echo "[-105.05, 39.95, -105, 40]" | utiles tiles 12 + [852, 1550, 12] + [852, 1551, 12] + [853, 1550, 12] + [853, 1551, 12] + + """ + src = iter(normalize_input(input)) + first_line = next(src) + + # If input is RS-delimited JSON sequence. + if first_line.startswith(RS): + + def feature_gen() -> Iterable[Dict[str, Any]]: + buffer = first_line.strip(RS) + for line in src: + if line.startswith(RS): + if buffer: + yield json.loads(buffer) + buffer = line.strip(RS) + else: + buffer += line + else: + yield json.loads(buffer) + + else: + + def feature_gen() -> Iterable[Dict[str, Any]]: + yield json.loads(first_line) + for line in src: + yield json.loads(line) + + for obj in feature_gen(): + if isinstance(obj, list): + bbox = obj + if len(bbox) == 2: + bbox += bbox + elif len(bbox) != 4: + msg = f"{bbox}" + raise click.BadParameter(msg, param=input, param_hint="input") + elif isinstance(obj, dict): + if "bbox" in obj: + bbox = obj["bbox"] + else: + bbox = utiles.geojson_bounds(obj) + + west, south, east, north = bbox + epsilon = 1.0e-10 + + if east != west and north != south: + # 2D bbox + # shrink the bounds a small amount so that + # shapes/tiles round trip. + west += epsilon + south += epsilon + east -= epsilon + north -= epsilon + + for tile in utiles.tiles(west, south, east, north, [zoom], truncate=False): + vals = (tile.x, tile.y, zoom) + output = json.dumps(vals) + if seq: + click.echo(RS) + click.echo(output) + + +# The bounding-tile command. +@cli.command( + "bounding-tile", + short_help="Print the bounding tile of a lng/lat point, " + "bounding box, or GeoJSON objects.", +) +# This input is either a filename, stdin, or a string. +@click.argument("input", default="-", required=False) +@click.option( + "--seq/--lf", + default=False, + help="Write a RS-delimited JSON sequence (default is LF).", +) +@click.pass_context +def bounding_tile(_ctx: click.Context, input: str, seq: bool = False) -> None: + """Print the Web Mercator tile at ZOOM level bounding + GeoJSON [west, south, east, north] bounding boxes, features, or + collections read from stdin. + + Input may be a compact newline-delimited sequences of JSON or + a pretty-printed ASCII RS-delimited sequence of JSON (like + https://tools.ietf.org/html/rfc8142 and + https://tools.ietf.org/html/rfc7159). + + Example: + + \b + echo "[-105.05, 39.95, -105, 40]" | utiles bounding-tile + [426, 775, 11] + + """ + src = iter(normalize_input(input)) + first_line = next(src) + + # If input is RS-delimited JSON sequence. + if first_line.startswith(RS): + + def feature_gen() -> Iterable[Dict[str, Any]]: + buffer = first_line.strip(RS) + for line in src: + if line.startswith(RS): + if buffer: + yield json.loads(buffer) + buffer = line.strip(RS) + else: + buffer += line + else: + yield json.loads(buffer) + + else: + + def feature_gen() -> Iterable[Dict[str, Any]]: + yield json.loads(first_line) + for line in src: + yield json.loads(line) + + for obj in feature_gen(): + if isinstance(obj, list): + bbox = obj + if len(bbox) == 2: + bbox += bbox + elif len(bbox) != 4: + msg = f"{bbox}" + raise click.BadParameter(msg, param=input, param_hint="input") + + elif isinstance(obj, dict): + if "bbox" in obj: + bbox = obj["bbox"] + else: + bbox = utiles.geojson_bounds(obj) + + west, south, east, north = bbox + vals = utiles.bounding_tile(west, south, east, north, truncate=False) + + # print(vals) + # output = json.dumps(vals) + output = vals.json(obj=False) + + if seq: + click.echo(RS) + + click.echo(output) + + +# The children command. +@cli.command(short_help="Print the children of the tile.") +@click.argument("input", default="-", required=False) +@click.option( + "--depth", + type=int, + default=1, + help="Number of zoom levels to traverse (default is 1).", +) +@click.pass_context +def children(_ctx: click.Context, input: str, depth: int = 1) -> None: + """Takes [x, y, z] tiles as input and writes children to stdout + in the same form. + + Input may be a compact newline-delimited sequences of JSON or + a pretty-printed ASCII RS-delimited sequence of JSON (like + https://tools.ietf.org/html/rfc8142 and + https://tools.ietf.org/html/rfc7159). + + Example: + + \b + echo "[486, 332, 10]" | utiles children + [972, 664, 11] + [973, 664, 11] + [973, 665, 11] + [972, 665, 11] + + """ + src = normalize_input(input) + for line in iter_lines(src): + tiles = [json.loads(line)[:3]] + for _i in range(depth): + tiles = sum([utiles.children(t) for t in tiles], []) + for t in tiles: + click.echo(t.json(obj=False)) + + +# The parent command. +@cli.command(short_help="Print the parent tile.") +@click.argument("input", default="-", required=False) +@click.option( + "--depth", + type=int, + default=1, + help="Number of zoom levels to traverse (default is 1).", +) +@click.pass_context +def parent(_ctx: click.Context, input: str, depth: int = 1) -> None: + """Takes [x, y, z] tiles as input and writes parents to stdout + in the same form. + + Input may be a compact newline-delimited sequences of JSON or + a pretty-printed ASCII RS-delimited sequence of JSON (like + https://tools.ietf.org/html/rfc8142 and + https://tools.ietf.org/html/rfc7159). + + Example: + + \b + echo "[486, 332, 10]" | utiles parent + [243, 166, 9] + + """ + src = normalize_input(input) + for line in iter_lines(src): + parsed = json.loads(line)[:3] + tile = utiles.parse_tile_arg(parsed) + if tile[2] - depth < 0: + msg = f"Invalid parent level: {tile[2] - depth}" + raise click.UsageError(msg) + for _i in range(depth): + ptile = utiles.parent(tile) + if ptile is None: + msg = f"Invalid parent level: {tile[2] - depth}" + raise click.UsageError(msg) + tile = ptile + output = tile.json(obj=False) + click.echo(output) + + +# The neighbors command. +@cli.command(short_help="Print the neighbors of the tile.") +@click.argument("input", default="-", required=False) +@click.pass_context +def neighbors(_ctx: click.Context, input: str) -> None: + """Takes [x, y, z] tiles as input and writes adjacent + tiles on the same zoom level to stdout in the same form. + + There are no ordering guarantees for the output tiles. + + Input may be a compact newline-delimited sequences of JSON or + a pretty-printed ASCII RS-delimited sequence of JSON (like + https://tools.ietf.org/html/rfc8142 and + https://tools.ietf.org/html/rfc7159). + + Example: + + \b + echo "[486, 332, 10]" | utiles neighbors + [485, 331, 10] + [485, 332, 10] + [485, 333, 10] + [486, 331, 10] + [486, 333, 10] + [487, 331, 10] + [487, 332, 10] + [487, 333, 10] + + """ + src = normalize_input(input) + for line in iter_lines(src): + tile = json.loads(line)[:3] + tiles = utiles.neighbors(tile) + for t in tiles: + click.echo(t.json(obj=False)) + + +@cli.command(short_help="Convert to/from quadkeys.") +@click.argument("input", default="-", required=False) +@click.pass_context +def quadkey(_ctx: click.Context, input: str) -> None: + """Takes [x, y, z] tiles or quadkeys as input and writes + quadkeys or a [x, y, z] tiles to stdout, respectively. + + Input may be a compact newline-delimited sequences of JSON or + a pretty-printed ASCII RS-delimited sequence of JSON (like + https://tools.ietf.org/html/rfc8142 and + https://tools.ietf.org/html/rfc7159). + + Examples: + + \b + echo "[486, 332, 10]" | utiles quadkey + 0313102310 + + \b + echo "0313102310" | utiles quadkey + [486, 332, 10] + + """ + src = normalize_input(input) + try: + for line in iter_lines(src): + if line[0] == "[": + tile = json.loads(line)[:3] + output = utiles.quadkey(tile) + else: + tile = utiles.quadkey_to_tile(line) + output = tile.json(obj=False) + click.echo(output) + except ValueError as ve: + e = click.BadParameter( + f"{input}", param=click.Parameter("input", type=str), param_hint="input" + ) + raise e from ve + + +if __name__ == "__main__": + cli() diff --git a/python/utiles/cli.py b/python/utiles/cli.py index ee7a19d1..88d936c0 100644 --- a/python/utiles/cli.py +++ b/python/utiles/cli.py @@ -1,580 +1,25 @@ """Utiles cli""" from __future__ import annotations -import json import logging import sys -from typing import Any, Dict, Iterable, List, Optional, Tuple, Union - -import click - -import utiles - - -def configure_logging(verbosity: int) -> None: - """Configure logging level - - Parameters - ---------- - verbosity : int - The number of `-v` options from the command line. - - Returns - ------- - None - """ - log_level = max(10, 30 - 10 * verbosity) - logging.basicConfig(stream=sys.stderr, level=log_level) +from utiles import ut_cli logger = logging.getLogger(__name__) -RS = "\x1e" - - -def normalize_input(input: str) -> List[str]: - """Normalize file or string input.""" - try: - src = click.open_file(input).readlines() - except OSError: - src = [input] - return src - - -def iter_lines(lines: List[str]) -> Iterable[str]: - """Iterate over lines of input, stripping and skipping.""" - for line in lines: - line_stripped = line.strip() - if line_stripped: - yield line_stripped - - -# The CLI command group. -@click.group(name="utiles", help="utiles cli (python)") -@click.option("--verbose", "-v", count=True, help="Increase verbosity.") -@click.option("--quiet", "-q", count=True, help="Decrease verbosity.") -@click.version_option(version=utiles.__version__, message="%(version)s") -@click.pass_context -def cli(ctx: click.Context, verbose: int, quiet: int) -> None: - """Execute the main utiles command""" - verbosity = verbose - quiet - configure_logging(verbosity) - ctx.obj = {} - ctx.obj["verbosity"] = verbosity - - -# Commands are below. - - -# The shapes command. -@cli.command(short_help="Print the shapes of tiles as GeoJSON.") -# This input is either a filename, stdin, or a string. -@click.argument("input", default="-", required=False) -# Coordinate precision option. -@click.option( - "--precision", type=int, default=None, help="Decimal precision of coordinates." -) -# JSON formatting options. -@click.option( - "--indent", default=None, type=int, help="Indentation level for JSON output" -) -@click.option( - "--compact/--no-compact", default=False, help="Use compact separators (',', ':')." -) -# Geographic (default) or Mercator switch. -@click.option( - "--geographic", - "projected", - flag_value="geographic", - default=True, - help="Output in geographic coordinates (the default).", -) -@click.option( - "--mercator", - "projected", - flag_value="mercator", - help="Output in Web Mercator coordinates.", -) -@click.option( - "--seq", - is_flag=True, - default=False, - help="Write a RS-delimited JSON sequence (default is LF).", -) -# GeoJSON feature (default) or collection switch. Meaningful only -# when --x-json-seq is used. -@click.option( - "--feature", - "output_mode", - flag_value="feature", - default=True, - help="Output as sequence of GeoJSON features (the default).", -) -@click.option( - "--bbox", - "output_mode", - flag_value="bbox", - help="Output as sequence of GeoJSON bbox arrays.", -) -@click.option( - "--collect", - is_flag=True, - default=False, - help="Output as a GeoJSON feature collections.", -) -# Optionally write out bboxen in a form that goes -# straight into GDAL utilities like gdalwarp. -@click.option( - "--extents/--no-extents", - default=False, - help="Write shape extents as ws-separated strings (default is False).", -) -# Optionally buffer the shapes by shifting the x and y values of each -# vertex by a constant number of decimal degrees or meters (depending -# on whether --geographic or --mercator is in effect). -@click.option( - "--buffer", - type=float, - default=None, - help="Shift shape x and y values by a constant number", -) -@click.pass_context -def shapes( - _ctx: click.Context, - input: str, - precision: Optional[int] = None, - indent: Optional[int] = None, - projected: str = "geographic", - output_mode: str = "feature", - buffer: Optional[float] = None, - compact: bool = False, - seq: bool = False, - collect: bool = False, - extents: bool = False, -) -> None: - """Print tiles as GeoJSON feature collections or sequences. - - Input may be a compact newline-delimited sequences of JSON or - a pretty-printed ASCII RS-delimited sequence of JSON (like - https://tools.ietf.org/html/rfc8142 and - https://tools.ietf.org/html/rfc7159). - - Tile descriptions may be either an [x, y, z] array or a JSON - object of the form - - {"tile": [x, y, z], "properties": {"name": "foo", ...}} - - In the latter case, the properties object will be used to update - the properties object of the output feature. - - Example: - - \b - echo "[486, 332, 10]" | utiles shapes --precision 4 --bbox - [-9.1406, 53.1204, -8.7891, 53.3309] - - """ - dump_kwds: Dict[str, Union[bool, int, Tuple[str, ...]]] = {"sort_keys": True} - if indent: - dump_kwds["indent"] = indent - if compact: - dump_kwds["separators"] = (",", ":") - - src = normalize_input(input) - features = [] - col_xs = [] - col_ys = [] - - for _i, line in enumerate(iter_lines(src)): - obj = json.loads(line) - if isinstance(obj, dict): - x, y, z = obj["tile"][:3] - props = obj.get("properties") - fid = obj.get("id") - elif isinstance(obj, list): - x, y, z = obj[:3] - props = {} - fid = None - else: - msg = f"{obj}" - raise click.BadParameter(msg, param=input, param_hint="input") # type: ignore - - feature = utiles.feature( - (x, y, z), - fid=fid, - props=props, - projected=projected, - buffer=buffer, - precision=precision, - ) - bbox = feature["bbox"] - w, s, e, n = bbox - col_xs.extend([w, e]) - col_ys.extend([s, n]) - - if collect: - features.append(feature) - elif extents: - click.echo(" ".join(map(str, bbox))) - else: - if seq: - click.echo(RS) - if output_mode == "bbox": - click.echo(json.dumps(bbox, **dump_kwds)) # type: ignore - elif output_mode == "feature": - click.echo(json.dumps(feature, **dump_kwds)) # type: ignore - - if collect and features: - bbox = [min(col_xs), min(col_ys), max(col_xs), max(col_ys)] - click.echo( - json.dumps( - {"type": "FeatureCollection", "bbox": bbox, "features": features}, - **dump_kwds, # type: ignore - ) - ) - - -# The tiles command. -@cli.command( - short_help="Print tiles that overlap or contain a lng/lat point, " - "bounding box, or GeoJSON objects." -) -# Mandatory Mercator zoom level argument. -@click.argument("zoom", type=int, default=-1) -# This input is either a filename, stdin, or a string. -# Has to follow the zoom arg. -@click.argument("input", default="-", required=False) -@click.option( - "--seq/--lf", - default=False, - help="Write a RS-delimited JSON sequence (default is LF).", -) -@click.pass_context -def tiles( - _ctx: click.Context, - zoom: int = -1, - input: str = "-", - seq: bool = False, -) -> None: - """Lists Web Mercator tiles at ZOOM level intersecting - GeoJSON [west, south, east, north] bounding boxen, features, or - collections read from stdin. Output is a JSON - [x, y, z] array. - - Input may be a compact newline-delimited sequences of JSON or - a pretty-printed ASCII RS-delimited sequence of JSON (like - https://tools.ietf.org/html/rfc8142 and - https://tools.ietf.org/html/rfc7159). - - Example: - - \b - $ echo "[-105.05, 39.95, -105, 40]" | utiles tiles 12 - [852, 1550, 12] - [852, 1551, 12] - [853, 1550, 12] - [853, 1551, 12] - - """ - src = iter(normalize_input(input)) - first_line = next(src) - - # If input is RS-delimited JSON sequence. - if first_line.startswith(RS): - - def feature_gen() -> Iterable[Dict[str, Any]]: - buffer = first_line.strip(RS) - for line in src: - if line.startswith(RS): - if buffer: - yield json.loads(buffer) - buffer = line.strip(RS) - else: - buffer += line - else: - yield json.loads(buffer) - - else: - - def feature_gen() -> Iterable[Dict[str, Any]]: - yield json.loads(first_line) - for line in src: - yield json.loads(line) - - for obj in feature_gen(): - if isinstance(obj, list): - bbox = obj - if len(bbox) == 2: - bbox += bbox - elif len(bbox) != 4: - msg = f"{bbox}" - raise click.BadParameter(msg, param=input, param_hint="input") - elif isinstance(obj, dict): - if "bbox" in obj: - bbox = obj["bbox"] - else: - bbox = utiles.geojson_bounds(obj) - - west, south, east, north = bbox - epsilon = 1.0e-10 - - if east != west and north != south: - # 2D bbox - # shrink the bounds a small amount so that - # shapes/tiles round trip. - west += epsilon - south += epsilon - east -= epsilon - north -= epsilon - - for tile in utiles.tiles(west, south, east, north, [zoom], truncate=False): - vals = (tile.x, tile.y, zoom) - output = json.dumps(vals) - if seq: - click.echo(RS) - click.echo(output) - - -# The bounding-tile command. -@cli.command( - "bounding-tile", - short_help="Print the bounding tile of a lng/lat point, " - "bounding box, or GeoJSON objects.", -) -# This input is either a filename, stdin, or a string. -@click.argument("input", default="-", required=False) -@click.option( - "--seq/--lf", - default=False, - help="Write a RS-delimited JSON sequence (default is LF).", -) -@click.pass_context -def bounding_tile(_ctx: click.Context, input: str, seq: bool = False) -> None: - """Print the Web Mercator tile at ZOOM level bounding - GeoJSON [west, south, east, north] bounding boxes, features, or - collections read from stdin. - - Input may be a compact newline-delimited sequences of JSON or - a pretty-printed ASCII RS-delimited sequence of JSON (like - https://tools.ietf.org/html/rfc8142 and - https://tools.ietf.org/html/rfc7159). - - Example: - - \b - echo "[-105.05, 39.95, -105, 40]" | utiles bounding-tile - [426, 775, 11] - - """ - src = iter(normalize_input(input)) - first_line = next(src) - - # If input is RS-delimited JSON sequence. - if first_line.startswith(RS): - - def feature_gen() -> Iterable[Dict[str, Any]]: - buffer = first_line.strip(RS) - for line in src: - if line.startswith(RS): - if buffer: - yield json.loads(buffer) - buffer = line.strip(RS) - else: - buffer += line - else: - yield json.loads(buffer) - - else: - - def feature_gen() -> Iterable[Dict[str, Any]]: - yield json.loads(first_line) - for line in src: - yield json.loads(line) - - for obj in feature_gen(): - if isinstance(obj, list): - bbox = obj - if len(bbox) == 2: - bbox += bbox - elif len(bbox) != 4: - msg = f"{bbox}" - raise click.BadParameter(msg, param=input, param_hint="input") - - elif isinstance(obj, dict): - if "bbox" in obj: - bbox = obj["bbox"] - else: - bbox = utiles.geojson_bounds(obj) - - west, south, east, north = bbox - vals = utiles.bounding_tile(west, south, east, north, truncate=False) - - # print(vals) - # output = json.dumps(vals) - output = vals.json(obj=False) - - if seq: - click.echo(RS) - - click.echo(output) - - -# The children command. -@cli.command(short_help="Print the children of the tile.") -@click.argument("input", default="-", required=False) -@click.option( - "--depth", - type=int, - default=1, - help="Number of zoom levels to traverse (default is 1).", -) -@click.pass_context -def children(_ctx: click.Context, input: str, depth: int = 1) -> None: - """Takes [x, y, z] tiles as input and writes children to stdout - in the same form. - - Input may be a compact newline-delimited sequences of JSON or - a pretty-printed ASCII RS-delimited sequence of JSON (like - https://tools.ietf.org/html/rfc8142 and - https://tools.ietf.org/html/rfc7159). - - Example: - - \b - echo "[486, 332, 10]" | utiles children - [972, 664, 11] - [973, 664, 11] - [973, 665, 11] - [972, 665, 11] - - """ - src = normalize_input(input) - for line in iter_lines(src): - tiles = [json.loads(line)[:3]] - for _i in range(depth): - tiles = sum([utiles.children(t) for t in tiles], []) - for t in tiles: - click.echo(t.json(obj=False)) - - -# The parent command. -@cli.command(short_help="Print the parent tile.") -@click.argument("input", default="-", required=False) -@click.option( - "--depth", - type=int, - default=1, - help="Number of zoom levels to traverse (default is 1).", -) -@click.pass_context -def parent(_ctx: click.Context, input: str, depth: int = 1) -> None: - """Takes [x, y, z] tiles as input and writes parents to stdout - in the same form. - - Input may be a compact newline-delimited sequences of JSON or - a pretty-printed ASCII RS-delimited sequence of JSON (like - https://tools.ietf.org/html/rfc8142 and - https://tools.ietf.org/html/rfc7159). - - Example: - - \b - echo "[486, 332, 10]" | utiles parent - [243, 166, 9] - - """ - src = normalize_input(input) - for line in iter_lines(src): - parsed = json.loads(line)[:3] - tile = utiles.parse_tile_arg(parsed) - if tile[2] - depth < 0: - msg = f"Invalid parent level: {tile[2] - depth}" - raise click.UsageError(msg) - for _i in range(depth): - ptile = utiles.parent(tile) - if ptile is None: - msg = f"Invalid parent level: {tile[2] - depth}" - raise click.UsageError(msg) - tile = ptile - output = tile.json(obj=False) - click.echo(output) - - -# The neighbors command. -@cli.command(short_help="Print the neighbors of the tile.") -@click.argument("input", default="-", required=False) -@click.pass_context -def neighbors(_ctx: click.Context, input: str) -> None: - """Takes [x, y, z] tiles as input and writes adjacent - tiles on the same zoom level to stdout in the same form. - - There are no ordering guarantees for the output tiles. - - Input may be a compact newline-delimited sequences of JSON or - a pretty-printed ASCII RS-delimited sequence of JSON (like - https://tools.ietf.org/html/rfc8142 and - https://tools.ietf.org/html/rfc7159). - - Example: - - \b - echo "[486, 332, 10]" | utiles neighbors - [485, 331, 10] - [485, 332, 10] - [485, 333, 10] - [486, 331, 10] - [486, 333, 10] - [487, 331, 10] - [487, 332, 10] - [487, 333, 10] - - """ - src = normalize_input(input) - for line in iter_lines(src): - tile = json.loads(line)[:3] - tiles = utiles.neighbors(tile) - for t in tiles: - click.echo(t.json(obj=False)) - - -@cli.command(short_help="Convert to/from quadkeys.") -@click.argument("input", default="-", required=False) -@click.pass_context -def quadkey(_ctx: click.Context, input: str) -> None: - """Takes [x, y, z] tiles or quadkeys as input and writes - quadkeys or a [x, y, z] tiles to stdout, respectively. - - Input may be a compact newline-delimited sequences of JSON or - a pretty-printed ASCII RS-delimited sequence of JSON (like - https://tools.ietf.org/html/rfc8142 and - https://tools.ietf.org/html/rfc7159). - - Examples: - - \b - echo "[486, 332, 10]" | utiles quadkey - 0313102310 - \b - echo "0313102310" | utiles quadkey - [486, 332, 10] +def cli() -> None: + args = sys.argv[1:] - """ - src = normalize_input(input) + # if first arg is "utiles" then remove it + if args and (args[0] == "utiles" or args[0] == "ut"): + args = args[1:] try: - for line in iter_lines(src): - if line[0] == "[": - tile = json.loads(line)[:3] - output = utiles.quadkey(tile) - else: - tile = utiles.quadkey_to_tile(line) - output = tile.json(obj=False) - click.echo(output) - except ValueError as ve: - e = click.BadParameter( - f"{input}", param=click.Parameter("input", type=str), param_hint="input" - ) - raise e from ve + ut_cli(["ut", *args]) + except Exception as e: + logger.error(e) + raise e from e if __name__ == "__main__": diff --git a/python/utiles/rio_plugin.py b/python/utiles/rio_plugin.py index ecace2f0..85c9faa2 100644 --- a/python/utiles/rio_plugin.py +++ b/python/utiles/rio_plugin.py @@ -1,9 +1,4 @@ -import click - -from utiles.cli import cli as rio_utiles +from utiles._click import ut_click as rio_ut +from utiles._click import utiles_click as rio_utiles __all__ = ("rio_ut", "rio_utiles") - -rio_ut = click.CommandCollection( - sources=[rio_utiles], name="ut", help="utiles cli (alias)" -) diff --git a/src/lib.rs b/src/lib.rs index b45cb77b..23680aef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ -use std::collections::{HashMap, HashSet}; use pyo3::exceptions::{self, PyValueError}; use pyo3::prelude::*; use pyo3::types::{PyDict, PyTuple}; @@ -7,6 +6,7 @@ use pyutiles::pyiters::CoordinateIterator; use pyutiles::pylnglat::PyLngLat; use pyutiles::pylnglatbbox::PyLngLatBbox; use pyutiles::pytile::PyTile; +use std::collections::{HashMap, HashSet}; use utiles::bbox::BBox; use utiles::libtiletype; use utiles::zoom::ZoomOrZooms; diff --git a/tests/cli/test_mt.py b/tests/cli/test_mt.py index f3b90681..1cd793c6 100644 --- a/tests/cli/test_mt.py +++ b/tests/cli/test_mt.py @@ -14,7 +14,7 @@ def _run_cli( _python = sys.executable _args = args or [] res = run( - [_python, "-m", "utiles._cli", *_args], + [_python, "-m", "utiles.cli", *_args], input=input, capture_output=True, text=True, diff --git a/tests/mercantests/test_cli.py b/tests/mercantests/test_cli.py index 5ae15580..a000ee54 100644 --- a/tests/mercantests/test_cli.py +++ b/tests/mercantests/test_cli.py @@ -5,7 +5,7 @@ import pytest from click.testing import CliRunner -from utiles.cli import cli +from utiles._legacy.cli import cli def test_cli_shapes_failure() -> None: From c537bc7d75639d12eb2b3bdd90be09d8db1aae6a Mon Sep 17 00:00:00 2001 From: jessekrubin Date: Fri, 10 Nov 2023 15:43:38 -0800 Subject: [PATCH 80/80] CCCCCHANGE LOG --- CHANGELOG.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dc48f0c..105905f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,16 @@ -# 0.1.0 +# 0.2.0 (2023-11-10) + + - Converted cli to rust as an excerise in learning clap + - Moved old click cli to `utiles._legacy.cli` + - Added tilejson/tj command to rust cli to write out tilejson files for mbtiles + - Added meta command to rust cli to write out json of metadata table for mbtiles + +# 0.1.0 (2023-10-27) - Drop python 3.7 (was good knowing you) - Update pyo3 to 0.20.0 - Added rasterio/rio entry points ('utiles' and 'ut' alias bc why type `rio utiles` over `rio ut`) - # 0.0.2 - Added `__len__` to TilesGenerator for pbars