1+ import { describe , it , expect } from 'vitest'
2+ import { lonLatToTile , type BoundingRegion , degToRad } from './utils'
3+
4+ describe ( 'lonLatToTile' , ( ) => {
5+ // Define a test root region covering a 2x2 degree area in radians
6+ const testRoot : BoundingRegion = {
7+ west : degToRad ( - 1 ) , // -1 degree
8+ south : degToRad ( - 1 ) , // -1 degree
9+ east : degToRad ( 1 ) , // 1 degree
10+ north : degToRad ( 1 ) , // 1 degree
11+ minH : 0 ,
12+ maxH : 100
13+ }
14+
15+ describe ( 'level 0 (single tile)' , ( ) => {
16+ it ( 'should return {x: 0, y: 0} for any point inside the root region' , ( ) => {
17+ expect ( lonLatToTile ( testRoot , 0 , degToRad ( 0 ) , degToRad ( 0 ) ) ) . toEqual ( { x : 0 , y : 0 } )
18+ expect ( lonLatToTile ( testRoot , 0 , degToRad ( - 0.5 ) , degToRad ( 0.5 ) ) ) . toEqual ( { x : 0 , y : 0 } )
19+ expect ( lonLatToTile ( testRoot , 0 , degToRad ( 0.9 ) , degToRad ( - 0.9 ) ) ) . toEqual ( { x : 0 , y : 0 } )
20+ } )
21+ } )
22+
23+ describe ( 'level 1 (2x2 grid)' , ( ) => {
24+ it ( 'should return correct tile coordinates for each quadrant' , ( ) => {
25+ // Bottom-left quadrant (x=0, y=0)
26+ expect ( lonLatToTile ( testRoot , 1 , degToRad ( - 0.5 ) , degToRad ( - 0.5 ) ) ) . toEqual ( { x : 0 , y : 0 } )
27+
28+ // Bottom-right quadrant (x=1, y=0)
29+ expect ( lonLatToTile ( testRoot , 1 , degToRad ( 0.5 ) , degToRad ( - 0.5 ) ) ) . toEqual ( { x : 1 , y : 0 } )
30+
31+ // Top-left quadrant (x=0, y=1)
32+ expect ( lonLatToTile ( testRoot , 1 , degToRad ( - 0.5 ) , degToRad ( 0.5 ) ) ) . toEqual ( { x : 0 , y : 1 } )
33+
34+ // Top-right quadrant (x=1, y=1)
35+ expect ( lonLatToTile ( testRoot , 1 , degToRad ( 0.5 ) , degToRad ( 0.5 ) ) ) . toEqual ( { x : 1 , y : 1 } )
36+ } )
37+
38+ it ( 'should handle coordinates at tile boundaries correctly' , ( ) => {
39+ // Point exactly at the center should go to bottom-left tile
40+ expect ( lonLatToTile ( testRoot , 1 , degToRad ( 0 ) , degToRad ( 0 ) ) ) . toEqual ( { x : 1 , y : 1 } )
41+
42+ // Point at west boundary should go to left tiles
43+ expect ( lonLatToTile ( testRoot , 1 , degToRad ( - 1 ) , degToRad ( - 0.5 ) ) ) . toEqual ( { x : 0 , y : 0 } )
44+ expect ( lonLatToTile ( testRoot , 1 , degToRad ( - 1 ) , degToRad ( 0.5 ) ) ) . toEqual ( { x : 0 , y : 1 } )
45+ } )
46+ } )
47+
48+ describe ( 'level 2 (4x4 grid)' , ( ) => {
49+ it ( 'should return correct tile coordinates for higher subdivision' , ( ) => {
50+ // Test a point in the first tile (0,0)
51+ expect ( lonLatToTile ( testRoot , 2 , degToRad ( - 0.75 ) , degToRad ( - 0.75 ) ) ) . toEqual ( { x : 0 , y : 0 } )
52+
53+ // Test a point in the last tile (3,3)
54+ expect ( lonLatToTile ( testRoot , 2 , degToRad ( 0.75 ) , degToRad ( 0.75 ) ) ) . toEqual ( { x : 3 , y : 3 } )
55+
56+ // Test center point
57+ expect ( lonLatToTile ( testRoot , 2 , degToRad ( 0 ) , degToRad ( 0 ) ) ) . toEqual ( { x : 2 , y : 2 } )
58+ } )
59+ } )
60+
61+ describe ( 'boundary conditions' , ( ) => {
62+ it ( 'should handle coordinates at exact region boundaries' , ( ) => {
63+ // Points at the exact boundaries of the root region
64+ expect ( lonLatToTile ( testRoot , 1 , testRoot . west , testRoot . south ) ) . toEqual ( { x : 0 , y : 0 } )
65+ expect ( lonLatToTile ( testRoot , 1 , testRoot . east , testRoot . north ) ) . toEqual ( { x : 1 , y : 1 } )
66+
67+ // Points very close to but within boundaries
68+ const epsilon = 1e-10
69+ expect ( lonLatToTile ( testRoot , 1 , testRoot . west + epsilon , testRoot . south + epsilon ) ) . toEqual ( { x : 0 , y : 0 } )
70+ } )
71+
72+ it ( 'should clamp to maximum tile indices for edge coordinates' , ( ) => {
73+ // At level 2, max indices should be 3,3
74+ expect ( lonLatToTile ( testRoot , 2 , testRoot . east , testRoot . north ) ) . toEqual ( { x : 3 , y : 3 } )
75+ } )
76+ } )
77+
78+ describe ( 'error conditions' , ( ) => {
79+ it ( 'should throw RangeError for coordinates outside west boundary' , ( ) => {
80+ expect ( ( ) => {
81+ lonLatToTile ( testRoot , 1 , degToRad ( - 1.1 ) , degToRad ( 0 ) )
82+ } ) . toThrow ( RangeError )
83+ expect ( ( ) => {
84+ lonLatToTile ( testRoot , 1 , degToRad ( - 1.1 ) , degToRad ( 0 ) )
85+ } ) . toThrow ( 'Position is outside the root bounding region' )
86+ } )
87+
88+ it ( 'should throw RangeError for coordinates outside east boundary' , ( ) => {
89+ expect ( ( ) => {
90+ lonLatToTile ( testRoot , 1 , degToRad ( 1.1 ) , degToRad ( 0 ) )
91+ } ) . toThrow ( RangeError )
92+ } )
93+
94+ it ( 'should throw RangeError for coordinates outside south boundary' , ( ) => {
95+ expect ( ( ) => {
96+ lonLatToTile ( testRoot , 1 , degToRad ( 0 ) , degToRad ( - 1.1 ) )
97+ } ) . toThrow ( RangeError )
98+ } )
99+
100+ it ( 'should throw RangeError for coordinates outside north boundary' , ( ) => {
101+ expect ( ( ) => {
102+ lonLatToTile ( testRoot , 1 , degToRad ( 0 ) , degToRad ( 1.1 ) )
103+ } ) . toThrow ( RangeError )
104+ } )
105+ } )
106+
107+ describe ( 'real-world coordinate system' , ( ) => {
108+ // Test with a more realistic bounding region (Switzerland area)
109+ const swissRegion : BoundingRegion = {
110+ west : degToRad ( 5.96 ) , // Western Switzerland
111+ south : degToRad ( 45.82 ) , // Southern Switzerland
112+ east : degToRad ( 10.49 ) , // Eastern Switzerland
113+ north : degToRad ( 47.81 ) , // Northern Switzerland
114+ minH : 0 ,
115+ maxH : 4000
116+ }
117+
118+ it ( 'should handle realistic geographic coordinates' , ( ) => {
119+ // Test Bern coordinates (approximately 7.45°E, 46.95°N)
120+ const bernLon = degToRad ( 7.45 )
121+ const bernLat = degToRad ( 46.95 )
122+
123+ const result = lonLatToTile ( swissRegion , 3 , bernLon , bernLat )
124+
125+ // Verify the result is within expected bounds for level 3 (8x8 grid)
126+ expect ( result . x ) . toBeGreaterThanOrEqual ( 0 )
127+ expect ( result . x ) . toBeLessThanOrEqual ( 7 )
128+ expect ( result . y ) . toBeGreaterThanOrEqual ( 0 )
129+ expect ( result . y ) . toBeLessThanOrEqual ( 7 )
130+
131+ // Verify it's roughly in the center of Switzerland (should be around middle tiles)
132+ // Bern is roughly in the center-west of Switzerland, so expect x around 2-3 and y around 4-5
133+ expect ( result . x ) . toBeGreaterThanOrEqual ( 2 )
134+ expect ( result . x ) . toBeLessThanOrEqual ( 4 )
135+ expect ( result . y ) . toBeGreaterThanOrEqual ( 3 )
136+ expect ( result . y ) . toBeLessThanOrEqual ( 6 )
137+ } )
138+
139+ it ( 'should handle Lausanne dataset coordinates' , ( ) => {
140+ // Lausanne dataset bounds
141+ const lausanneRegion : BoundingRegion = {
142+ west : degToRad ( 6.5248166 ) , // Western Lausanne
143+ south : degToRad ( 46.4976347 ) , // Southern Lausanne
144+ east : degToRad ( 6.6700634 ) , // Eastern Lausanne
145+ north : degToRad ( 46.6156402 ) , // Northern Lausanne
146+ minH : 0 ,
147+ maxH : 1000
148+ }
149+
150+ // Test coordinates in central Lausanne (approximately 6.63°E, 46.52°N)
151+ const lausanneLon = degToRad ( 6.63 )
152+ const lausanneLat = degToRad ( 46.52 )
153+
154+ const result = lonLatToTile ( lausanneRegion , 4 , lausanneLon , lausanneLat )
155+
156+ // Verify the result is within expected bounds for level 4 (16x16 grid)
157+ expect ( result . x ) . toBeGreaterThanOrEqual ( 0 )
158+ expect ( result . x ) . toBeLessThanOrEqual ( 15 )
159+ expect ( result . y ) . toBeGreaterThanOrEqual ( 0 )
160+ expect ( result . y ) . toBeLessThanOrEqual ( 15 )
161+
162+ // For a small region like Lausanne, the coordinates should be in the eastern part
163+ // since 6.63 is closer to the eastern bound (6.67) than western (6.52)
164+ expect ( result . x ) . toBeGreaterThan ( 8 ) // Should be in the eastern half
165+ expect ( result . y ) . toBeGreaterThanOrEqual ( 0 ) // Should be in the valid range
166+ } )
167+
168+ it ( 'should handle coordinates at Lausanne dataset boundaries' , ( ) => {
169+ const lausanneRegion : BoundingRegion = {
170+ west : degToRad ( 6.5248166 ) ,
171+ south : degToRad ( 46.4976347 ) ,
172+ east : degToRad ( 6.6700634 ) ,
173+ north : degToRad ( 46.6156402 ) ,
174+ minH : 0 ,
175+ maxH : 1000
176+ }
177+
178+ // Test boundary coordinates
179+ expect ( ( ) => {
180+ lonLatToTile ( lausanneRegion , 2 , lausanneRegion . west , lausanneRegion . south )
181+ } ) . not . toThrow ( )
182+
183+ expect ( ( ) => {
184+ lonLatToTile ( lausanneRegion , 2 , lausanneRegion . east , lausanneRegion . north )
185+ } ) . not . toThrow ( )
186+
187+ // Test coordinates just outside the boundaries
188+ expect ( ( ) => {
189+ lonLatToTile ( lausanneRegion , 2 , degToRad ( 6.5 ) , degToRad ( 46.5 ) )
190+ } ) . toThrow ( RangeError )
191+
192+ expect ( ( ) => {
193+ lonLatToTile ( lausanneRegion , 2 , degToRad ( 6.68 ) , degToRad ( 46.62 ) )
194+ } ) . toThrow ( RangeError )
195+ } )
196+ } )
197+ } )
0 commit comments