use std::collections::HashMap;
use rayon::prelude::*;
pub use super::level::{Crs, METERS_PER_DEGREE};
pub use super::simplify::Representation;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FeatureKind {
Point,
Line,
Polygon,
}
impl FeatureKind {
#[inline]
fn discriminant(self) -> u8 {
match self {
FeatureKind::Point => 0,
FeatureKind::Line => 1,
FeatureKind::Polygon => 2,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SortDirection {
Desc,
Asc,
}
#[derive(Debug, Clone, Copy)]
pub struct AssignFeature {
pub index: usize,
pub bbox: [f64; 4],
pub kind: FeatureKind,
pub sort_key: Option<f64>,
pub entry_level: Option<u8>,
}
impl AssignFeature {
#[inline]
pub(super) fn center(&self) -> (f64, f64) {
let [xmin, ymin, xmax, ymax] = self.bbox;
((xmin + xmax) * 0.5, (ymin + ymax) * 0.5)
}
#[inline]
fn diag_sq(&self) -> f64 {
let [xmin, ymin, xmax, ymax] = self.bbox;
let dx = xmax - xmin;
let dy = ymax - ymin;
dx * dx + dy * dy
}
}
#[derive(Debug, Clone, Copy)]
pub struct AssignConfig {
pub point_thinning: f64,
pub line_thinning: f64,
pub polygon_thinning: f64,
pub line_visibility: f64,
pub polygon_visibility: f64,
pub sort_direction: SortDirection,
}
pub const CLUSTER_POINT_THINNING_DEFAULT: f64 = 16.0;
impl Default for AssignConfig {
fn default() -> Self {
Self {
point_thinning: 4.0,
line_thinning: 1.0,
polygon_thinning: 1.0,
line_visibility: 2.0,
polygon_visibility: 2.0,
sort_direction: SortDirection::Desc,
}
}
}
impl AssignConfig {
#[inline]
fn thinning_factor(&self, kind: FeatureKind) -> f64 {
match kind {
FeatureKind::Point => self.point_thinning,
FeatureKind::Line => self.line_thinning,
FeatureKind::Polygon => self.polygon_thinning,
}
}
#[inline]
fn visibility_factor(&self, kind: FeatureKind) -> f64 {
match kind {
FeatureKind::Point => 0.0,
FeatureKind::Line => self.line_visibility,
FeatureKind::Polygon => self.polygon_visibility,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FeatureAssignment {
pub index: usize,
pub min_level: u8,
}
#[derive(Debug, Clone)]
pub struct Assignment {
pub assignments: Vec<FeatureAssignment>,
pub num_levels: u8,
}
impl Assignment {
pub fn duplicating_at_level(&self, level: u8) -> Vec<usize> {
self.assignments
.iter()
.filter(|a| a.min_level <= level)
.map(|a| a.index)
.collect()
}
pub fn partitioning_at_level(&self, level: u8) -> Vec<usize> {
self.assignments
.iter()
.filter(|a| a.min_level == level)
.map(|a| a.index)
.collect()
}
}
#[inline]
pub fn gsd_to_coord_units(gsd_meters: f64, crs: Crs) -> f64 {
crs.meters_to_units(gsd_meters)
}
#[inline]
fn stable_hash(index: usize) -> u64 {
let mut x = index as u64;
x ^= x >> 33;
x = x.wrapping_mul(0xff51afd7ed558ccd);
x ^= x >> 33;
x = x.wrapping_mul(0xc4ceb9fe1a85ec53);
x ^= x >> 33;
x
}
type CellKey = (u8, i64, i64);
#[derive(Debug, Clone, Copy)]
pub(super) struct Priority {
sort_rank: Option<f64>,
diag_sq: f64,
hash: u64,
index: usize,
}
impl Priority {
pub(super) fn new(feat: &AssignFeature, dir: SortDirection) -> Self {
let sort_rank = feat.sort_key.map(|k| match dir {
SortDirection::Desc => k,
SortDirection::Asc => -k,
});
Priority {
sort_rank,
diag_sq: feat.diag_sq(),
hash: stable_hash(feat.index),
index: feat.index,
}
}
pub(super) fn beats(&self, other: &Priority) -> bool {
match (self.sort_rank, other.sort_rank) {
(Some(a), Some(b)) => {
if a != b {
return a > b;
}
}
(Some(_), None) => return true,
(None, Some(_)) => return false,
(None, None) => {}
}
if self.diag_sq != other.diag_sq {
return self.diag_sq > other.diag_sq;
}
if self.hash != other.hash {
return self.hash > other.hash;
}
self.index < other.index
}
}
fn level_winner_positions(
features: &[AssignFeature],
config: &AssignConfig,
gsd_units: f64,
repr: Representation,
) -> Vec<usize> {
let mut grid: HashMap<CellKey, usize> = HashMap::new();
let mut winners: Vec<usize> = Vec::new();
for (pos, feat) in features.iter().enumerate() {
let effective_kind = match repr {
Representation::Point if feat.kind == FeatureKind::Polygon => FeatureKind::Point,
_ => feat.kind,
};
if feat.entry_level.is_some() {
continue;
}
let vis = if repr == Representation::Square && feat.kind == FeatureKind::Polygon {
0.0
} else {
config.visibility_factor(effective_kind)
};
if vis > 0.0 {
let gate = vis * gsd_units;
if feat.diag_sq() < gate * gate {
continue; }
}
if config.thinning_factor(effective_kind) == 0.0 {
winners.push(pos);
continue;
}
let cell_size = gsd_units * config.thinning_factor(effective_kind);
if cell_size <= 0.0 || cell_size.is_nan() {
continue;
}
let (cx, cy) = feat.center();
let key: CellKey = (
feat.kind.discriminant(),
(cx / cell_size).floor() as i64,
(cy / cell_size).floor() as i64,
);
grid.entry(key)
.and_modify(|slot| {
let challenger = Priority::new(feat, config.sort_direction);
let incumbent = Priority::new(&features[*slot], config.sort_direction);
if challenger.beats(&incumbent) {
*slot = pos;
}
})
.or_insert(pos);
}
winners.extend(grid.into_values());
winners
}
const GRID_ENTRY_EST_BYTES: u64 = 96;
fn center_extent_and_kind_counts(features: &[AssignFeature]) -> ((f64, f64), [usize; 3]) {
let mut min_x = f64::INFINITY;
let mut min_y = f64::INFINITY;
let mut max_x = f64::NEG_INFINITY;
let mut max_y = f64::NEG_INFINITY;
let mut counts = [0usize; 3];
for feat in features {
let (cx, cy) = feat.center();
min_x = min_x.min(cx);
min_y = min_y.min(cy);
max_x = max_x.max(cx);
max_y = max_y.max(cy);
counts[feat.kind.discriminant() as usize] += 1;
}
if features.is_empty() {
return ((0.0, 0.0), counts);
}
((max_x - min_x, max_y - min_y), counts)
}
fn estimate_level_grid_bytes(
config: &AssignConfig,
gsd_units: f64,
extent: (f64, f64),
kind_counts: &[usize; 3],
) -> u64 {
let kinds = [FeatureKind::Point, FeatureKind::Line, FeatureKind::Polygon];
let mut entries: u64 = 0;
for kind in kinds {
let count = kind_counts[kind.discriminant() as usize];
if count == 0 {
continue;
}
if config.thinning_factor(kind) == 0.0 {
entries = entries.saturating_add(count as u64);
continue;
}
let cell = gsd_units * config.thinning_factor(kind);
if cell <= 0.0 || cell.is_nan() {
continue; }
let cells = ((extent.0 / cell).floor() + 1.0) * ((extent.1 / cell).floor() + 1.0);
let capped = if cells.is_finite() && cells < count as f64 {
cells as u64
} else {
count as u64
};
entries = entries.saturating_add(capped);
}
entries.saturating_mul(GRID_ENTRY_EST_BYTES)
}
fn plan_level_waves(estimates: &[u64], budget_bytes: u64) -> Vec<std::ops::Range<usize>> {
let mut waves = Vec::new();
let mut start = 0usize;
let mut sum = 0u64;
for (i, &est) in estimates.iter().enumerate() {
if i > start && sum.saturating_add(est) > budget_bytes {
waves.push(start..i);
start = i;
sum = 0;
}
sum = sum.saturating_add(est);
}
if start < estimates.len() {
waves.push(start..estimates.len());
}
waves
}
pub fn assign_levels(
features: &[AssignFeature],
level_gsds: &[f64],
config: &AssignConfig,
crs: Crs,
) -> Assignment {
assign_levels_bounded(features, level_gsds, config, crs, u64::MAX, &[])
}
pub fn assign_levels_banded(
features: &[AssignFeature],
level_gsds: &[f64],
config: &AssignConfig,
crs: Crs,
level_reprs: &[Representation],
) -> Assignment {
assign_levels_bounded(features, level_gsds, config, crs, u64::MAX, level_reprs)
}
pub fn assign_levels_bounded(
features: &[AssignFeature],
level_gsds: &[f64],
config: &AssignConfig,
crs: Crs,
grid_budget_bytes: u64,
level_reprs: &[Representation],
) -> Assignment {
let num_levels_usize = level_gsds.len();
let num_levels = num_levels_usize.min(u8::MAX as usize) as u8;
if num_levels_usize == 0 {
return Assignment {
assignments: features
.iter()
.map(|f| FeatureAssignment {
index: f.index,
min_level: 0,
})
.collect(),
num_levels: 0,
};
}
let finest = num_levels - 1;
let mut min_levels: Vec<u8> = vec![finest; features.len()];
let laddered = features.iter().filter(|f| f.entry_level.is_some()).count();
if laddered > 0 {
for (pos, feat) in features.iter().enumerate() {
if let Some(entry) = feat.entry_level {
min_levels[pos] = entry.min(finest);
}
}
log::info!(
"[assign] entry-zoom ladder (#364): {laddered} of {} feature(s) take an \
attribute-driven level, exempt from visibility gates and thinning",
features.len()
);
}
let gsd_units: Vec<f64> = level_gsds[..finest as usize]
.iter()
.map(|&g| gsd_to_coord_units(g, crs))
.collect();
let (extent, kind_counts) = center_extent_and_kind_counts(features);
let estimates: Vec<u64> = gsd_units
.iter()
.map(|&g| estimate_level_grid_bytes(config, g, extent, &kind_counts))
.collect();
let waves = plan_level_waves(&estimates, grid_budget_bytes);
if waves.len() > 1 {
let total_mib = estimates.iter().copied().sum::<u64>() / (1024 * 1024);
let budget_mib = grid_budget_bytes / (1024 * 1024);
log::info!(
"[assign] winner grids est {total_mib} MiB exceed the {budget_mib} MiB \
budget: building {} coarse level(s) in {} memory-bounded wave(s) (#306)",
estimates.len(),
waves.len()
);
}
for wave in waves {
let winners_per_level: Vec<Vec<usize>> = wave
.clone()
.into_par_iter()
.map(|level_idx| {
let repr = level_reprs.get(level_idx).copied().unwrap_or_default();
level_winner_positions(features, config, gsd_units[level_idx], repr)
})
.collect();
for (offset, winners) in winners_per_level.iter().enumerate() {
let level = (wave.start + offset) as u8;
for &pos in winners {
if level < min_levels[pos] {
min_levels[pos] = level;
}
}
}
}
let assignments = features
.iter()
.zip(min_levels)
.map(|(f, min_level)| FeatureAssignment {
index: f.index,
min_level,
})
.collect();
Assignment {
assignments,
num_levels,
}
}
use std::cmp::Ordering;
pub const SUPERCELL_GSD_FACTOR: f64 = 128.0;
pub const MIN_DENSITY_LEVEL_FEATURES: usize = 256;
#[derive(Debug, Clone, Copy)]
pub struct DensityBudgetConfig {
pub enabled: bool,
pub drop_rate: f64,
pub gamma: f64,
}
impl Default for DensityBudgetConfig {
fn default() -> Self {
Self {
enabled: true,
drop_rate: 1.65,
gamma: 1.5,
}
}
}
pub fn apply_density_budget(
assignment: &Assignment,
features: &[AssignFeature],
level_gsds: &[f64],
config: &AssignConfig,
budget: &DensityBudgetConfig,
crs: Crs,
) -> Assignment {
let num_levels = assignment.num_levels as usize;
if !budget.enabled
|| budget.drop_rate <= 1.0
|| budget.drop_rate.is_nan()
|| features.is_empty()
|| num_levels < 2
{
return assignment.clone();
}
let n = features.len();
let finest = num_levels - 1;
let cw_min: Vec<usize> = assignment
.assignments
.iter()
.map(|a| a.min_level as usize)
.collect();
let prio: Vec<Priority> = features
.iter()
.map(|f| Priority::new(f, config.sort_direction))
.collect();
let keep_frac = 1.0 / budget.drop_rate;
let total = n as f64;
let effective_budget = |level: usize| -> usize {
if level >= finest {
return n; }
let raw = total * keep_frac.powi((finest - level) as i32);
(raw.round() as usize).max(MIN_DENSITY_LEVEL_FEATURES)
};
let mut admitted = vec![false; n];
let mut admitted_at = vec![finest as u8; n];
let mut kept_count = 0usize;
#[allow(clippy::needless_range_loop)]
for level in 0..num_levels {
let cands: Vec<usize> = (0..n)
.filter(|&i| !admitted[i] && cw_min[i] <= level)
.collect();
if cands.is_empty() {
continue;
}
let budget_l = effective_budget(level);
if level == finest || kept_count + cands.len() <= budget_l {
for &i in &cands {
admitted[i] = true;
admitted_at[i] = level as u8;
}
kept_count += cands.len();
continue;
}
let available = budget_l.saturating_sub(kept_count);
if available == 0 {
continue;
}
let chosen = select_budget_survivors(
&cands,
available,
features,
&prio,
level_gsds[level],
crs,
budget.gamma,
);
for &i in &chosen {
admitted[i] = true;
admitted_at[i] = level as u8;
}
kept_count += chosen.len();
}
Assignment {
assignments: features
.iter()
.zip(admitted_at)
.map(|(f, min_level)| FeatureAssignment {
index: f.index,
min_level,
})
.collect(),
num_levels: assignment.num_levels,
}
}
#[inline]
fn priority_order(prio: &[Priority], a: usize, b: usize) -> Ordering {
if a == b {
Ordering::Equal
} else if prio[a].beats(&prio[b]) {
Ordering::Less
} else {
Ordering::Greater
}
}
pub(super) fn select_budget_survivors(
cands: &[usize],
available: usize,
features: &[AssignFeature],
prio: &[Priority],
gsd_m: f64,
crs: Crs,
gamma: f64,
) -> Vec<usize> {
let gsd_units = gsd_to_coord_units(gsd_m, crs);
let super_size = gsd_units * SUPERCELL_GSD_FACTOR;
if super_size <= 0.0 || super_size.is_nan() {
let mut all = cands.to_vec();
all.sort_by(|&a, &b| priority_order(prio, a, b));
all.truncate(available);
return all;
}
let mut cells: HashMap<(i64, i64), Vec<usize>> = HashMap::new();
for &i in cands {
let (cx, cy) = features[i].center();
let key = (
(cx / super_size).floor() as i64,
(cy / super_size).floor() as i64,
);
cells.entry(key).or_default().push(i);
}
for members in cells.values_mut() {
members.sort_by(|&a, &b| priority_order(prio, a, b));
}
let mut keys: Vec<(i64, i64)> = cells.keys().copied().collect();
keys.sort_unstable();
let pops: Vec<usize> = keys.iter().map(|k| cells[k].len()).collect();
let alpha = 1.0 / gamma.max(1.0);
let allocs = water_fill(&pops, available, alpha);
let mut chosen = Vec::with_capacity(available);
for (k, a) in keys.iter().zip(allocs) {
chosen.extend(cells[k].iter().take(a).copied());
}
chosen
}
fn water_fill(pops: &[usize], budget: usize, alpha: f64) -> Vec<usize> {
let n = pops.len();
let total_pop: usize = pops.iter().sum();
if budget >= total_pop {
return pops.to_vec();
}
let mut alloc = vec![0usize; n];
let mut capped = vec![false; n];
let mut remaining = budget;
loop {
let sum_w: f64 = (0..n)
.filter(|&i| !capped[i])
.map(|i| (pops[i] as f64).powf(alpha))
.sum();
if sum_w <= 0.0 || remaining == 0 {
break;
}
let mut newly_capped = false;
for i in 0..n {
if capped[i] {
continue;
}
let raw = remaining as f64 * (pops[i] as f64).powf(alpha) / sum_w;
if raw >= pops[i] as f64 {
alloc[i] = pops[i];
capped[i] = true;
newly_capped = true;
}
}
if newly_capped {
let capped_sum: usize = (0..n).filter(|&i| capped[i]).map(|i| alloc[i]).sum();
remaining = budget.saturating_sub(capped_sum);
continue;
}
let sum_w2 = sum_w;
let mut leftover = remaining;
let mut fracs: Vec<(usize, f64)> = Vec::new();
for i in 0..n {
if capped[i] {
continue;
}
let raw = remaining as f64 * (pops[i] as f64).powf(alpha) / sum_w2;
let floor = raw.floor();
alloc[i] = floor as usize;
leftover = leftover.saturating_sub(floor as usize);
fracs.push((i, raw - floor));
}
fracs.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(Ordering::Equal));
for &(i, _) in &fracs {
if leftover == 0 {
break;
}
if alloc[i] < pops[i] {
alloc[i] += 1;
leftover -= 1;
}
}
break;
}
alloc
}
#[cfg(test)]
mod tests {
use super::*;
fn gsd(z: u32) -> f64 {
40_075_016.69 / 1024.0 / 2f64.powi(z as i32)
}
fn point(index: usize, x: f64, y: f64) -> AssignFeature {
AssignFeature {
index,
bbox: [x, y, x, y],
kind: FeatureKind::Point,
sort_key: None,
entry_level: None,
}
}
fn poly(index: usize, xmin: f64, ymin: f64, xmax: f64, ymax: f64) -> AssignFeature {
AssignFeature {
index,
bbox: [xmin, ymin, xmax, ymax],
kind: FeatureKind::Polygon,
sort_key: None,
entry_level: None,
}
}
#[test]
fn ladder_admits_a_tiny_feature_the_visibility_gate_would_drop() {
let gsds = [gsd(2), gsd(4), gsd(6)];
let cfg = AssignConfig {
polygon_visibility: 2.0, ..Default::default()
};
let mut core = poly(0, 0.0, 0.0, 0.00005, 0.00005);
let ring = poly(1, -2.0, -2.0, 2.0, 2.0);
let control = assign_levels(&[core, ring], &gsds, &cfg, Crs::Epsg4326);
assert_eq!(
control.assignments[0].min_level, 2,
"without a ladder the tiny core is gated out of every coarse level"
);
core.entry_level = Some(0);
let laddered = assign_levels(&[core, ring], &gsds, &cfg, Crs::Epsg4326);
assert_eq!(
laddered.assignments[0].min_level, 0,
"an entry level must override the visibility gate"
);
}
#[test]
fn ladder_holds_back_a_large_feature_that_would_otherwise_win_early() {
let gsds = [gsd(2), gsd(4), gsd(6)];
let cfg = AssignConfig::default();
let mut ring = poly(0, -2.0, -2.0, 2.0, 2.0);
let control = assign_levels(&[ring], &gsds, &cfg, Crs::Epsg4326);
assert_eq!(control.assignments[0].min_level, 0, "large: wins level 0");
ring.entry_level = Some(1);
let laddered = assign_levels(&[ring], &gsds, &cfg, Crs::Epsg4326);
assert_eq!(
laddered.assignments[0].min_level, 1,
"an entry level must also delay a feature the grid would admit"
);
}
#[test]
fn ladder_features_do_not_consume_cells_from_competitors() {
let gsds = [gsd(2), gsd(6)];
let cfg = AssignConfig::default();
let mut a = point(0, 10.0, 10.0);
a.sort_key = Some(100.0);
let mut b = point(1, 10.0001, 10.0001);
b.sort_key = Some(1.0);
let control = assign_levels(&[a, b], &gsds, &cfg, Crs::Epsg4326);
assert_eq!(control.assignments[0].min_level, 0);
assert_eq!(control.assignments[1].min_level, 1, "b loses the cell");
a.entry_level = Some(0);
let laddered = assign_levels(&[a, b], &gsds, &cfg, Crs::Epsg4326);
assert_eq!(laddered.assignments[0].min_level, 0, "a: by ladder");
assert_eq!(
laddered.assignments[1].min_level, 0,
"b should now win the cell a vacated"
);
}
#[test]
fn features_without_an_entry_level_keep_the_ordinary_behaviour() {
let gsds = [gsd(2), gsd(4), gsd(6)];
let cfg = AssignConfig::default();
let big = poly(0, -2.0, -2.0, 2.0, 2.0);
let mut laddered = poly(1, 40.0, 40.0, 44.0, 44.0);
laddered.entry_level = Some(1);
let out = assign_levels(&[big, laddered], &gsds, &cfg, Crs::Epsg4326);
assert_eq!(
out.assignments[0].min_level, 0,
"the unlabelled feature is unaffected by another feature's ladder"
);
assert_eq!(out.assignments[1].min_level, 1);
}
#[test]
fn entry_level_beyond_the_finest_clamps() {
let gsds = [gsd(2), gsd(4)];
let cfg = AssignConfig::default();
let mut far = point(0, 5.0, 5.0);
far.entry_level = Some(200);
let mut near = point(1, 6.0, 6.0);
near.entry_level = Some(0);
let both = assign_levels(&[far, near], &gsds, &cfg, Crs::Epsg4326);
assert_eq!(
both.assignments[1].min_level, 0,
"an in-range entry level is honoured, so the value is read"
);
assert_eq!(both.assignments[0].min_level, 1, "200 clamps to the finest");
let mut p = point(0, 5.0, 5.0);
p.entry_level = Some(200);
let out = assign_levels(&[p], &gsds, &cfg, Crs::Epsg4326);
assert_eq!(
out.assignments[0].min_level, 1,
"clamped to the finest level"
);
}
#[test]
fn ladder_assignment_is_identical_under_every_grid_budget() {
let gsds = [gsd(2), gsd(4), gsd(6)];
let cfg = AssignConfig::default();
let feats: Vec<AssignFeature> = (0..40)
.map(|i| {
let mut f = poly(i, i as f64 * 0.5, 0.0, i as f64 * 0.5 + 0.2, 0.2);
if i % 3 == 0 {
f.entry_level = Some((i % 5) as u8);
}
f
})
.collect();
let unbounded = assign_levels_bounded(&feats, &gsds, &cfg, Crs::Epsg4326, u64::MAX, &[]);
let one_wave = assign_levels_bounded(&feats, &gsds, &cfg, Crs::Epsg4326, 1, &[]);
assert_eq!(
unbounded.assignments, one_wave.assignments,
"the wave plan is scheduling only; it must not move a feature"
);
}
#[test]
fn empty_input_yields_empty_assignments() {
let out = assign_levels(
&[],
&[gsd(4), gsd(6)],
&AssignConfig::default(),
Crs::Epsg3857,
);
assert!(out.assignments.is_empty());
assert_eq!(out.num_levels, 2);
assert!(out.duplicating_at_level(0).is_empty());
assert!(out.partitioning_at_level(1).is_empty());
}
#[test]
fn single_cell_one_winner_larger_polygon() {
let big = poly(0, 0.0, 0.0, 100_000.0, 100_000.0);
let small = poly(1, 1000.0, 1000.0, 2000.0, 2000.0);
let gsds = [gsd(2), gsd(6)];
let out = assign_levels(
&[big, small],
&gsds,
&AssignConfig::default(),
Crs::Epsg3857,
);
assert_eq!(out.assignments[0].min_level, 0, "big polygon wins coarse");
assert!(out.assignments[1].min_level > 0);
}
#[test]
fn winner_is_deterministic_across_runs() {
let a = poly(7, 0.0, 0.0, 50_000.0, 50_000.0);
let b = poly(42, 100.0, 100.0, 50_100.0, 50_100.0);
let gsds = [gsd(2), gsd(8)];
let cfg = AssignConfig::default();
let out1 = assign_levels(&[a, b], &gsds, &cfg, Crs::Epsg3857);
let out2 = assign_levels(&[b, a], &gsds, &cfg, Crs::Epsg3857);
let w1: Vec<usize> = out1
.assignments
.iter()
.filter(|x| x.min_level == 0)
.map(|x| x.index)
.collect();
let w2: Vec<usize> = out2
.assignments
.iter()
.filter(|x| x.min_level == 0)
.map(|x| x.index)
.collect();
assert_eq!(w1, w2, "winner must not depend on input order");
assert_eq!(w1.len(), 1, "exactly one winner in the shared cell");
}
#[test]
fn all_features_one_cell_single_winner_at_coarsest() {
let feats: Vec<AssignFeature> = (0..20)
.map(|i| poly(i, 0.0, 0.0, 40_000.0, 40_000.0))
.collect();
let gsds = [gsd(2), gsd(10)];
let out = assign_levels(&feats, &gsds, &AssignConfig::default(), Crs::Epsg3857);
let at0 = out.duplicating_at_level(0);
assert_eq!(at0.len(), 1, "one winner at coarsest level");
}
#[test]
fn parallel_grid_merge_is_deterministic_at_scale() {
let mut feats: Vec<AssignFeature> = Vec::new();
let mut idx = 0usize;
for c in 0..50 {
let base = c as f64 * 5_000.0;
for k in 0..400 {
let span = 200.0 + (k as f64) * 50.0;
feats.push(poly(idx, base, base, base + span, base + span));
idx += 1;
}
}
let gsds = [gsd(4), gsd(8), gsd(12), gsd(14)];
let cfg = AssignConfig::default();
let baseline = assign_levels(&feats, &gsds, &cfg, Crs::Epsg3857);
let levels_of = |a: &Assignment| -> Vec<(usize, u8)> {
let mut v: Vec<(usize, u8)> = a
.assignments
.iter()
.map(|x| (x.index, x.min_level))
.collect();
v.sort_unstable();
v
};
let want = levels_of(&baseline);
for _ in 0..8 {
let got = assign_levels(&feats, &gsds, &cfg, Crs::Epsg3857);
assert_eq!(levels_of(&got), want, "assignment unstable across runs");
}
let mut rev = feats.clone();
rev.reverse();
assert_eq!(
levels_of(&assign_levels(&rev, &gsds, &cfg, Crs::Epsg3857)),
want,
"assignment must not depend on input order",
);
}
#[test]
fn points_always_eligible() {
let p = point(0, 0.0, 0.0);
let gsds = [gsd(2), gsd(6)];
let out = assign_levels(&[p], &gsds, &AssignConfig::default(), Crs::Epsg3857);
assert_eq!(out.assignments[0].min_level, 0);
}
#[test]
fn small_polygon_gated_until_fine_level() {
let g6 = gsd(6);
let side = 4.5 * g6 / std::f64::consts::SQRT_2; let small = poly(0, 0.0, 0.0, side, side);
let gsds = [gsd(2), gsd(4), gsd(6)];
let out = assign_levels(&[small], &gsds, &AssignConfig::default(), Crs::Epsg3857);
assert_eq!(
out.assignments[0].min_level, 2,
"polygon only visible at finest level"
);
}
#[test]
fn per_kind_visibility_gates_are_independent() {
let g4 = gsd(4);
let side = 3.0 * g4 / std::f64::consts::SQRT_2; let mut line = poly(0, 0.0, 0.0, side, side);
line.kind = FeatureKind::Line;
let polygon = poly(1, 1e7, 1e7, 1e7 + side, 1e7 + side); let gsds = [gsd(4), gsd(8)];
let cfg = AssignConfig {
polygon_visibility: 4.0,
..AssignConfig::default()
};
let out = assign_levels(&[line, polygon], &gsds, &cfg, Crs::Epsg3857);
assert_eq!(out.assignments[0].min_level, 0, "line visible at level 0");
assert!(out.assignments[1].min_level > 0, "polygon gated at level 0");
}
#[test]
fn default_visibility_gates() {
let cfg = AssignConfig::default();
assert_eq!(cfg.polygon_visibility, 2.0);
assert_eq!(cfg.line_visibility, 2.0);
assert_eq!(cfg.visibility_factor(FeatureKind::Point), 0.0);
}
#[test]
fn sort_key_beats_size_and_null_loses() {
let big_null = poly(0, 0.0, 0.0, 80_000.0, 80_000.0);
let mut small_key = poly(1, 10_000.0, 10_000.0, 70_000.0, 70_000.0);
small_key.sort_key = Some(5.0);
let gsds = [gsd(2), gsd(8)];
let out = assign_levels(
&[big_null, small_key],
&gsds,
&AssignConfig::default(),
Crs::Epsg3857,
);
assert!(
out.assignments[0].min_level > 0,
"null sort_key loses despite larger size"
);
assert_eq!(out.assignments[1].min_level, 0, "sort_key holder wins");
}
#[test]
fn sort_key_direction_desc_vs_asc() {
let a = {
let mut f = poly(0, 0.0, 0.0, 40_000.0, 40_000.0);
f.sort_key = Some(1.0);
f
};
let b = {
let mut f = poly(1, 100.0, 100.0, 40_100.0, 40_100.0);
f.sort_key = Some(9.0);
f
};
let gsds = [gsd(2), gsd(8)];
let desc = AssignConfig {
sort_direction: SortDirection::Desc,
..Default::default()
};
let out_desc = assign_levels(&[a, b], &gsds, &desc, Crs::Epsg3857);
assert_eq!(
out_desc.assignments[1].min_level, 0,
"desc: larger key wins"
);
let asc = AssignConfig {
sort_direction: SortDirection::Asc,
..Default::default()
};
let out_asc = assign_levels(&[a, b], &gsds, &asc, Crs::Epsg3857);
assert_eq!(out_asc.assignments[0].min_level, 0, "asc: smaller key wins");
}
#[test]
fn duplicating_vs_partitioning_expansion() {
let out = Assignment {
assignments: vec![
FeatureAssignment {
index: 10,
min_level: 0,
},
FeatureAssignment {
index: 11,
min_level: 1,
},
FeatureAssignment {
index: 12,
min_level: 2,
},
],
num_levels: 3,
};
assert_eq!(out.duplicating_at_level(0), vec![10]);
assert_eq!(out.duplicating_at_level(1), vec![10, 11]);
assert_eq!(out.duplicating_at_level(2), vec![10, 11, 12]);
assert_eq!(out.partitioning_at_level(0), vec![10]);
assert_eq!(out.partitioning_at_level(1), vec![11]);
assert_eq!(out.partitioning_at_level(2), vec![12]);
}
#[test]
fn every_feature_present_at_finest_level_duplicating() {
let feats: Vec<AssignFeature> = (0..15)
.map(|i| poly(i, 0.0, 0.0, 30_000.0, 30_000.0))
.collect();
let gsds = [gsd(2), gsd(4), gsd(6)];
let out = assign_levels(&feats, &gsds, &AssignConfig::default(), Crs::Epsg3857);
let finest = out.num_levels - 1;
assert_eq!(
out.duplicating_at_level(finest).len(),
feats.len(),
"all features present at finest level"
);
}
#[test]
fn crs_affects_grid_sizing() {
let p0 = point(0, 0.0, 0.0);
let p1 = point(1, 0.05, 0.0);
let gsds = [gsd(6), gsd(8)];
let cfg = AssignConfig::default();
let out_4326 = assign_levels(&[p0, p1], &gsds, &cfg, Crs::Epsg4326);
assert_eq!(
out_4326.duplicating_at_level(0).len(),
2,
"4326: points separated into two cells"
);
let out_3857 = assign_levels(&[p0, p1], &gsds, &cfg, Crs::Epsg3857);
assert_eq!(
out_3857.duplicating_at_level(0).len(),
1,
"3857: points collide into one cell"
);
}
#[test]
fn gsd_to_coord_units_conversion() {
assert_eq!(gsd_to_coord_units(1000.0, Crs::Epsg3857), 1000.0);
assert!((gsd_to_coord_units(111_320.0, Crs::Epsg4326) - 1.0).abs() < 1e-9);
}
fn all_at_level_zero(n: usize, num_levels: u8) -> Assignment {
Assignment {
assignments: (0..n)
.map(|i| FeatureAssignment {
index: i,
min_level: 0,
})
.collect(),
num_levels,
}
}
fn budget_cfg(enabled: bool, drop_rate: f64, gamma: f64) -> DensityBudgetConfig {
DensityBudgetConfig {
enabled,
drop_rate,
gamma,
}
}
#[test]
fn budget_geometric_decay_shape() {
let n = 16_384usize;
let num_levels = 6u8;
let finest = (num_levels - 1) as usize;
let feats: Vec<AssignFeature> = (0..n)
.map(|i| {
let x = (i % 128) as f64 * 5000.0;
let y = (i / 128) as f64 * 5000.0;
point(i, x, y)
})
.collect();
let gsds: Vec<f64> = (0..num_levels).map(|z| gsd(z as u32)).collect();
let base = all_at_level_zero(n, num_levels);
let out = apply_density_budget(
&base,
&feats,
&gsds,
&AssignConfig::default(),
&budget_cfg(true, 2.0, 1.0),
Crs::Epsg3857,
);
let counts: Vec<usize> = (0..num_levels)
.map(|l| out.duplicating_at_level(l).len())
.collect();
assert_eq!(counts[finest], n, "canonical must keep all features");
for w in counts.windows(2) {
let ratio = w[1] as f64 / w[0] as f64;
assert!(
(ratio - 2.0).abs() < 0.12,
"geometric decay violated: counts={counts:?} ratio={ratio}"
);
}
}
#[test]
fn budget_keeps_high_priority_survivors() {
let n = 1024usize;
let high = 256usize;
let feats: Vec<AssignFeature> = (0..n)
.map(|i| {
let mut f = point(i, i as f64 * 100.0, 0.0); if i < high {
f.sort_key = Some(100.0);
}
f
})
.collect();
let gsds = [gsd(0), gsd(2)];
let base = all_at_level_zero(n, 2);
let out = apply_density_budget(
&base,
&feats,
&gsds,
&AssignConfig::default(),
&budget_cfg(true, 2.0, 1.5),
Crs::Epsg3857,
);
assert_eq!(out.duplicating_at_level(0).len(), 512, "level-0 budget");
let high_at_0 = out
.assignments
.iter()
.filter(|a| a.index < high && a.min_level == 0)
.count();
assert_eq!(
high_at_0, high,
"all high-priority features survive the cut"
);
}
#[test]
fn budget_spatial_fairness_protects_sparse() {
let a_n = 64usize;
let b_n = 576usize;
let n = a_n + b_n;
let mut feats: Vec<AssignFeature> = Vec::with_capacity(n);
for i in 0..a_n {
feats.push(point(i, i as f64 * 1000.0, 0.0)); }
for j in 0..b_n {
feats.push(point(a_n + j, 10_000_000.0 + j as f64 * 1000.0, 0.0)); }
let gsds = [gsd(0), gsd(2)];
let base = all_at_level_zero(n, 2);
let out = apply_density_budget(
&base,
&feats,
&gsds,
&AssignConfig::default(),
&budget_cfg(true, 2.0, 2.0),
Crs::Epsg3857,
);
let a_kept = out
.assignments
.iter()
.filter(|a| a.index < a_n && a.min_level == 0)
.count();
let mut order: Vec<usize> = (0..n).collect();
let prio: Vec<Priority> = feats
.iter()
.map(|f| Priority::new(f, SortDirection::Desc))
.collect();
order.sort_by(|&a, &b| priority_order(&prio, a, b));
let global_a = order.iter().take(320).filter(|&&i| i < a_n).count();
assert_eq!(a_kept, a_n, "fairness keeps the entire sparse cluster");
assert!(
a_kept > global_a,
"sparse cluster retains more than a global cut: fair={a_kept} global={global_a}"
);
}
#[test]
fn budget_canonical_never_dropped() {
let n = 4000usize;
let num_levels = 5u8;
let finest = num_levels - 1;
let feats: Vec<AssignFeature> = (0..n)
.map(|i| point(i, (i % 64) as f64 * 200.0, (i / 64) as f64 * 200.0))
.collect();
let gsds: Vec<f64> = (0..num_levels).map(|z| gsd(z as u32)).collect();
let base = all_at_level_zero(n, num_levels);
let out = apply_density_budget(
&base,
&feats,
&gsds,
&AssignConfig::default(),
&DensityBudgetConfig::default(),
Crs::Epsg3857,
);
assert_eq!(
out.duplicating_at_level(finest).len(),
n,
"canonical level must contain every feature"
);
}
#[test]
fn budget_disabled_is_identity() {
let feats: Vec<AssignFeature> = (0..500)
.map(|i| point(i, (i % 20) as f64 * 100.0, (i / 20) as f64 * 100.0))
.collect();
let gsds = [gsd(2), gsd(4), gsd(6)];
let cw = assign_levels(&feats, &gsds, &AssignConfig::default(), Crs::Epsg3857);
let out = apply_density_budget(
&cw,
&feats,
&gsds,
&AssignConfig::default(),
&budget_cfg(false, 2.0, 1.5),
Crs::Epsg3857,
);
assert_eq!(
cw.assignments, out.assignments,
"disabled budget must be an identity"
);
}
#[test]
fn antimeridian_inflated_bbox_assigned_to_coarsest_level() {
let inflated = poly(0, -179.95, -0.05, 179.95, 0.05);
let true_extent = poly(1, 179.95, -0.05, 180.05, 0.05);
let gsds = [gsd(2), gsd(6)];
let out = assign_levels(
&[inflated, true_extent],
&gsds,
&AssignConfig::default(),
Crs::Epsg4326,
);
assert_eq!(
out.assignments[0].min_level, 0,
"PIN: inflated antimeridian bbox promotes a 0.2°-wide feature \
to the coarsest level"
);
assert_eq!(
out.assignments[1].min_level, 1,
"the same feature at its true extent is visibility-gated to the \
finest level"
);
}
#[test]
fn antimeridian_center_lands_on_prime_meridian_and_displaces_neighbor() {
let antimeridian = poly(0, -179.9, -0.1, 179.9, 0.1); let local = poly(1, -0.5, -0.5, 0.5, 0.5); let gsds = [gsd(2), gsd(6)];
let out = assign_levels(
&[antimeridian, local],
&gsds,
&AssignConfig::default(),
Crs::Epsg4326,
);
assert_eq!(
out.assignments[0].min_level, 0,
"PIN: antimeridian feature wins the prime-meridian cell"
);
assert_eq!(
out.assignments[1].min_level, 1,
"PIN: genuine prime-meridian feature is displaced to the finest \
level by the antimeridian feature's inflated priority"
);
}
#[test]
fn plan_waves_single_when_under_budget() {
assert_eq!(plan_level_waves(&[100, 200, 300], 1_000), vec![0..3]);
assert_eq!(plan_level_waves(&[100, 200, 300], 600), vec![0..3]);
assert!(plan_level_waves(&[], 1).is_empty());
}
#[test]
fn plan_waves_splits_preserving_order_with_min_one_level() {
assert_eq!(
plan_level_waves(&[500, 500, 500], 100),
vec![0..1, 1..2, 2..3]
);
assert_eq!(
plan_level_waves(&[100, 100, 300, 50], 250),
vec![0..2, 2..3, 3..4]
);
assert_eq!(
plan_level_waves(&[u64::MAX, u64::MAX], u64::MAX - 1),
vec![0..1, 1..2]
);
}
#[test]
fn grid_estimate_caps_entries_at_feature_count() {
let cfg = AssignConfig::default();
let est = estimate_level_grid_bytes(&cfg, 1.0, (1e12, 1e12), &[1_000, 0, 0]);
assert_eq!(est, 1_000 * GRID_ENTRY_EST_BYTES);
assert_eq!(
estimate_level_grid_bytes(&cfg, 1.0, (1e12, 1e12), &[0, 0, 0]),
0
);
}
#[test]
fn grid_estimate_extent_bound_binds_at_coarse_levels() {
let cfg = AssignConfig {
point_thinning: 1.0,
..AssignConfig::default()
};
let est = estimate_level_grid_bytes(&cfg, 100.0, (1_000.0, 1_000.0), &[1_000_000, 0, 0]);
assert_eq!(est, 121 * GRID_ENTRY_EST_BYTES);
let fine = estimate_level_grid_bytes(&cfg, 10.0, (1_000.0, 1_000.0), &[1_000_000, 0, 0]);
assert!(fine > est, "finer GSD ⇒ more cells ⇒ larger estimate");
}
#[test]
fn bounded_waves_match_unbounded_assignment() {
let mut feats: Vec<AssignFeature> = Vec::new();
let mut idx = 0usize;
for c in 0..40 {
let base = c as f64 * 7_000.0;
for k in 0..25 {
let span = 150.0 + (k as f64) * 400.0;
feats.push(poly(idx, base, base, base + span, base + span));
idx += 1;
feats.push(point(idx, base + k as f64 * 31.0, base + k as f64 * 17.0));
idx += 1;
}
}
let gsds = [gsd(2), gsd(5), gsd(8), gsd(11), gsd(14)];
let cfg = AssignConfig::default();
let unbounded = assign_levels(&feats, &gsds, &cfg, Crs::Epsg3857);
let bounded = assign_levels_bounded(&feats, &gsds, &cfg, Crs::Epsg3857, 1, &[]);
assert_eq!(unbounded.assignments, bounded.assignments);
assert_eq!(unbounded.num_levels, bounded.num_levels);
let mid_budget = 200 * GRID_ENTRY_EST_BYTES;
let mid = assign_levels_bounded(&feats, &gsds, &cfg, Crs::Epsg3857, mid_budget, &[]);
assert_eq!(unbounded.assignments, mid.assignments);
}
#[test]
fn bounded_waves_match_unbounded_banded_assignment() {
let mut feats: Vec<AssignFeature> = Vec::new();
for i in 0..200 {
let base = (i % 20) as f64 * 0.01;
feats.push(poly(
i,
base,
base,
base + 0.0005 + (i as f64) * 1e-6,
base + 0.0005,
));
}
let gsds = [gsd(2), gsd(5), gsd(8), gsd(12)];
let cfg = AssignConfig::default();
let reprs = [
Representation::Point,
Representation::Point,
Representation::Square,
Representation::Geometry,
];
let unbounded = assign_levels_banded(&feats, &gsds, &cfg, Crs::Epsg4326, &reprs);
let bounded = assign_levels_bounded(&feats, &gsds, &cfg, Crs::Epsg4326, 1, &reprs);
assert_eq!(unbounded.assignments, bounded.assignments);
assert!(
unbounded.assignments.iter().any(|a| a.min_level == 0),
"point band must admit coarse winners"
);
}
#[test]
fn banded_point_levels_bypass_polygon_visibility_gate() {
let small = poly(0, 0.0, 0.0, 0.001, 0.001);
let gsds = [gsd(2), gsd(12)];
let cfg = AssignConfig::default();
let plain = assign_levels(&[small], &gsds, &cfg, Crs::Epsg4326);
assert_eq!(
plain.assignments[0].min_level, 1,
"precondition: gated out of the coarse level without a band"
);
let banded = assign_levels_banded(
&[small],
&gsds,
&cfg,
Crs::Epsg4326,
&[Representation::Point, Representation::Geometry],
);
assert_eq!(
banded.assignments[0].min_level, 0,
"point-band level must admit the sub-gate polygon"
);
}
#[test]
fn banded_empty_flags_match_plain_assign() {
let feats = [
poly(0, 0.0, 0.0, 5.0, 5.0),
poly(1, 10.0, 10.0, 10.001, 10.001),
];
let gsds = [gsd(2), gsd(6), gsd(12)];
let cfg = AssignConfig::default();
assert_eq!(
assign_levels(&feats, &gsds, &cfg, Crs::Epsg4326).assignments,
assign_levels_banded(&feats, &gsds, &cfg, Crs::Epsg4326, &[]).assignments
);
assert_eq!(
assign_levels(&feats, &gsds, &cfg, Crs::Epsg4326).assignments,
assign_levels_banded(
&feats,
&gsds,
&cfg,
Crs::Epsg4326,
&[Representation::Geometry; 3]
)
.assignments
);
}
#[test]
fn banded_point_levels_thin_polygons_on_point_grid() {
let a = poly(0, 0.0, 0.0, 0.002, 0.002);
let b = poly(1, 0.003, 0.003, 0.004, 0.004);
let gsds = [gsd(2), gsd(12)];
let cfg = AssignConfig::default();
let out = assign_levels_banded(
&[a, b],
&gsds,
&cfg,
Crs::Epsg4326,
&[Representation::Point, Representation::Geometry],
);
let coarse = out.assignments.iter().filter(|x| x.min_level == 0).count();
assert_eq!(
coarse, 1,
"one winner per point-grid cell at the band level"
);
assert_eq!(out.assignments[0].min_level, 0);
assert_eq!(out.assignments[1].min_level, 1);
}
#[test]
fn banded_square_levels_bypass_gate_on_polygon_grid() {
let a = poly(0, 0.0, 0.0, 0.001, 0.001);
let b = poly(1, 0.15, 0.0, 0.151, 0.001);
let gsds = [gsd(2), gsd(12)];
let cfg = AssignConfig::default();
let plain = assign_levels(&[a, b], &gsds, &cfg, Crs::Epsg4326);
assert!(
plain.assignments.iter().all(|x| x.min_level == 1),
"precondition: both gated out of the coarse level without a band"
);
let square_band = assign_levels_banded(
&[a, b],
&gsds,
&cfg,
Crs::Epsg4326,
&[Representation::Square, Representation::Geometry],
);
assert!(
square_band.assignments.iter().all(|x| x.min_level == 0),
"square band must admit both tiny polygons (polygon grid cells \
differ): {:?}",
square_band.assignments
);
let point_band = assign_levels_banded(
&[a, b],
&gsds,
&cfg,
Crs::Epsg4326,
&[Representation::Point, Representation::Geometry],
);
assert_eq!(
point_band
.assignments
.iter()
.filter(|x| x.min_level == 0)
.count(),
1,
"point band thins the same pair on the coarser point grid"
);
}
#[test]
fn banded_lines_and_points_unaffected() {
let line = AssignFeature {
index: 0,
bbox: [0.0, 0.0, 0.001, 0.001],
kind: FeatureKind::Line,
sort_key: None,
entry_level: None,
};
let gsds = [gsd(2), gsd(12)];
let cfg = AssignConfig::default();
let plain = assign_levels(&[line], &gsds, &cfg, Crs::Epsg4326);
let banded = assign_levels_banded(
&[line],
&gsds,
&cfg,
Crs::Epsg4326,
&[Representation::Point, Representation::Geometry],
);
assert_eq!(plain.assignments, banded.assignments);
}
}