From f256387e31b9889c87837a117b2be27adbcbb3fe Mon Sep 17 00:00:00 2001 From: Masashi Katsumata Date: Tue, 10 Nov 2026 21:24:09 -0800 Subject: [PATCH] Check the date-line wrap before skipping a heatmap tile --- .../heatmap/HeatmapTileRenderer.kt | 4 ++- .../com/mapconductor/heatmap/HeatmapWorld.kt | 6 ++++ .../mapconductor/heatmap/BoundsWrapTest.kt | 32 +++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/mapconductor/heatmap/BoundsWrapTest.kt diff --git a/src/main/java/com/mapconductor/heatmap/HeatmapTileRenderer.kt b/src/main/java/com/mapconductor/heatmap/HeatmapTileRenderer.kt index a182fae..2c51bb6 100644 --- a/src/main/java/com/mapconductor/heatmap/HeatmapTileRenderer.kt +++ b/src/main/java/com/mapconductor/heatmap/HeatmapTileRenderer.kt @@ -242,7 +242,9 @@ class HeatmapTileRenderer( minY = bounds.minY - padding, maxY = bounds.maxY + padding, ) - if (!tileBounds.intersects(paddedBounds)) return null + // 日付変更線をまたいだ点も余白ぶんは隣のタイルに掛かる(HeatmapTileBinner が回り込ませる)ので、 + // ここで弾くときも 1 周ずらした位置と比べる。 + if (!tileBounds.intersectsWrapped(paddedBounds)) return null val buffers = resetBuffers(gridDim) timings.let { it.setupMs = msSince(setupStartNs) } diff --git a/src/main/java/com/mapconductor/heatmap/HeatmapWorld.kt b/src/main/java/com/mapconductor/heatmap/HeatmapWorld.kt index 8401af6..6820bc6 100644 --- a/src/main/java/com/mapconductor/heatmap/HeatmapWorld.kt +++ b/src/main/java/com/mapconductor/heatmap/HeatmapWorld.kt @@ -29,6 +29,12 @@ internal data class Bounds( maxX >= other.minX && minY <= other.maxY && maxY >= other.minY + + /** 日付変更線の向こう側(x を 1 周ずらした位置)にある [other] とも重なるかを見る。 */ + fun intersectsWrapped(other: Bounds): Boolean = + intersects(other) || + intersects(other.copy(minX = other.minX - HeatmapWorld.WORLD_WIDTH, maxX = other.maxX - HeatmapWorld.WORLD_WIDTH)) || + intersects(other.copy(minX = other.minX + HeatmapWorld.WORLD_WIDTH, maxX = other.maxX + HeatmapWorld.WORLD_WIDTH)) } /** diff --git a/src/test/java/com/mapconductor/heatmap/BoundsWrapTest.kt b/src/test/java/com/mapconductor/heatmap/BoundsWrapTest.kt new file mode 100644 index 0000000..ef82ff6 --- /dev/null +++ b/src/test/java/com/mapconductor/heatmap/BoundsWrapTest.kt @@ -0,0 +1,32 @@ +package com.mapconductor.heatmap + +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +/** + * タイルを描く前の「データ範囲と重なるか」の判定が、日付変更線の向こう側も見ることを確かめる。 + * 点が経度 179.9 付近にしか無いとき、z1 の西半球タイル (x=0) の左端には + * HeatmapTileBinner が回り込ませた点がにじむので、ここで弾いてはいけない。 + * react-sdk の tileRenderer.test.mjs と同じ配置で見る。 + */ +class BoundsWrapTest { + @Test + fun dataJustEastOfTheDateLineReachesTheWesternTile() { + val dataX = HeatmapWorld.WORLD_WIDTH * (179.9 + 180.0) / 360.0 + val padding = 0.5 * 10.0 / 64.0 // z1 のタイル幅 0.5、半径 10px、64px タイル + val data = Bounds(dataX - padding, dataX + padding, 0.5 - padding, 0.5 + padding) + val westTile = Bounds(0.0 - padding, 0.5 + padding, 0.5 - padding, 1.0 + padding) + + assertFalse(westTile.intersects(data)) + assertTrue(westTile.intersectsWrapped(data)) + } + + @Test + fun wrappingDoesNotMatchTilesThatAreFarAway() { + val data = Bounds(0.99, 1.0, 0.5, 0.51) + // 西端から離れたタイル、y の外れたタイルは 1 周ずらしても重ならない。 + assertFalse(Bounds(0.2, 0.3, 0.5, 0.51).intersectsWrapped(data)) + assertFalse(Bounds(-0.01, 0.01, 0.0, 0.1).intersectsWrapped(data)) + } +}