code wiki / _hdl_build / nx_geo_centroid.nx
nx_geo_centroid.nx source
↩ module page · 59 lines · 2417 B
1// nx_geo_centroid.nx -- LIB: GEO-011 POLYGON CENTROID, two integer-exact variants.
2//
3// geo_centroid_vertex -- mean of the vertices (= turf.centroid). Exact at ANY scale; but sensitive
4// to how densely each edge is sampled with vertices.
5// geo_centroid_area -- the AREA-WEIGHTED centroid (= PostGIS ST_Centroid / turf.centerOfMass):
6// Cx = (1/(3*A2)) * sum (x_i+x_j)*cross, Cy likewise, cross = x_j*y_i-x_i*y_j.
7//
8// THE EXCEED ANGLE (measured): the area-weighted centroid is ROBUST -- it is unchanged by redundant
9// collinear edge vertices, whereas the vertex-mean shifts toward densely-sampled edges. The gate proves
10// they DIFFER on exactly such a polygon, and that the area-weighted one stays at the true center. Both
11// integer-exact (no float drift).
12//
13// OVERFLOW: geo_centroid_area origin-shifts to vertex 0 so the sums stay in delta space -- i64-exact
14// for regional polygons (sub-degree extent, modest vertex count = the geofence domain). Continental-
15// scale area-weighted centroids need the 128-bit path (nx_mul_wide); a documented future hardening.
16// Convention: poly = flat [lat0,lon0, ...], lat=Y, lon=X. license_tier: ORIGINAL
17import "nx_syscalls.nx"
18
19// mean of vertices -> out2 = [lat,lon].
20func geo_centroid_vertex(poly: *i64, n: i64, out2: *i64) -> i64 {
21 var sy: i64 = 0
22 var sx: i64 = 0
23 var i: i64 = 0
24 while i < n {
25 sy = sy + poly[i * 2]
26 sx = sx + poly[i * 2 + 1]
27 i = i + 1
28 }
29 out2[0] = sy / n
30 out2[1] = sx / n
31 return 0
32}
33
34// area-weighted centroid -> out2 = [lat,lon]. Origin-shifted to vertex 0 for i64 headroom.
35func geo_centroid_area(poly: *i64, n: i64, out2: *i64) -> i64 {
36 let oy: i64 = poly[0]
37 let ox: i64 = poly[1]
38 var a2: i64 = 0
39 var sx: i64 = 0
40 var sy: i64 = 0
41 var i: i64 = 0
42 var j: i64 = n - 1
43 while i < n {
44 let yi: i64 = poly[i * 2] - oy
45 let xi: i64 = poly[i * 2 + 1] - ox
46 let yj: i64 = poly[j * 2] - oy
47 let xj: i64 = poly[j * 2 + 1] - ox
48 let cross: i64 = xj * yi - xi * yj
49 a2 = a2 + cross
50 sx = sx + (xj + xi) * cross
51 sy = sy + (yj + yi) * cross
52 j = i
53 i = i + 1
54 }
55 if a2 == 0 { out2[0] = oy; out2[1] = ox; return 0 } // degenerate (zero-area) -> vertex 0
56 out2[0] = oy + sy / (3 * a2)
57 out2[1] = ox + sx / (3 * a2)
58 return 0
59}