// Ported from topojson-client src/quantize.js, ISC.
///|
fn replace_fields(j : Json, changes : Map[String, Json]) -> Json {
let out = match j {
Object(m) => m.copy()
_ => Map([])
}
for k, v in changes {
out[k] = v
}
Json::object(out)
}
///|
fn Quantizer::geometry(self : Quantizer, g : Json) -> Json raise TopoError {
let out = match kind(g) {
"Point" =>
replace_fields(
g,
Map([
(
"coordinates",
self.point(array(field(g, "coordinates")).map(number)).to_json(),
),
]),
)
"MultiPoint" =>
replace_fields(
g,
Map([
(
"coordinates",
array(field(g, "coordinates"))
.map(p => self.point(array(p).map(number)))
.to_json(),
),
]),
)
"GeometryCollection" =>
replace_fields(
g,
Map([
(
"geometries",
array(field(g, "geometries")).map(c => self.geometry(c)).to_json(),
),
]),
)
_ => g
}
out
}
///|
/// Explicit transform variant. Source must not already be quantized.
pub fn Topology::quantize_with(
self : Topology,
q : Quantizer,
) -> Topology raise TopoError {
if field(self.data, "transform") != Json::null() {
raise Invalid("quantize.already")
}
let objects : Map[String, Json] = Map([])
for name in self.names() {
objects[name] = q.geometry(self.object(name))
}
let arcs = array(field(self.data, "arcs")).map(a => {
q.arc(array(a).map(p => array(p).map(number)))
})
let out : Map[String, Json] = Map([
("type", "Topology".to_json()),
("objects", Json::object(objects)),
("arcs", arcs.to_json()),
(
"transform",
{ "scale": q.scale.to_json(), "translate": q.translate.to_json() },
),
])
let box = field(self.data, "bbox")
if box != Json::null() {
out["bbox"] = box
}
read(Json::object(out).stringify())
}
///|
/// Quantize to n grid values per axis (2..1e9); preserves references and metadata.
pub fn Topology::quantize(self : Topology, n : Int) -> Topology raise TopoError {
if n < 2 || n > 1000000000 {
raise Invalid("quantize.grid")
}
let stored = field(self.data, "bbox")
let box = if stored == Json::null() {
match self.bbox() {
Some(b) => b
None => raise Invalid("quantize.empty")
}
} else {
let b = array(stored).map(number)
if b.length() != 4 || b[0] > b[2] || b[1] > b[3] {
raise Invalid("bbox.invalid")
}
for v in array(stored) {
guard v is Number(x, ..) else { raise Invalid("bbox.number") }
if !finite(x) {
raise Invalid("bbox.number")
}
}
b
}
let dx = box[2] - box[0]
let dy = box[3] - box[1]
let q = quantizer(
[
if dx == 0.0 {
1.0
} else {
dx / (n - 1).to_double()
},
if dy == 0.0 {
1.0
} else {
dy / (n - 1).to_double()
},
],
[box[0], box[1]],
)
let source : Topology = {
data: replace_fields(self.data, Map([("bbox", box.to_json())])),
}
source.quantize_with(q)
}