Skip to content

Commit 8a65262

Browse files
committed
Add tests for tileToRegion function in utils.test.ts, covering various levels and height splitting scenarios.
1 parent 5cbdca1 commit 8a65262

File tree

1 file changed

+249
-1
lines changed

1 file changed

+249
-1
lines changed

apps/api/src/utils/utils.test.ts

Lines changed: 249 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, it, expect } from 'vitest'
2-
import { lonLatToTile, type BoundingRegion, degToRad } from './utils'
2+
import { lonLatToTile, tileToRegion, type BoundingRegion, degToRad } from './utils'
33

44
describe('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

Comments
 (0)