1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
// use ahash::HashSet;
use HashSet;
use HashSet;
use crateassert_all_same_zoom;
use crate::;
/// Finds edge tiles in a given set of tiles. optionally wraps the x-coordinate
///
/// # Errors
///
/// Errors if the tiles are not all the same zoom level
// ============================================================================
// previous implementation that uses ndarray
// ============================================================================
// fn burn_tiles(tiles: &[Tile], zbox: TileZBox) -> Array2<bool> {
// let xmin = zbox.min.x as usize;
// let ymin = zbox.min.y as usize;
// // add 3 to pad as dydx ranges are inclusive
// let dx = zbox.dx() as usize + 3;
// let dy = zbox.dy() as usize + 3;
// let mut burn = Array2::<bool>::default((dx, dy));
// for tile in tiles {
// let x_us = tile.x() as usize - xmin + 1; // +1 for padding
// let y_us = tile.y() as usize - ymin + 1; // +1 for padding
// burn[(x_us, y_us)] = true;
// }
// burn
// }
// pub fn find_edges(tiles: &[Tile]) -> UtilesResult<Vec<Tile>> {
// let zbox = TileZBox::from_tiles(tiles)?;
// // Create the burn array with padding
// let burn = burn_tiles(tiles, zbox);
// let uxmin = (zbox.minx() - 1) as usize; // Adjusted for padding
// let uymin = (zbox.miny() - 1) as usize; // Adjusted for padding
//
// let mut edge_tiles = Vec::new();
// for ((i, j), &is_burn) in burn.indexed_iter() {
// if is_burn {
// for &(dx, dy) in IDXS.iter() {
// let ni = (i as isize + dx) as usize;
// let nj = (j as isize + dy) as usize;
//
// // Since we have padding, we don't need to check bounds
// if !burn[(ni, nj)] {
// // This is an edge tile
// let x = (i + uxmin) as u32;
// let y = (j + uymin) as u32;
// edge_tiles.push(Tile::new(x, y, zbox.zoom));
// break;
// }
// }
// }
// }
//
// Ok(edge_tiles)
// }
// ============================================================================
// previous slower implementation
// ============================================================================
// use crate::{UtilesError, UtilesResult};
// use ndarray::{stack, Array2, Axis};
// use utiles_core::{Tile, TileLike, TileZBox};
//
// static IDXS: &[(isize, isize)] = &[
// (-1, -1),
// (-1, 0),
// (-1, 1),
// (0, -1),
// (0, 1),
// (1, -1),
// (1, 0),
// (1, 1),
// ];
//
// fn burn_tiles(tiles: &[Tile], zbox: TileZBox) -> Array2<bool> {
// let xmin = zbox.min.x as usize;
// let ymin = zbox.min.y as usize;
// let dx = zbox.dx() as usize;
// let dy = zbox.dy() as usize;
// let mut burn = Array2::<bool>::default((dx + 3, dy + 3));
// for tile in tiles {
// let x_us = tile.x() as usize;
// let y_us = tile.y() as usize;
// burn[(x_us - xmin + 1, y_us - ymin + 1)] = true;
// }
// burn
// }
//
// fn roll_2d(arr: &Array2<bool>, x_shift: isize, y_shift: isize) -> Array2<bool> {
// let (rows, cols) = arr.dim();
// let mut rolled = Array2::default((rows, cols));
//
// // rolled.indexed_iter().par_map(
// // )
// for i in 0..rows {
// for j in 0..cols {
// let new_i = ((i as isize + x_shift).rem_euclid(rows as isize)) as usize;
// let new_j = ((j as isize + y_shift).rem_euclid(cols as isize)) as usize;
// rolled[(new_i, new_j)] = arr[(i, j)];
// }
// }
// rolled
// }
//
// #[expect(clippy::similar_names)]
// pub fn find_edges(tiles: &[Tile]) -> UtilesResult<Vec<Tile>> {
// let zbox = TileZBox::from_tiles(tiles)?;
// // make 2D burn array
// let burn = burn_tiles(tiles, zbox);
//
// // rolled arrays w/o adding an extra axis
// let stacks: Vec<Array2<bool>> = IDXS
// .iter()
// .map(|(dx, dy)| roll_2d(&burn, *dx, *dy))
// .collect();
// // stack along axis2, which should be 3d arr
// let stacked = stack(
// Axis(2),
// &stacks.iter().map(|a| a.view()).collect::<Vec<_>>(),
// )
// .map_err(UtilesError::NdarrayShapeError)?;
//
// // edges
// let min_array =
// stacked.map_axis(Axis(2), |view| *view.iter().min().unwrap_or(&false));
// // xor the 2 arrs
// let xys_edge = &burn & !&min_array;
//
// // collect the edge tiles
// let uxmin = (zbox.minx() - 1) as usize;
// let uymin = (zbox.miny() - 1) as usize;
//
// // v1 of weird itering
// // ==========================================
// // let tiles = xys_edge.indexed_iter().map(
// // |((i, j), is_edge)| {
// // if *is_edge{
// // let tile = Tile::new(
// // (i + uxmin) as u32,
// // (j + uymin) as u32,
// // zoom,
// // );
// // Some(
// // tile
// // )
// // }else{
// // None
// // }
// //
// // }
// //
// // ).flatten().collect::<Vec<Tile>>();
// // ==========================================
// // more sane version:
//
// let tiles = xys_edge
// .indexed_iter()
// .filter(|(_, &is_edge)| is_edge)
// .map(|((i, j), _)| Tile::new((i + uxmin) as u32, (j + uymin) as u32, zbox.zoom))
// .collect::<Vec<Tile>>();
//
// Ok(tiles)
// }