import assert from "node:assert/strict"; import test from "node:test"; import { ArcType, ApproximateTerrainHeights, Cartesian3, Ellipsoid, GroundPolylineGeometry, Matrix4, Math as CesiumMath, PolylineGeometry, Transforms, } from "cesium"; import { alignedGridValues, boundedAngularParts, fixedGridOrigin, geodeticRectangleAreaSquareMeters, geodeticToLocalGridPlane, graticuleGranularity, graticuleLinePlan, graticuleMajorTileAt, graticuleMajorTileBounds, graticuleMajorTileChildren, graticuleMajorTileForSector, graticuleMajorTileNeighbors, graticuleMajorTileSummary, graticuleSectorAreaSquareMeters, graticuleSectorAt, graticuleSectorBounds, graticuleSectorNeighbors, graticuleSectorSummary, isGraticuleMajorLineIndex, isGraticuleMajorLineValue, isLocalMajorLineIndex, localGridPlan, localMajorTileAt, localMajorTileBounds, localMajorTileChildren, localMajorTileForSector, localMajorTileId, localMajorTileNeighbors, localMajorTileSummary, MAX_GRID_CHILD_PAGE_SIZE, MAX_LOCAL_GRID_INDEX, localParentSector, localSectorAreaSquareMeters, localSectorAt, localSectorAtGeodetic, localSectorBounds, localSectorNeighbors, localSectorSummary, localVolumeAt, localVolumeBounds, localVolumeId, localVolumeNeighbors, localVolumeSummary, normalizeLongitudeDegrees, splitLongitudeRange, } from "../apps/catalog/src/mapSectorGrid.mjs"; const localDefinition = { lod: 1, originLatitude: 55.7558, originLongitude: 37.6173, stepMeters: 1_000, }; const localHierarchyDefinition = { ...localDefinition, tileSizeMeters: 10_000, }; const volumeDefinition = { ...localDefinition, altitudeFloorMeters: -50, altitudeCeilingMeters: 225, altitudeBandMeters: 100, }; test("fixed origin normalizes WGS84 coordinates without consulting the camera", () => { assert.deepEqual(fixedGridOrigin({}), { latitude: 55.7558, longitude: 37.6173 }); assert.deepEqual(fixedGridOrigin({ gridCenterLatitude: 100, gridCenterLongitude: 540 }), { latitude: 89.9, longitude: -180, }); assert.equal(normalizeLongitudeDegrees(-540), -180); assert.equal(normalizeLongitudeDegrees(360), 0); assert.equal(Object.is(normalizeLongitudeDegrees(-360), -0), false); }); test("local sectors use half-open floor boundaries on both sides of the ENU origin", () => { assert.deepEqual( [ [0, 0], [999.999, 999.999], [1_000, 1_000], [-0.001, -0.001], [-1_000, -1_000], [-1_000.001, -1_000.001], ].map(([eastMeters, northMeters]) => { const sector = localSectorAt({ eastMeters, northMeters }, localDefinition); return [sector.eastIndex, sector.northIndex]; }), [ [0, 0], [0, 0], [1, 1], [-1, -1], [-1, -1], [-2, -2], ], ); }); test("WGS84 positions use the same tangent-plane address as Cesium sector picking", () => { const origin = Cartesian3.fromDegrees(localDefinition.originLongitude, localDefinition.originLatitude, 0); const inverseEnu = Matrix4.inverse(Transforms.eastNorthUpToFixedFrame(origin), new Matrix4()); const point = { longitude: 37.645, latitude: 55.773 }; const worldPosition = Cartesian3.fromDegrees(point.longitude, point.latitude, 0); const surface = Ellipsoid.WGS84.scaleToGeodeticSurface(worldPosition, new Cartesian3()); const normal = Ellipsoid.WGS84.geodeticSurfaceNormal(surface, new Cartesian3()); const localSurface = Matrix4.multiplyByPoint(inverseEnu, surface, new Cartesian3()); const localNormal = Matrix4.multiplyByPointAsVector(inverseEnu, normal, new Cartesian3()); const normalScale = -localSurface.z / localNormal.z; const expected = { eastMeters: localSurface.x + localNormal.x * normalScale, northMeters: localSurface.y + localNormal.y * normalScale, }; const actual = geodeticToLocalGridPlane(point, localDefinition); assert.ok(Math.abs(actual.eastMeters - expected.eastMeters) < 1e-6); assert.ok(Math.abs(actual.northMeters - expected.northMeters) < 1e-6); assert.equal( localSectorAtGeodetic(point, localDefinition).id, localSectorAt(expected, localDefinition).id, ); }); test("local sector IDs and bounds stay stable across calls and normalized equivalent origins", () => { const first = localSectorAt({ eastMeters: -1, northMeters: 2_500 }, localDefinition); const second = localSectorAt({ eastMeters: -1, northMeters: 2_500 }, { ...localDefinition, originLongitude: localDefinition.originLongitude + 360, }); assert.equal(first.id, "grid/local/55.755800,37.617300/l1/s1000.000/e-1/n+2"); assert.equal(second.id, first.id); assert.deepEqual(localSectorBounds(first, localDefinition.stepMeters), { west: -1_000, east: 0, south: 2_000, north: 3_000, }); }); test("local sector neighbors and integer-ratio parents preserve signed addressing", () => { const child = localSectorAt({ eastMeters: -1, northMeters: 2_500 }, localDefinition); const neighbors = localSectorNeighbors(child, localDefinition); assert.deepEqual( Object.fromEntries(Object.entries(neighbors).map(([direction, address]) => [ direction, [address.eastIndex, address.northIndex], ])), { north: [-1, 3], east: [0, 2], south: [-1, 1], west: [-2, 2], }, ); const parent = localParentSector(child, 1_000, { ...localDefinition, lod: 2, stepMeters: 5_000 }); assert.deepEqual([parent.eastIndex, parent.northIndex], [-1, 0]); assert.equal(parent.id, "grid/local/55.755800,37.617300/l2/s5000.000/e-1/n+0"); assert.throws( () => localParentSector(child, 1_000, { ...localDefinition, lod: 2, stepMeters: 2_500 }), /grid_parent_step_must_be_integer_multiple/, ); }); test("local major tiles keep a fixed phase across positive and negative ENU boundaries", () => { const samples = [ [-10_000.001, -2], [-10_000, -1], [-0.001, -1], [0, 0], [9_999.999, 0], [10_000, 1], ]; assert.deepEqual( samples.map(([eastMeters]) => localMajorTileAt( { eastMeters, northMeters: 9_999.999 }, localHierarchyDefinition, ).eastIndex), samples.map(([, expected]) => expected), ); const tile = localMajorTileAt( { eastMeters: -0.001, northMeters: 9_999.999 }, localHierarchyDefinition, ); assert.equal(tile.id, "grid/local/55.755800,37.617300/l1/s1000.000/t10000.000/e-1/n+0"); assert.equal(tile.minorPerSide, 10); assert.deepEqual(localMajorTileBounds(tile, localHierarchyDefinition.tileSizeMeters), { west: -10_000, east: 0, south: 0, north: 10_000, }); }); test("local major parent, children and neighbors are exact inverse hierarchy operations", () => { const tile = localMajorTileAt( { eastMeters: -1, northMeters: 1 }, localHierarchyDefinition, ); const children = localMajorTileChildren(tile, localHierarchyDefinition); assert.equal(children.length, 100); assert.deepEqual( [children[0].eastIndex, children[0].northIndex, children.at(-1).eastIndex, children.at(-1).northIndex], [-10, 0, -1, 9], ); for (const child of children) { assert.equal(localMajorTileForSector(child, localHierarchyDefinition).id, tile.id); } const neighbors = localMajorTileNeighbors(tile, localHierarchyDefinition); assert.deepEqual( Object.fromEntries(Object.entries(neighbors).map(([direction, address]) => [ direction, [address.eastIndex, address.northIndex], ])), { north: [-1, 1], east: [0, 0], south: [-1, -1], west: [-2, 0] }, ); assert.equal(isLocalMajorLineIndex(-10, localHierarchyDefinition), true); assert.equal(isLocalMajorLineIndex(-1, localHierarchyDefinition), false); assert.equal(isLocalMajorLineIndex(0, localHierarchyDefinition), true); }); test("large local major tiles require bounded child pages", () => { const largeDefinition = { ...localDefinition, stepMeters: 100, tileSizeMeters: 50_000 }; const tile = localMajorTileAt({ eastMeters: 0, northMeters: 0 }, largeDefinition); assert.equal(tile.minorPerSide, 500); assert.throws( () => localMajorTileChildren(tile, largeDefinition), /grid_child_page_limit_exceeded/, ); const tail = localMajorTileChildren(tile, largeDefinition, { offset: 249_998, limit: 2 }); assert.deepEqual(tail.map(({ eastIndex, northIndex }) => [eastIndex, northIndex]), [ [498, 499], [499, 499], ]); assert.throws( () => localMajorTileChildren(tile, largeDefinition, { limit: MAX_GRID_CHILD_PAGE_SIZE + 1 }), /grid_child_page_limit_exceeded/, ); }); test("local hierarchy rejects non-integral, non-positive and unsafe definitions", () => { assert.throws( () => localMajorTileAt({ eastMeters: 0, northMeters: 0 }, { ...localDefinition, tileSizeMeters: 2_500, }), /grid_tile_size_must_be_integer_multiple_of_step/, ); assert.throws( () => localMajorTileAt({ eastMeters: 0, northMeters: 0 }, { ...localDefinition, tileSizeMeters: 0, }), /grid_tile_size_must_be_positive/, ); assert.throws( () => localMajorTileAt({ eastMeters: 0, northMeters: 0 }, { ...localDefinition, stepMeters: Number.NaN, tileSizeMeters: 10_000, }), /grid_step_must_be_positive/, ); assert.throws( () => localMajorTileId(localHierarchyDefinition, 0.5, 0), /grid_index_must_be_safe_integer/, ); assert.doesNotThrow(() => localMajorTileAt( { eastMeters: -0.001, northMeters: 0 }, { ...localDefinition, stepMeters: 0.1, tileSizeMeters: 1 }, )); }); test("local summaries are UI-ready and expose neighbor and major-parent geometry", () => { const sector = localSectorAt({ eastMeters: -1, northMeters: 2_500 }, localDefinition); const summary = localSectorSummary(sector, localHierarchyDefinition); assert.equal(summary.id, sector.id); assert.deepEqual(summary.center, { eastMeters: -500, northMeters: 2_500 }); assert.equal(summary.areaSquareMeters, 1_000_000); assert.equal(localSectorAreaSquareMeters(sector, 1_000), 1_000_000); assert.equal(summary.neighbors.west.address.id, localSectorNeighbors(sector, localDefinition).west.id); assert.deepEqual(summary.neighbors.north.center, { eastMeters: -500, northMeters: 3_500 }); assert.equal(summary.majorTile?.id, summary.parentMajorTile?.id); assert.equal(summary.majorTile?.minorPerSide, 10); assert.deepEqual(summary.majorTile?.center, { eastMeters: -5_000, northMeters: 5_000 }); assert.equal(summary.majorTile?.areaSquareMeters, 100_000_000); const standalone = localSectorSummary(sector, localDefinition); assert.equal(standalone.majorTile, null); assert.equal(standalone.parentMajorTile, null); const majorSummary = localMajorTileSummary(summary.majorTile.address, localHierarchyDefinition); assert.equal(majorSummary.childCount, 100); assert.equal(majorSummary.neighbors.east.address.eastIndex, 0); }); test("local volume addressing is half-open and clips an incomplete last band", () => { const samples = [ [-50, 0], [49.999, 0], [50, 1], [149.999, 1], [150, 2], [224.999, 2], ]; for (const [altitudeMeters, expectedBand] of samples) { assert.equal( localVolumeAt({ eastMeters: -1, northMeters: 2_500, altitudeMeters }, volumeDefinition)?.bandIndex, expectedBand, ); } assert.equal(localVolumeAt({ eastMeters: 0, northMeters: 0, altitudeMeters: -50.001 }, volumeDefinition), null); assert.equal(localVolumeAt({ eastMeters: 0, northMeters: 0, altitudeMeters: 225 }, volumeDefinition), null); const last = localVolumeAt({ eastMeters: -1, northMeters: 2_500, altitudeMeters: 200 }, volumeDefinition); assert.ok(last); assert.equal(last.id, "grid/local-volume/55.755800,37.617300/l1/s1000.000/f-50.000/c225.000/h100.000/e-1/n+2/z+2"); assert.equal(last.altitudeBandMeters, 75); assert.deepEqual(localVolumeBounds(last, volumeDefinition), { west: -1_000, east: 0, south: 2_000, north: 3_000, altitudeFloorMeters: 150, altitudeCeilingMeters: 225, }); const summary = localVolumeSummary(last, volumeDefinition); assert.equal(summary.address.id, last.id); assert.deepEqual(summary.center, { eastMeters: -500, northMeters: 2_500, altitudeMeters: 187.5, }); assert.equal(summary.footprintAreaSquareMeters, 1_000_000); assert.equal(summary.volumeCubicMeters, 75_000_000); }); test("local volume neighbors stop at vertical limits but remain unbounded horizontally", () => { const first = localVolumeAt({ eastMeters: 0, northMeters: 0, altitudeMeters: 0 }, volumeDefinition); assert.ok(first); const firstNeighbors = localVolumeNeighbors(first, volumeDefinition); assert.equal(firstNeighbors.below, null); assert.equal(firstNeighbors.above?.bandIndex, 1); assert.equal(firstNeighbors.west.eastIndex, -1); const last = localVolumeAt({ eastMeters: 0, northMeters: 0, altitudeMeters: 200 }, volumeDefinition); assert.ok(last); const lastNeighbors = localVolumeNeighbors(last, volumeDefinition); assert.equal(lastNeighbors.above, null); assert.equal(lastNeighbors.below?.bandIndex, 1); }); test("local volume contract rejects degenerate ranges, bands and invalid addresses", () => { assert.throws( () => localVolumeAt({ eastMeters: 0, northMeters: 0, altitudeMeters: 0 }, { ...volumeDefinition, altitudeBandMeters: 0, }), /grid_altitude_band_must_be_positive/, ); assert.throws( () => localVolumeAt({ eastMeters: 0, northMeters: 0, altitudeMeters: 0 }, { ...volumeDefinition, altitudeCeilingMeters: -50, }), /grid_altitude_ceiling_must_exceed_floor/, ); assert.throws( () => localVolumeAt({ eastMeters: 0, northMeters: 0, altitudeMeters: 0 }, { ...volumeDefinition, altitudeFloorMeters: Number.NaN, }), /grid_altitude_floor_must_be_finite/, ); assert.equal( localVolumeAt({ eastMeters: 0, northMeters: 0, altitudeMeters: Number.NaN }, volumeDefinition), null, ); assert.throws( () => localVolumeId(volumeDefinition, 0, 0, -1), /grid_altitude_band_index_out_of_range/, ); assert.throws( () => localVolumeId(volumeDefinition, 0, 0, 3), /grid_altitude_band_index_out_of_range/, ); }); test("local grid plans are symmetric about the immutable origin and cap marker density", () => { const plan = localGridPlan({ stepMeters: 1_000, radiusMeters: 2_000, maximumMarkers: 4 }); assert.deepEqual(plan.lines.map(({ index, offsetMeters }) => [index, offsetMeters]), [ [-2, -2_000], [-1, -1_000], [0, 0], [1, 1_000], [2, 2_000], ]); assert.equal(plan.lines[0].extentMeters, 0); assert.equal(plan.lines[2].extentMeters, 2_000); assert.equal(plan.markerStride, 2); const bounded = localGridPlan({ stepMeters: 100, radiusMeters: 100_000_000 }); assert.equal(bounded.maximumIndex, MAX_LOCAL_GRID_INDEX); assert.equal(bounded.clipped, true); assert.ok(bounded.lines.length <= MAX_LOCAL_GRID_INDEX * 2 + 1); }); test("bounded RHUMB parts compile through Cesium at the equator, poles and date line", () => { const compile = (positions) => PolylineGeometry.createGeometry(new PolylineGeometry({ positions, width: 1, arcType: ArcType.RHUMB, granularity: CesiumMath.toRadians(2), })); for (const part of boundedAngularParts(-180, 180)) { assert.ok(compile([ Cartesian3.fromDegrees(part.start, 0, 500), Cartesian3.fromDegrees(part.end, 0, 500), ])); } for (const part of boundedAngularParts(-89.9, 89.9)) { assert.ok(compile([ Cartesian3.fromDegrees(180, part.start, 500), Cartesian3.fromDegrees(180, part.end, 500), ])); } }); test("ground graticule uses metre granularity and compiles without explosive subdivision", () => { const stepDegrees = 2; const granularity = graticuleGranularity(stepDegrees, true); assert.ok(granularity > 200_000 && granularity < 225_000); assert.equal(graticuleGranularity(stepDegrees, false), CesiumMath.toRadians(stepDegrees)); const positions = boundedAngularParts(-180, 180) .reduce((values, part, index) => [ ...values, ...(index === 0 ? [part.start] : []), part.end, ], []) .map((longitude) => Cartesian3.fromDegrees(longitude, 0)); const previousTerrainHeights = ApproximateTerrainHeights._terrainHeights; try { ApproximateTerrainHeights._terrainHeights = {}; const source = new GroundPolylineGeometry({ positions, width: 1, arcType: ArcType.RHUMB, granularity, }); assert.equal(source.granularity, granularity); assert.ok(GroundPolylineGeometry.createGeometry(source)); } finally { ApproximateTerrainHeights._terrainHeights = previousTerrainHeights; } }); test("anti-meridian ranges split explicitly while whole-world ranges keep one interval", () => { assert.deepEqual(splitLongitudeRange(170, -170), [ { west: 170, east: 180 }, { west: -180, east: -170 }, ]); assert.deepEqual(splitLongitudeRange(-170, 170), [{ west: -170, east: 170 }]); assert.deepEqual(splitLongitudeRange(-180, 180), [{ west: -180, east: 180 }]); assert.deepEqual(splitLongitudeRange(10, 370), [{ west: -180, east: 180 }]); }); test("graticule lines retain a global zero phase and never duplicate the date-line meridian", () => { const longitudeIntervals = splitLongitudeRange(170, -170); const plan = graticuleLinePlan({ south: -3.7, north: 3.7, longitudeIntervals, stepDegrees: 2, }); assert.deepEqual(plan.parallels, [-2, 0, 2]); assert.deepEqual(plan.meridians.map(({ longitude }) => longitude), [ 170, 172, 174, 176, 178, -180, -178, -176, -174, -172, -170, ]); assert.equal(new Set(plan.meridians.map(({ longitude }) => longitude)).size, plan.meridians.length); assert.deepEqual(alignedGridValues(1, 7, 2), [2, 4, 6]); const world = graticuleLinePlan({ south: -2, north: 2, longitudeIntervals: [{ west: -180, east: 180 }], stepDegrees: 2, }); assert.equal(world.meridians.some(({ longitude }) => longitude === 180), false); assert.equal(world.meridians.filter(({ longitude }) => longitude === -180).length, 1); }); test("graticule sector IDs, negative boundaries and bounds use the same global phase", () => { const definition = { lod: 4, stepDegrees: 2 }; const negative = graticuleSectorAt({ longitude: -0.0001, latitude: -0.0001 }, definition); const zero = graticuleSectorAt({ longitude: 0, latitude: 0 }, definition); const seamWest = graticuleSectorAt({ longitude: -180, latitude: 0 }, definition); const seamEast = graticuleSectorAt({ longitude: 180, latitude: 0 }, definition); assert.deepEqual([negative.longitudeIndex, negative.latitudeIndex], [-1, -1]); assert.equal(negative.id, "grid/wgs84/l4/s2.000000/x-1/y-1"); assert.equal(zero.id, "grid/wgs84/l4/s2.000000/x+0/y+0"); assert.deepEqual(graticuleSectorBounds(negative, definition.stepDegrees), { west: -2, east: 0, south: -2, north: 0, }); assert.equal(seamEast.id, seamWest.id); }); test("graticule minor neighbors wrap at the date line and stop at both poles", () => { const definition = { lod: 4, stepDegrees: 2 }; const northEast = graticuleSectorAt({ longitude: 179.999, latitude: 89.999 }, definition); const northEastNeighbors = graticuleSectorNeighbors(northEast, definition); assert.equal(northEastNeighbors.north, null); assert.deepEqual( [northEastNeighbors.east.longitudeIndex, northEastNeighbors.east.latitudeIndex], [-90, 44], ); assert.deepEqual( [northEastNeighbors.west.longitudeIndex, northEastNeighbors.west.latitudeIndex], [88, 44], ); const southWest = graticuleSectorAt({ longitude: -180, latitude: -90 }, definition); const southWestNeighbors = graticuleSectorNeighbors(southWest, definition); assert.equal(southWestNeighbors.south, null); assert.equal(southWestNeighbors.west.longitudeIndex, 89); const unevenDefinition = { lod: 4, stepDegrees: 7 }; const unevenWest = graticuleSectorAt({ longitude: -180, latitude: 0 }, unevenDefinition); const unevenNeighbors = graticuleSectorNeighbors(unevenWest, unevenDefinition); assert.equal(unevenNeighbors.west.longitudeIndex, 25); assert.throws( () => graticuleSectorNeighbors({ ...unevenWest, latitudeIndex: 13 }, unevenDefinition), /grid_graticule_latitude_index_out_of_range/, ); }); test("graticule major hierarchy has stable IDs, exact children and seam-safe neighbors", () => { const definition = { lod: 4, stepDegrees: 2, majorStepDegrees: 10 }; const tile = graticuleMajorTileAt({ longitude: -0.001, latitude: 9.999 }, definition); assert.equal(tile.id, "grid/wgs84/l4/s2.000000/m10.000000/x-1/y+0"); assert.equal(tile.minorPerSide, 5); assert.deepEqual(graticuleMajorTileBounds(tile, definition.majorStepDegrees), { west: -10, east: 0, south: 0, north: 10, }); const children = graticuleMajorTileChildren(tile, definition); assert.equal(children.length, 25); assert.deepEqual( [children[0].longitudeIndex, children[0].latitudeIndex, children.at(-1).longitudeIndex, children.at(-1).latitudeIndex], [-5, 0, -1, 4], ); for (const child of children) { assert.equal(graticuleMajorTileForSector(child, definition).id, tile.id); } const seam = graticuleMajorTileAt({ longitude: 179.999, latitude: 89.999 }, definition); const seamNeighbors = graticuleMajorTileNeighbors(seam, definition); assert.equal(seam.longitudeIndex, 17); assert.equal(seam.latitudeIndex, 8); assert.equal(seamNeighbors.north, null); assert.equal(seamNeighbors.east.longitudeIndex, -18); assert.equal(graticuleMajorTileAt({ longitude: 180, latitude: 0 }, definition).longitudeIndex, -18); assert.equal(graticuleMajorTileAt({ longitude: -180, latitude: 0 }, definition).id, graticuleMajorTileAt({ longitude: 180, latitude: 0 }, definition).id); assert.equal( graticuleMajorTileAt({ longitude: -180, latitude: 0 }, definition).id, graticuleMajorTileAt({ longitude: 540, latitude: 0 }, definition).id, ); assert.equal(isGraticuleMajorLineIndex(-5, definition), true); assert.equal(isGraticuleMajorLineIndex(-4, definition), false); assert.equal(isGraticuleMajorLineValue(-10, definition), true); assert.equal(isGraticuleMajorLineValue(-9.999, definition), false); }); test("large graticule major tiles expose children through bounded pages", () => { const definition = { lod: 4, stepDegrees: 0.1, majorStepDegrees: 90 }; const tile = graticuleMajorTileAt({ longitude: 0, latitude: 0 }, definition); assert.equal(tile.minorPerSide, 900); assert.throws( () => graticuleMajorTileChildren(tile, definition), /grid_child_page_limit_exceeded/, ); const tail = graticuleMajorTileChildren(tile, definition, { offset: 809_999, limit: 1 }); assert.deepEqual( [tail[0].longitudeIndex, tail[0].latitudeIndex], [899, 899], ); }); test("graticule hierarchy rejects ambiguous global partitions and malformed indices", () => { assert.throws( () => graticuleMajorTileAt({ longitude: 0, latitude: 0 }, { lod: 4, stepDegrees: 2, majorStepDegrees: 5, }), /grid_graticule_major_step_must_be_integer_multiple_of_step/, ); assert.throws( () => graticuleMajorTileAt({ longitude: 0, latitude: 0 }, { lod: 4, stepDegrees: 1, majorStepDegrees: 7, }), /grid_graticule_major_step_must_partition_hemisphere/, ); assert.throws( () => graticuleMajorTileAt({ longitude: 0, latitude: 0 }, { lod: 4, stepDegrees: 1, majorStepDegrees: 0, }), /grid_graticule_major_step_must_be_positive/, ); assert.throws( () => graticuleMajorTileBounds({ longitudeIndex: 0, latitudeIndex: 9.5 }, 10), /grid_index_must_be_safe_integer/, ); }); test("WGS84 summaries expose ellipsoidal area, centers, neighbors and major parents", () => { const definition = { lod: 4, stepDegrees: 2, majorStepDegrees: 10 }; const sector = graticuleSectorAt({ longitude: -0.001, latitude: -0.001 }, definition); const summary = graticuleSectorSummary(sector, definition); assert.deepEqual(summary.center, { longitude: -1, latitude: -1 }); assert.equal(summary.address.id, sector.id); assert.equal(summary.neighbors.east.address.id, graticuleSectorAt({ longitude: 0, latitude: -0.001 }, definition).id); assert.equal(summary.majorTile?.id, summary.parentMajorTile?.id); assert.deepEqual(summary.majorTile?.bounds, { west: -10, east: 0, south: -10, north: 0 }); assert.equal(summary.majorTile?.childCount, 25); assert.ok(summary.areaSquareMeters > 49_000_000_000 && summary.areaSquareMeters < 50_000_000_000); const equator = graticuleSectorAt({ longitude: 0, latitude: 0 }, definition); const polar = graticuleSectorAt({ longitude: 0, latitude: 89 }, definition); assert.ok( graticuleSectorAreaSquareMeters(equator, definition.stepDegrees) > graticuleSectorAreaSquareMeters(polar, definition.stepDegrees) * 20, ); const worldArea = geodeticRectangleAreaSquareMeters({ west: -180, east: 180, south: -90, north: 90 }); assert.ok(worldArea > 5.10e14 && worldArea < 5.11e14); const majorSummary = graticuleMajorTileSummary(summary.majorTile.address, definition); assert.equal(majorSummary.neighbors.east.address.longitudeIndex, 0); assert.equal(graticuleSectorSummary(sector, { lod: 4, stepDegrees: 2 }).majorTile, null); });