// Ported from topojson-client src/neighbors.js, ISC.
///|
fn arc_id(i : Int) -> Int {
if i < 0 {
-i - 1
} else {
i
}
}
///|
fn refs_flat(j : Json, out : Array[Int]) -> Unit {
match j {
Array(a) =>
for v in a {
refs_flat(v, out)
}
Number(n, ..) => out.push(n.to_int())
_ => ()
}
}
///|
fn geometry_refs(g : Json) -> Array[Int] {
let out : Array[Int] = []
if kind(g) == "GeometryCollection" {
for c in array(field(g, "geometries")) {
for i in geometry_refs(c) {
out.push(i)
}
}
} else {
refs_flat(field(g, "arcs"), out)
}
out
}
///|
fn Topology::selected(
self : Topology,
names : Array[String],
) -> Array[Json] raise TopoError {
names.map(n => self.lookup(n))
}
///|
fn adjacency(objects : Array[Json]) -> Array[Array[Int]] raise TopoError {
let owners : Map[Int, Array[Int]] = Map([])
for k = 0; k < objects.length(); k = k + 1 {
for r in geometry_refs(objects[k]) {
let id = arc_id(r)
let list = owners.get(id).unwrap_or([])
list.push(k)
owners[id] = list
}
}
let mut work = 0
for _, group in owners {
if group.length() > 1000 {
raise Invalid("limit.neighbor.owners")
}
work += group.length() * group.length()
if work > 2000000 {
raise Invalid("limit.neighbor.pairs")
}
}
let out = Array::makei(objects.length(), _ => [])
for _, group in owners {
for j = 0; j < group.length(); j = j + 1 {
for k = j + 1; k < group.length(); k = k + 1 {
let a = group[j]
let b = group[k]
if !out[a].contains(b) {
out[a].push(b)
}
if !out[b].contains(a) {
out[b].push(a)
}
}
}
}
for row in out {
row.sort()
}
out
}
///|
/// Neighbors are based on shared arc identity, not coincident coordinates.
pub fn Topology::neighbors(
self : Topology,
names : Array[String],
) -> Array[Array[Int]] raise TopoError {
adjacency(self.selected(names))
}
///|
/// Each direct member of a GeometryCollection is one neighbor-graph node.
pub fn Topology::collection_neighbors(
self : Topology,
name : String,
) -> Array[Array[Int]] raise TopoError {
let g = self.lookup(name)
if kind(g) != "GeometryCollection" {
raise Invalid("collection.required")
}
adjacency(array(field(g, "geometries")))
}