1- import { describe , it , expect } from 'vitest'
2- import { cellToBoundary , cellToLonLat , lonLatToCell } from 'a5/core/cell'
1+ import { describe , it , expect } from 'vitest' ;
2+ import type { Degrees , LonLat } from "a5/core/coordinate-systems" ;
3+ import { cellToBoundary , cellToLonLat , lonLatToCell , a5cellContainsPoint } from 'a5/core/cell'
4+ import { deserialize , MAX_RESOLUTION } from 'a5/core/serialization' ;
35
4- describe ( 'cell' , ( ) => {
5- it ( 'passes' , ( ) => { } )
6- } )
6+ interface GeoJSONFeature {
7+ type : 'Feature' ;
8+ properties : {
9+ resolution : number ;
10+ } ;
11+ geometry : {
12+ type : 'Polygon' ;
13+ coordinates : number [ ] [ ] [ ] ;
14+ } ;
15+ }
16+
17+ interface GeoJSONFeatureCollection {
18+ type : 'FeatureCollection' ;
19+ features : GeoJSONFeature [ ] ;
20+ }
21+
22+ function boundaryToGeoJSON ( boundary : LonLat [ ] , resolution : number ) : GeoJSONFeatureCollection {
23+ // Create coordinates list with first point appended at the end to close the polygon
24+ const coordinates = boundary . map ( ( [ lon , lat ] ) => [ lon , lat ] ) ;
25+ if ( coordinates . length > 0 ) {
26+ coordinates . push ( coordinates [ 0 ] ) ; // Close the polygon
27+ }
28+
29+ // Create a polygon feature
30+ const feature : GeoJSONFeature = {
31+ type : 'Feature' ,
32+ properties : {
33+ resolution
34+ } ,
35+ geometry : {
36+ type : 'Polygon' ,
37+ coordinates : [ coordinates ] // Wrap in list as per GeoJSON spec
38+ }
39+ } ;
40+
41+ // Create a feature collection
42+ const featureCollection : GeoJSONFeatureCollection = {
43+ type : 'FeatureCollection' ,
44+ features : [ feature ]
45+ } ;
46+
47+ return featureCollection ;
48+ }
49+
50+ describe ( 'Cell Boundary Tests' , ( ) => {
51+ it ( 'should contain point and have valid boundaries for all resolutions' , ( ) => {
52+ // Test coordinates
53+ const testLonlat = [ 106.706360 as Degrees , 10.775305 as Degrees ] as LonLat ;
54+
55+ // Dictionary to store failures for each resolution
56+ const failures : Record < number , string [ ] > = { } ;
57+ // Test resolutions from 0 to MAX_RESOLUTION
58+ for ( let resolution = 1 ; resolution <= MAX_RESOLUTION ; resolution ++ ) {
59+ const resolutionFailures : string [ ] = [ ] ;
60+
61+ try {
62+ // Get cell ID for the coordinates
63+ const cellId = lonLatToCell ( testLonlat , resolution ) ;
64+
65+ // Get cell boundary
66+ const boundary = cellToBoundary ( cellId ) ;
67+
68+ // Convert boundary to GeoJSON and print it
69+ const geojson = boundaryToGeoJSON ( boundary , resolution ) ;
70+ console . log ( `\nResolution ${ resolution } GeoJSON:\n` , JSON . stringify ( geojson ) ) ;
71+
72+ // Verify the original point is contained within the cell
73+ const cell = deserialize ( cellId ) ;
74+ if ( ! a5cellContainsPoint ( cell , testLonlat ) ) {
75+ resolutionFailures . push ( `Cell does not contain the original point ${ testLonlat } ` ) ;
76+ // Add cell center for reference
77+ const center = cellToLonLat ( cellId ) ;
78+ resolutionFailures . push ( `Cell center is at ${ center } ` ) ;
79+ }
80+
81+ // Verify boundary points are valid coordinates
82+ boundary . forEach ( ( point , i ) => {
83+ if ( ! Array . isArray ( point ) ) {
84+ resolutionFailures . push ( `Boundary point ${ i } is not an array, got ${ typeof point } ` ) ;
85+ } else if ( point . length !== 2 ) {
86+ resolutionFailures . push ( `Boundary point ${ i } should have 2 coordinates, got ${ point . length } ` ) ;
87+ } else {
88+ const [ lon , lat ] = point ;
89+ if ( lon < - 180 || lon > 180 ) {
90+ resolutionFailures . push ( `Boundary point ${ i } has invalid longitude: ${ lon } ` ) ;
91+ }
92+ if ( lat < - 90 || lat > 90 ) {
93+ resolutionFailures . push ( `Boundary point ${ i } has invalid latitude: ${ lat } ` ) ;
94+ }
95+ }
96+ } ) ;
97+
98+ } catch ( e ) {
99+ resolutionFailures . push ( `Unexpected error: ${ e instanceof Error ? e . message : String ( e ) } ` ) ;
100+ if ( e instanceof Error && e . stack ) {
101+ resolutionFailures . push ( `Traceback: ${ e . stack } ` ) ;
102+ }
103+ }
104+
105+ // Store failures for this resolution if any occurred
106+ if ( resolutionFailures . length > 0 ) {
107+ failures [ resolution ] = resolutionFailures ;
108+ }
109+ }
110+
111+ // Report all failures
112+ if ( Object . keys ( failures ) . length > 0 ) {
113+ let failureMessage = '\nFailures by resolution:\n' ;
114+ for ( const [ resolution , resolutionFailures ] of Object . entries ( failures ) ) {
115+ failureMessage += `\nResolution ${ resolution } :\n` ;
116+ for ( const failure of resolutionFailures ) {
117+ failureMessage += ` - ${ failure } \n` ;
118+ }
119+ }
120+ throw new Error ( failureMessage ) ;
121+ }
122+ } ) ;
123+ } ) ;
0 commit comments