mod common;
use kenro::functions::{edit, io};
fn from_hex(hex: &str) -> Vec<u8> {
(0..hex.len())
.step_by(2)
.map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
.collect()
}
fn ordinate_bits(bytes: &[u8]) -> Vec<u64> {
let mut out = Vec::new();
kenro::coords::for_each_coord(bytes, &mut |c| {
out.push(c.x.to_bits());
out.push(c.y.to_bits());
if let Some(z) = c.z {
out.push(z.to_bits());
}
})
.unwrap();
out
}
#[test]
fn golden_quantize() {
let (mut sweep, mut xy, mut xyz, mut errors) = (0usize, 0usize, 0usize, 0usize);
for v in common::load("quantize") {
let operand = from_hex(v.a.as_deref().unwrap_or_else(|| panic!("{}: no `a`", v.id)));
let args = v
.args
.as_ref()
.unwrap_or_else(|| panic!("{}: no args", v.id));
let p = |i: usize| args[i] as i32;
let got = match v.func.as_str() {
"quantize" => {
sweep += 1;
edit::st_quantize_coordinates(&operand, p(0), None, None)
}
"quantize_xy" => {
xy += 1;
edit::st_quantize_coordinates(&operand, p(0), Some(p(1)), None)
}
"quantize_xyz" => {
xyz += 1;
edit::st_quantize_coordinates(&operand, p(0), Some(p(1)), Some(p(2)))
}
other => panic!("unknown fn {other} in the quantize suite"),
};
let expected = v.effective();
if expected.get("error").is_some() {
errors += 1;
assert!(got.is_err(), "{}: expected an error, got {got:?}", v.id);
continue;
}
let want = from_hex(expected.as_str().unwrap_or_else(|| panic!("{}", v.id)));
let got = got.unwrap_or_else(|e| panic!("{}: {e}", v.id));
assert_eq!(
ordinate_bits(&got),
ordinate_bits(&want),
"{}: ordinate bits differ",
v.id
);
}
assert!(sweep > 2000, "only {sweep} sweep vectors ran");
assert!(xy == 3 && xyz == 3, "{xy} xy, {xyz} xyz");
assert_eq!(errors, 1, "the M divergence vector did not run");
}
#[test]
fn every_ordinate_stays_within_its_stated_precision() {
for x in [
1.0f64,
0.1,
std::f64::consts::PI,
123.456789,
-98765.4321,
1e-5,
6.02214076e23,
] {
for prec in 0..=17i32 {
let g = io::st_geom_from_text(&format!("POINT({x} 0)"), None).unwrap();
let q = edit::st_quantize_coordinates(&g, prec, None, None).unwrap();
let out: f64 = io::st_as_text(&q)
.unwrap()
.trim_start_matches("POINT(")
.split(' ')
.next()
.unwrap()
.parse()
.unwrap();
let tolerance = 10f64.powi(-prec) * x.abs().max(1.0);
assert!(
(out - x).abs() <= tolerance,
"x={x} prec={prec}: {out} is further than {tolerance} away"
);
}
}
}
#[test]
fn quantization_is_idempotent_and_monotone() {
let g = io::st_geom_from_text(
"LINESTRING(3.14159265 2.71828182,1.41421356 1.73205080)",
None,
)
.unwrap();
let once = edit::st_quantize_coordinates(&g, 4, None, None).unwrap();
let twice = edit::st_quantize_coordinates(&once, 4, None, None).unwrap();
assert_eq!(ordinate_bits(&once), ordinate_bits(&twice));
let widened = edit::st_quantize_coordinates(&once, 12, None, None).unwrap();
assert_eq!(ordinate_bits(&once), ordinate_bits(&widened));
}
#[test]
fn the_sql_surface() {
let conn = rusqlite::Connection::open_in_memory().unwrap();
kenro::register(&conn).unwrap();
let wkt: String = conn
.query_row(
"SELECT ST_AsText(ST_QuantizeCoordinates(
ST_GeomFromText('POINT(1.23456789 9.87654321)'), 2))",
[],
|r| r.get(0),
)
.unwrap();
assert_eq!(wkt, "POINT(1.234375 9.875)");
let wkt: String = conn
.query_row(
"SELECT ST_AsText(ST_QuantizeCoordinates(
ST_GeomFromText('POINT(1.23456789 9.87654321)'), 2, 15))",
[],
|r| r.get(0),
)
.unwrap();
assert_eq!(wkt, "POINT(1.234375 9.87654321)");
let srid: i32 = conn
.query_row(
"SELECT ST_SRID(ST_QuantizeCoordinates(
ST_GeomFromText('POINT(139.7654321 35.6812345)', 4326), 5, 5, 5))",
[],
|r| r.get(0),
)
.unwrap();
assert_eq!(srid, 4326);
for sql in [
"ST_QuantizeCoordinates(NULL, 3)",
"ST_QuantizeCoordinates(ST_GeomFromText('POINT(1 2)'), NULL)",
"ST_QuantizeCoordinates(ST_GeomFromText('POINT(1 2)'), 3, NULL)",
] {
let v: Option<Vec<u8>> = conn
.query_row(&format!("SELECT {sql}"), [], |r| r.get(0))
.unwrap();
assert_eq!(v, None, "{sql}");
}
}