1package cells_test23import (4 "testing"56 "github.com/FelineStateMachine/atlas/format/semconv"7 "github.com/FelineStateMachine/atlas/internal/app/cells"8)910// The held-cell predicate, asked the questions a reader's pins actually ask11// it: points on a cell's edge, points a hair past it, and the same place12// spelled twice at the two ends of a world that wraps.13//14// The shared vectors (analysis/testdata/cells, read by tests/cells) hold the15// arithmetic to the oracle both lanes answer. This file holds the *edges* of16// it -- the cases a vector file never quite reaches, because a fixture records17// what a map did and these ask what a map must never do.18//19// Everything here is a table and nothing reads a file: this package lives20// under internal/app, where the hostenv rule says the arithmetic may not know21// what a filesystem is. The one declaration spelled out below -- the Mars22// flattening -- is the corpus bundle's own, and tests/cells holds the shared23// grounds fixture to the shipped payload so a drift in either copy is a24// failure somewhere rather than a quiet disagreement.2526// square is the hand-derived 1024 ground of the analysis suite: a world27// declaring the whole of itself, which is the ground `m6` was halved out of.28// Its rectangle is [672, -704] to [704, -672]29// (analysis/testdata/cells/containment.json).30var square = cells.Extent{MinX: 0, MinY: -1024, MaxX: 1024, MaxY: 0}3132// marsAttrs is the declaration the shipped Mars bundle makes33// (testdata/corpus/bundles/mars, and `mars/global/0` in the shared grounds):34// a sphere, flattened equirectangularly over the whole 8192x4096 square.35var marsAttrs = map[string]string{36 semconv.KeyGeometrySurface: semconv.SurfaceSphere,37 semconv.KeyGeometryProjection: semconv.ProjectionEquirect,38 semconv.KeyGeometryEquirectPx: "0,0,8192,4096",39 semconv.KeyGeometryEquirectDeg: "-180,90,180,-90",40}4142func TestGeohashHoldsItsBoundaries(t *testing.T) {43 tests := []struct {44 name string45 hash string46 x, y float6447 holds bool48 }{49 {"the middle of the cell", "m6", 688, -688, true},50 {"the maximum corner", "m6", 704, -672, true},51 {"the minimum corner", "m6", 672, -704, true},52 {"the top edge", "m6", 688, -672, true},53 {"the left edge", "m6", 672, -688, true},54 // Boundaries are inclusive on both sides, so a pin on the line55 // between two cells is standing in both. That is the contract's56 // answer and not an accident of the comparison: the line is drawn a57 // pixel wide and the reader put the pin on it.58 {"the neighbour shares the edge", "m4", 672, -688, true},59 {"a hair past the maximum", "m6", 704.0001, -672, false},60 {"a hair past the minimum", "m6", 672, -704.0001, false},61 {"the other side of the world", "m6", 128, -128, false},62 // The parent holds everything its children do, which is what makes63 // telescoping in narrow rather than move.64 {"the parent holds the child's corner", "m", 704, -672, true},65 // The root is the ground: an address with nothing in it halves66 // nothing away.67 {"the root holds the far corner", "", 1024, 0, true},68 {"the root does not hold what is off the ground", "", 1024.5, 0, false},69 }70 for _, test := range tests {71 t.Run(test.name, func(t *testing.T) {72 if got := cells.GeohashHeld(square, test.hash)(test.x, test.y); got != test.holds {73 t.Errorf("%q holds (%g, %g) = %v, want %v", test.hash, test.x, test.y, got, test.holds)74 }75 })76 }77}7879// TestS2HoldsOnMarsAsTheBundleDeclaresIt asks the predicate the questions a80// reader standing on Mars asks it, through the flattening the shipped bundle81// declares.82func TestS2HoldsOnMarsAsTheBundleDeclaresIt(t *testing.T) {83 mapping, ok := cells.MappingOf(marsAttrs)84 if !ok {85 t.Fatal("the Mars declaration is a flattening and the reader refused it")86 }8788 // Mars's world square is 8192 x 4096 picturing the whole body, so the89 // centre of the raster is (0, 0) on the ground and the corners are the90 // antimeridian at either pole. World pixels are y-negative-down, which is91 // the space a packed position lands in.92 const width, height = 8192.0, 4096.093 centre := [2]float64{width / 2, -height / 2}94 held := cells.S2CellAt(mapping, centre[0], centre[1])95 if held == "" {96 t.Fatal("the middle of Mars is nowhere")97 }9899 tests := []struct {100 name string101 cell string102 x, y float64103 holds bool104 }{105 {"the cell under a point holds it", held, centre[0], centre[1], true},106 {"and so does its parent", cells.S2Parent(held), centre[0], centre[1], true},107 {"and so does the face they are on", faceOf(held), centre[0], centre[1], true},108 {"but not one of the other five", otherFace(t, held), centre[0], centre[1], false},109 {"a level-10 cell is small", held, centre[0] + 64, centre[1], false},110 {"the antipode is elsewhere", held, 0, centre[1], false},111 // The root is the whole sphere. Nothing narrows, which is the same112 // answer the seam gives and the reason a session with no cell held113 // never reaches this predicate at all.114 {"the root holds every place", "", 12, -3000, true},115 // An address nobody can read holds nothing. The guard matters: the116 // library's zero cell id spans the entire curve, so an unguarded117 // containment on a misspelling would quietly stop narrowing.118 {"a misspelling holds nothing", "not-a-token", centre[0], centre[1], false},119 {"an empty-looking misspelling holds nothing", "zzzz", centre[0], centre[1], false},120 }121 for _, test := range tests {122 t.Run(test.name, func(t *testing.T) {123 if got := cells.S2Held(mapping, test.cell)(test.x, test.y); got != test.holds {124 t.Errorf("%q holds (%g, %g) = %v, want %v", test.cell, test.x, test.y, got, test.holds)125 }126 })127 }128129 // The two ends of the raster are one meridian on the body. The picture130 // has a seam there and the sphere does not, and the arithmetic says so:131 // the far west edge and the far east edge land on the same face, on132 // either side of a cell boundary that happens to fall exactly on the133 // antimeridian. They are neighbours rather than the same address, which134 // is as close as floating point gets to "the same place" -- and it is the135 // seam's answer too, since it is the same library's.136 west := cells.S2CellAt(mapping, 0, -height/2)137 east := cells.S2CellAt(mapping, width, -height/2)138 if west == "" || east == "" || faceOf(west) != faceOf(east) {139 t.Errorf("the antimeridian is two places: west %q on face %q, east %q on face %q",140 west, faceOf(west), east, faceOf(east))141 }142143 // A pin straddling the edge between two S2 cells is in both of them, the144 // same way it is in both of two geohash cells: the leaf under the point145 // is one cell, and the neighbours that share the edge are asked about the146 // same leaf. This is the weaker claim the arithmetic actually makes --147 // containment is by descendancy, and a point is in exactly the chain of148 // cells above its leaf.149 for level, ancestor := 1, held; ancestor != ""; level, ancestor = level+1, cells.S2Parent(ancestor) {150 if !cells.S2Held(mapping, ancestor)(centre[0], centre[1]) {151 t.Errorf("the ancestor %q of %q does not hold the point they were both found from",152 ancestor, held)153 }154 if got := cells.S2Level(ancestor); got != cells.S2Level(held)-level+1 {155 t.Errorf("%q is at level %d, %d levels above %q", ancestor, got, level-1, held)156 }157 }158}159160func TestApplicableSystemsAsksTheWorldTwoQuestions(t *testing.T) {161 tests := []struct {162 name string163 attrs map[string]string164 systems []string165 }{166 {"a world that says nothing is a plane", nil, []string{"geohash"}},167 {"and so is one that says so", map[string]string{168 semconv.KeyGeometrySurface: "plane",169 }, []string{"geohash"}},170 {"a sphere with a flattening takes both", marsAttrs, []string{"geohash", "s2"}},171 // The included Earth volume's own declaration: the body and its172 // radius change nothing the systems read. In particular, naming the173 // earth on a *sphere* does not put geohash into its real mode --174 // that mode is a plane's (see TestEarthMercatorAsksAllThreeQuestions)175 // -- so the whole-planet picture divides synthetically, like Mars.176 {"the included Earth's declaration takes both", map[string]string{177 semconv.KeyGeometrySurface: semconv.SurfaceSphere,178 semconv.KeyGeometryProjection: semconv.ProjectionEquirect,179 semconv.KeyGeometryEquirectPx: "0,0,8192,4096",180 semconv.KeyGeometryEquirectDeg: "-180,90,180,-90",181 semconv.KeyGeometryBody: "earth",182 semconv.KeyGeometryRadiusKM: "6371.0088",183 }, []string{"geohash", "s2"}},184 {"a sphere that declares no flattening takes neither but geohash", map[string]string{185 semconv.KeyGeometrySurface: "sphere",186 }, []string{"geohash"}},187 {"nor one whose window is unreadable", map[string]string{188 semconv.KeyGeometrySurface: "sphere",189 semconv.KeyGeometryProjection: "equirect",190 semconv.KeyGeometryEquirectPx: "0,0,0,0",191 semconv.KeyGeometryEquirectDeg: "-180,90,180,-90",192 }, []string{"geohash"}},193 {"nor one that pictures no ground", map[string]string{194 semconv.KeyGeometrySurface: "sphere",195 semconv.KeyGeometryProjection: "equirect",196 semconv.KeyGeometryEquirectPx: "0,0,8192,4096",197 semconv.KeyGeometryEquirectDeg: "-180,90,-180,-90",198 }, []string{"geohash"}},199 {"nor one whose numbers are not numbers", map[string]string{200 semconv.KeyGeometrySurface: "sphere",201 semconv.KeyGeometryProjection: "equirect",202 semconv.KeyGeometryEquirectPx: "0,0,8192",203 semconv.KeyGeometryEquirectDeg: "-180,90,180,-90",204 }, []string{"geohash"}},205 // The window is only read when the world says which flattening the206 // numbers are for, which is the reader's rule in the seam too207 // (analysis/semconv/geometry.ts, declaredWindow).208 {"nor one that does not say what its numbers are", map[string]string{209 semconv.KeyGeometrySurface: "sphere",210 semconv.KeyGeometryEquirectPx: "0,0,8192,4096",211 semconv.KeyGeometryEquirectDeg: "-180,90,180,-90",212 }, []string{"geohash"}},213 {"a flattening without a sphere is a picture of nothing in particular", map[string]string{214 semconv.KeyGeometryProjection: "equirect",215 semconv.KeyGeometryEquirectPx: "0,0,8192,4096",216 semconv.KeyGeometryEquirectDeg: "-180,90,180,-90",217 }, []string{"geohash"}},218 }219 for _, test := range tests {220 t.Run(test.name, func(t *testing.T) {221 got := cells.ApplicableSystems(test.attrs)222 if len(got) != len(test.systems) {223 t.Fatalf("systems = %v, want %v", got, test.systems)224 }225 for at := range got {226 if got[at] != test.systems[at] {227 t.Fatalf("systems = %v, want %v", got, test.systems)228 }229 }230 // The list and the question have to answer alike, because the231 // navigator offers from one and the session validates with the232 // other.233 for _, system := range []string{"geohash", "s2", "", "h3", "GEOHASH"} {234 want := false235 for _, offered := range got {236 want = want || offered == system237 }238 if cells.Applicable(test.attrs, system) != want {239 t.Errorf("Applicable(%q) disagrees with %v", system, got)240 }241 }242 })243 }244}245246// TestS2ParseCellRefusesWhatIsNotAPlace holds the address reader to the247// navigator's rules: the root is a place, a token past the telescope's floor248// clamps to it, and everything else is either canonical or nothing.249func TestS2ParseCellRefusesWhatIsNotAPlace(t *testing.T) {250 tests := []struct {251 name string252 text string253 cell string254 ok bool255 }{256 {"the root", "", "", true},257 {"a face", "5", "5", true},258 {"a token at the floor", "47a1cb", "47a1cb", true},259 {"a leaf clamps to the floor", "47a1cbd595522b39", "47a1cb", true},260 {"a non-token", "xyz", "", false},261 {"a word", "geohash", "", false},262 {"a number that is not a cell", "0", "", false},263 }264 for _, test := range tests {265 t.Run(test.name, func(t *testing.T) {266 cell, ok := cells.S2ParseCell(test.text)267 if ok != test.ok || cell != test.cell {268 t.Errorf("S2ParseCell(%q) = %q, %v; want %q, %v", test.text, cell, ok, test.cell, test.ok)269 }270 })271 }272}273274// bendAttrs is the declaration the shipped bend-or bundle makes275// (testdata/corpus/bundles/bend-or, and `bend-or/2026-08-02/0` in the shared276// grounds): a plane city anchored to real earth by a Mercator window.277var bendAttrs = map[string]string{278 semconv.KeyGeometrySurface: semconv.SurfacePlane,279 semconv.KeyGeometryBody: "earth",280 semconv.KeyGeometryMercatorPx: "0,0,8192,8192",281 semconv.KeyGeometryMercatorDeg: "-121.48204667177453,44.17572950664809,-121.1529533282255,43.93922950850393",282}283284// TestBendOrComesOutBaseDepthFourFloorSix pins the anchored telescope to the285// window it was derived from: cell spans halve from 360°/180° per the 5-bit286// alternation, depth 3 is 1.40625° both ways (too coarse for either axis of287// the 0.32909° × 0.23650° window), and depth 4's latitude span (0.17578125°)288// fits where its longitude span (0.3515625°) just misses. Base 4, floor 6 --289// exactly, and the seam's geohash.test.ts asserts the same two numbers.290func TestBendOrComesOutBaseDepthFourFloorSix(t *testing.T) {291 mapping, anchored := cells.EarthMercator(bendAttrs)292 if !anchored {293 t.Fatal("the bend-or declaration is an earth anchoring and the reader refused it")294 }295 if got := cells.GeohashBaseDepth(mapping); got != 4 {296 t.Errorf("base depth = %d, want 4", got)297 }298 if got := cells.GeohashDepthOf(bendAttrs); got != 6 {299 t.Errorf("depth floor = %d, want 6", got)300 }301 // And a synthetic plane keeps the constant.302 if got := cells.GeohashDepthOf(nil); got != cells.GeohashMaxDepth {303 t.Errorf("a synthetic plane answers %d, want %d", got, cells.GeohashMaxDepth)304 }305}306307// TestEarthMercatorAsksAllThreeQuestions: a plane, an invertible Mercator308// window, and a body that says earth -- drop any one and real mode never309// starts. Mars is a sphere with an equirect window and no earth body, and310// must be completely unaffected.311func TestEarthMercatorAsksAllThreeQuestions(t *testing.T) {312 without := func(key string) map[string]string {313 out := map[string]string{}314 for k, v := range bendAttrs {315 if k != key {316 out[k] = v317 }318 }319 return out320 }321 if _, anchored := cells.EarthMercator(bendAttrs); !anchored {322 t.Error("the whole declaration anchors, and was refused")323 }324 // Surface ABSENT still means plane; the other clauses hold it up.325 if _, anchored := cells.EarthMercator(without(semconv.KeyGeometrySurface)); !anchored {326 t.Error("an undeclared surface is a plane, and was refused")327 }328 for _, key := range []string{329 semconv.KeyGeometryBody,330 semconv.KeyGeometryMercatorPx,331 semconv.KeyGeometryMercatorDeg,332 } {333 if _, anchored := cells.EarthMercator(without(key)); anchored {334 t.Errorf("dropping %s still anchored", key)335 }336 }337 if _, anchored := cells.EarthMercator(marsAttrs); anchored {338 t.Error("mars anchored to earth")339 }340 // A world that declares its numbers are a DIFFERENT flattening is341 // declaring something this cannot read.342 contradicted := map[string]string{}343 for k, v := range bendAttrs {344 contradicted[k] = v345 }346 contradicted[semconv.KeyGeometryProjection] = semconv.ProjectionEquirect347 if _, anchored := cells.EarthMercator(contradicted); anchored {348 t.Error("a contradicting projection was read as a Mercator window")349 }350}351352// TestRealGeohashHoldsItsBoundaries is [TestGeohashHoldsItsBoundaries] in the353// real mode: the cell's ground is a degree rectangle, the question travels354// through the flattening, and the boundaries are inclusive in DEGREES. The355// probes are the shared vectors' own (analysis/testdata/cells,356// containment.json, the real-* cases), plus the edges a fixture never quite357// reaches.358func TestRealGeohashHoldsItsBoundaries(t *testing.T) {359 mapping, anchored := cells.EarthMercator(bendAttrs)360 if !anchored {361 t.Fatal("bend-or did not anchor")362 }363 tests := []struct {364 name string365 hash string366 x, y float64367 holds bool368 }{369 {"the raster centre is in its own base cell", "9rcd", 4096, -4096, true},370 {"and in its own floor cell", "9rcdxk", 4096, -4096, true},371 {"and not in the neighbour", "9rcf", 4096, -4096, false},372 {"a point east of the window is still on real ground", "9rcf", 9000, -4096, true},373 {"the root is the whole earth", "", 9000, -4096, true},374 {"the north-west quarter probe", "9rcdux", 1024, -2048, true},375 }376 for _, test := range tests {377 t.Run(test.name, func(t *testing.T) {378 if got := cells.GeohashRealHeld(mapping, test.hash)(test.x, test.y); got != test.holds {379 t.Errorf("%q holds (%g, %g) = %v, want %v", test.hash, test.x, test.y, got, test.holds)380 }381 })382 }383384 // The address under a point at every layer, against the vectors' own385 // derivation: 9rcd / 9rcdx / 9rcdxk under the raster centre.386 for depth, want := range map[int]string{4: "9rcd", 5: "9rcdx", 6: "9rcdxk"} {387 if got := cells.GeohashRealCellAt(mapping, 4096, -4096, depth); got != want {388 t.Errorf("the centre at depth %d = %q, want %q", depth, got, want)389 }390 }391392 // The drawn rectangle overhangs the window, honestly: 9rcd's west edge393 // (-121.640625°) is west of the raster, so its extent starts at a394 // negative x. The shared geometry vectors carry the full-precision395 // numbers; this holds the shape of the fact.396 extent := cells.GeohashRealExtent(mapping, "9rcd")397 if extent.MinX >= 0 {398 t.Errorf("9rcd's west edge is on the raster (MinX %g); it belongs west of it", extent.MinX)399 }400 if !extent.Holds(4096, -4096) {401 t.Error("9rcd's own extent does not hold the raster centre")402 }403}404405// faceOf telescopes an address all the way out to the face it is on.406func faceOf(cell string) string {407 for cells.S2Level(cell) > 1 {408 cell = cells.S2Parent(cell)409 }410 return cell411}412413// otherFace names one of the five faces an address is not on.414func otherFace(t *testing.T, cell string) string {415 t.Helper()416 for _, face := range cells.S2Children("") {417 if face != faceOf(cell) {418 return face419 }420 }421 t.Fatalf("%q is on every face", cell)422 return ""423}