11import { describe , it , expect } from 'vitest'
2- import { lonLatToTile , type BoundingRegion , degToRad } from './utils'
2+ import { lonLatToTile , tileToRegion , type BoundingRegion , degToRad } from './utils'
33
44describe ( 'lonLatToTile' , ( ) => {
55 // Define a test root region covering a 2x2 degree area in radians
@@ -194,4 +194,252 @@ describe('lonLatToTile', () => {
194194 } ) . toThrow ( RangeError )
195195 } )
196196 } )
197+ } )
198+
199+ describe ( 'tileToRegion' , ( ) => {
200+ // Define a test root region covering a 2x2 degree area in radians
201+ const testRoot : BoundingRegion = {
202+ west : degToRad ( - 1 ) , // -1 degree
203+ south : degToRad ( - 1 ) , // -1 degree
204+ east : degToRad ( 1 ) , // 1 degree
205+ north : degToRad ( 1 ) , // 1 degree
206+ minH : 0 ,
207+ maxH : 100
208+ }
209+
210+ describe ( 'level 0 (root tile)' , ( ) => {
211+ it ( 'should return the exact root region for tile (0,0)' , ( ) => {
212+ const result = tileToRegion ( testRoot , 0 , 0 , 0 )
213+
214+ expect ( result . west ) . toBeCloseTo ( testRoot . west )
215+ expect ( result . south ) . toBeCloseTo ( testRoot . south )
216+ expect ( result . east ) . toBeCloseTo ( testRoot . east )
217+ expect ( result . north ) . toBeCloseTo ( testRoot . north )
218+ expect ( result . minH ) . toBe ( testRoot . minH )
219+ expect ( result . maxH ) . toBe ( testRoot . maxH )
220+ } )
221+ } )
222+
223+ describe ( 'level 1 (2x2 grid)' , ( ) => {
224+ it ( 'should return correct region for bottom-left tile (0,0)' , ( ) => {
225+ const result = tileToRegion ( testRoot , 1 , 0 , 0 )
226+
227+ expect ( result . west ) . toBeCloseTo ( degToRad ( - 1 ) )
228+ expect ( result . south ) . toBeCloseTo ( degToRad ( - 1 ) )
229+ expect ( result . east ) . toBeCloseTo ( degToRad ( 0 ) )
230+ expect ( result . north ) . toBeCloseTo ( degToRad ( 0 ) )
231+ expect ( result . minH ) . toBe ( 0 )
232+ expect ( result . maxH ) . toBe ( 100 )
233+ } )
234+
235+ it ( 'should return correct region for bottom-right tile (1,0)' , ( ) => {
236+ const result = tileToRegion ( testRoot , 1 , 1 , 0 )
237+
238+ expect ( result . west ) . toBeCloseTo ( degToRad ( 0 ) )
239+ expect ( result . south ) . toBeCloseTo ( degToRad ( - 1 ) )
240+ expect ( result . east ) . toBeCloseTo ( degToRad ( 1 ) )
241+ expect ( result . north ) . toBeCloseTo ( degToRad ( 0 ) )
242+ expect ( result . minH ) . toBe ( 0 )
243+ expect ( result . maxH ) . toBe ( 100 )
244+ } )
245+
246+ it ( 'should return correct region for top-left tile (0,1)' , ( ) => {
247+ const result = tileToRegion ( testRoot , 1 , 0 , 1 )
248+
249+ expect ( result . west ) . toBeCloseTo ( degToRad ( - 1 ) )
250+ expect ( result . south ) . toBeCloseTo ( degToRad ( 0 ) )
251+ expect ( result . east ) . toBeCloseTo ( degToRad ( 0 ) )
252+ expect ( result . north ) . toBeCloseTo ( degToRad ( 1 ) )
253+ expect ( result . minH ) . toBe ( 0 )
254+ expect ( result . maxH ) . toBe ( 100 )
255+ } )
256+
257+ it ( 'should return correct region for top-right tile (1,1)' , ( ) => {
258+ const result = tileToRegion ( testRoot , 1 , 1 , 1 )
259+
260+ expect ( result . west ) . toBeCloseTo ( degToRad ( 0 ) )
261+ expect ( result . south ) . toBeCloseTo ( degToRad ( 0 ) )
262+ expect ( result . east ) . toBeCloseTo ( degToRad ( 1 ) )
263+ expect ( result . north ) . toBeCloseTo ( degToRad ( 1 ) )
264+ expect ( result . minH ) . toBe ( 0 )
265+ expect ( result . maxH ) . toBe ( 100 )
266+ } )
267+ } )
268+
269+ describe ( 'level 2 (4x4 grid)' , ( ) => {
270+ it ( 'should return correct region for first tile (0,0)' , ( ) => {
271+ const result = tileToRegion ( testRoot , 2 , 0 , 0 )
272+
273+ expect ( result . west ) . toBeCloseTo ( degToRad ( - 1 ) )
274+ expect ( result . south ) . toBeCloseTo ( degToRad ( - 1 ) )
275+ expect ( result . east ) . toBeCloseTo ( degToRad ( - 0.5 ) )
276+ expect ( result . north ) . toBeCloseTo ( degToRad ( - 0.5 ) )
277+ expect ( result . minH ) . toBe ( 0 )
278+ expect ( result . maxH ) . toBe ( 100 )
279+ } )
280+
281+ it ( 'should return correct region for last tile (3,3)' , ( ) => {
282+ const result = tileToRegion ( testRoot , 2 , 3 , 3 )
283+
284+ expect ( result . west ) . toBeCloseTo ( degToRad ( 0.5 ) )
285+ expect ( result . south ) . toBeCloseTo ( degToRad ( 0.5 ) )
286+ expect ( result . east ) . toBeCloseTo ( degToRad ( 1 ) )
287+ expect ( result . north ) . toBeCloseTo ( degToRad ( 1 ) )
288+ expect ( result . minH ) . toBe ( 0 )
289+ expect ( result . maxH ) . toBe ( 100 )
290+ } )
291+
292+ it ( 'should return correct region for center tile (1,1)' , ( ) => {
293+ const result = tileToRegion ( testRoot , 2 , 1 , 1 )
294+
295+ expect ( result . west ) . toBeCloseTo ( degToRad ( - 0.5 ) )
296+ expect ( result . south ) . toBeCloseTo ( degToRad ( - 0.5 ) )
297+ expect ( result . east ) . toBeCloseTo ( degToRad ( 0 ) )
298+ expect ( result . north ) . toBeCloseTo ( degToRad ( 0 ) )
299+ expect ( result . minH ) . toBe ( 0 )
300+ expect ( result . maxH ) . toBe ( 100 )
301+ } )
302+ } )
303+
304+ describe ( 'height splitting (splitH parameter)' , ( ) => {
305+ const rootWithHeight : BoundingRegion = {
306+ west : degToRad ( 0 ) ,
307+ south : degToRad ( 0 ) ,
308+ east : degToRad ( 2 ) ,
309+ north : degToRad ( 2 ) ,
310+ minH : 0 ,
311+ maxH : 400
312+ }
313+
314+ it ( 'should keep original height when splitH is false' , ( ) => {
315+ const result = tileToRegion ( rootWithHeight , 2 , 1 , 1 , false )
316+
317+ expect ( result . minH ) . toBe ( 0 )
318+ expect ( result . maxH ) . toBe ( 400 )
319+ } )
320+
321+ it ( 'should split height at level 1 when splitH is true' , ( ) => {
322+ // At level 1, height should be split in half
323+ const resultBottomLeft = tileToRegion ( rootWithHeight , 1 , 0 , 0 , true )
324+ const resultTopRight = tileToRegion ( rootWithHeight , 1 , 1 , 1 , true )
325+
326+ // Bottom tile (y=0) should get lower half of height
327+ expect ( resultBottomLeft . minH ) . toBe ( 0 )
328+ expect ( resultBottomLeft . maxH ) . toBe ( 200 )
329+
330+ // Top tile (y=1) should get upper half of height
331+ expect ( resultTopRight . minH ) . toBe ( 200 )
332+ expect ( resultTopRight . maxH ) . toBe ( 400 )
333+ } )
334+
335+ it ( 'should split height at level 2 when splitH is true' , ( ) => {
336+ // At level 2, height should be split into 4 parts
337+ const result00 = tileToRegion ( rootWithHeight , 2 , 0 , 0 , true )
338+ const result01 = tileToRegion ( rootWithHeight , 2 , 0 , 1 , true )
339+ const result02 = tileToRegion ( rootWithHeight , 2 , 0 , 2 , true )
340+ const result03 = tileToRegion ( rootWithHeight , 2 , 0 , 3 , true )
341+
342+ expect ( result00 . minH ) . toBe ( 0 )
343+ expect ( result00 . maxH ) . toBe ( 100 )
344+
345+ expect ( result01 . minH ) . toBe ( 100 )
346+ expect ( result01 . maxH ) . toBe ( 200 )
347+
348+ expect ( result02 . minH ) . toBe ( 200 )
349+ expect ( result02 . maxH ) . toBe ( 300 )
350+
351+ expect ( result03 . minH ) . toBe ( 300 )
352+ expect ( result03 . maxH ) . toBe ( 400 )
353+ } )
354+
355+ it ( 'should not split height at level 0 even when splitH is true' , ( ) => {
356+ const result = tileToRegion ( rootWithHeight , 0 , 0 , 0 , true )
357+
358+ expect ( result . minH ) . toBe ( 0 )
359+ expect ( result . maxH ) . toBe ( 400 )
360+ } )
361+ } )
362+
363+ describe ( 'tile coverage verification' , ( ) => {
364+ it ( 'should ensure all level 1 tiles cover the entire root region' , ( ) => {
365+ const tiles = [
366+ tileToRegion ( testRoot , 1 , 0 , 0 ) ,
367+ tileToRegion ( testRoot , 1 , 1 , 0 ) ,
368+ tileToRegion ( testRoot , 1 , 0 , 1 ) ,
369+ tileToRegion ( testRoot , 1 , 1 , 1 )
370+ ]
371+
372+ // Check that tiles don't overlap and cover the entire area
373+ expect ( tiles [ 0 ] . east ) . toBeCloseTo ( tiles [ 1 ] . west ) // Bottom tiles connect
374+ expect ( tiles [ 0 ] . north ) . toBeCloseTo ( tiles [ 2 ] . south ) // Left tiles connect
375+ expect ( tiles [ 1 ] . north ) . toBeCloseTo ( tiles [ 3 ] . south ) // Right tiles connect
376+ expect ( tiles [ 2 ] . east ) . toBeCloseTo ( tiles [ 3 ] . west ) // Top tiles connect
377+
378+ // Check coverage of root bounds
379+ expect ( Math . min ( tiles [ 0 ] . west , tiles [ 2 ] . west ) ) . toBeCloseTo ( testRoot . west )
380+ expect ( Math . min ( tiles [ 0 ] . south , tiles [ 1 ] . south ) ) . toBeCloseTo ( testRoot . south )
381+ expect ( Math . max ( tiles [ 1 ] . east , tiles [ 3 ] . east ) ) . toBeCloseTo ( testRoot . east )
382+ expect ( Math . max ( tiles [ 2 ] . north , tiles [ 3 ] . north ) ) . toBeCloseTo ( testRoot . north )
383+ } )
384+ } )
385+
386+ describe ( 'real-world coordinate systems' , ( ) => {
387+ const lausanneRegion : BoundingRegion = {
388+ west : degToRad ( 6.5248166 ) ,
389+ south : degToRad ( 46.4976347 ) ,
390+ east : degToRad ( 6.6700634 ) ,
391+ north : degToRad ( 46.6156402 ) ,
392+ minH : 0 ,
393+ maxH : 1000
394+ }
395+
396+ it ( 'should handle realistic geographic coordinates for Lausanne' , ( ) => {
397+ const result = tileToRegion ( lausanneRegion , 3 , 2 , 4 )
398+
399+ // Verify the result is within the original bounds
400+ expect ( result . west ) . toBeGreaterThanOrEqual ( lausanneRegion . west )
401+ expect ( result . south ) . toBeGreaterThanOrEqual ( lausanneRegion . south )
402+ expect ( result . east ) . toBeLessThanOrEqual ( lausanneRegion . east )
403+ expect ( result . north ) . toBeLessThanOrEqual ( lausanneRegion . north )
404+
405+ // Verify it's a proper sub-region
406+ expect ( result . west ) . toBeLessThan ( result . east )
407+ expect ( result . south ) . toBeLessThan ( result . north )
408+ expect ( result . minH ) . toBe ( lausanneRegion . minH )
409+ expect ( result . maxH ) . toBe ( lausanneRegion . maxH )
410+ } )
411+
412+ it ( 'should create consistent subdivisions for Lausanne dataset' , ( ) => {
413+ // Test that adjacent tiles share boundaries
414+ const tile00 = tileToRegion ( lausanneRegion , 2 , 0 , 0 )
415+ const tile10 = tileToRegion ( lausanneRegion , 2 , 1 , 0 )
416+ const tile01 = tileToRegion ( lausanneRegion , 2 , 0 , 1 )
417+
418+ expect ( tile00 . east ) . toBeCloseTo ( tile10 . west )
419+ expect ( tile00 . north ) . toBeCloseTo ( tile01 . south )
420+ } )
421+ } )
422+
423+ describe ( 'mathematical precision' , ( ) => {
424+ it ( 'should maintain precision with floating point arithmetic' , ( ) => {
425+ const preciseRoot : BoundingRegion = {
426+ west : 0.123456789 ,
427+ south : 0.987654321 ,
428+ east : 1.123456789 ,
429+ north : 1.987654321 ,
430+ minH : 123.456 ,
431+ maxH : 987.654
432+ }
433+
434+ const result = tileToRegion ( preciseRoot , 4 , 5 , 7 )
435+
436+ // Verify the result maintains reasonable precision
437+ expect ( result . west ) . toBeLessThan ( result . east )
438+ expect ( result . south ) . toBeLessThan ( result . north )
439+ expect ( result . west ) . toBeGreaterThanOrEqual ( preciseRoot . west )
440+ expect ( result . east ) . toBeLessThanOrEqual ( preciseRoot . east )
441+ expect ( result . south ) . toBeGreaterThanOrEqual ( preciseRoot . south )
442+ expect ( result . north ) . toBeLessThanOrEqual ( preciseRoot . north )
443+ } )
444+ } )
197445} )
0 commit comments