-
-
Notifications
You must be signed in to change notification settings - Fork 358
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactored to use sequences and added tests
Now testing with RobolectricTestRunner instead of mocking XmlPullParser
- Loading branch information
Showing
16 changed files
with
1,246 additions
and
391 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
346 changes: 214 additions & 132 deletions
346
app/src/main/java/de/westnordost/streetcomplete/data/import/GpxImport.kt
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
105 changes: 105 additions & 0 deletions
105
...test/java/de/westnordost/streetcomplete/data/import/GpxImportAddInterpolatedPointsTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
package de.westnordost.streetcomplete.data.import | ||
|
||
import de.westnordost.streetcomplete.data.osm.mapdata.LatLon | ||
import de.westnordost.streetcomplete.util.math.distanceTo | ||
import org.junit.Test | ||
import kotlin.test.assertContains | ||
import kotlin.test.assertEquals | ||
import kotlin.test.assertFails | ||
import kotlin.test.assertTrue | ||
|
||
class GpxImportAddInterpolatedPointsTest { | ||
@Test | ||
fun `fails with bad parameters`() { | ||
assertFails { | ||
emptySequence<LatLon>().addInterpolatedPoints(-15.0).count() | ||
} | ||
} | ||
|
||
@Test | ||
fun `gracefully handles small sequences`() { | ||
assertEquals( | ||
0, | ||
emptySequence<LatLon>().addInterpolatedPoints(1.0).count(), | ||
"empty sequence invents points" | ||
) | ||
assertEquals( | ||
1, | ||
sequenceOf(LatLon(89.9, 27.1)).addInterpolatedPoints(77.0).count(), | ||
"size 1 sequence invents points" | ||
) | ||
} | ||
|
||
@Test | ||
fun `does not add unnecessary points`() { | ||
val originalPoints = listOf(LatLon(0.0, 0.0), LatLon(0.1, -0.5), LatLon(1.0, -1.0)) | ||
val samplingDistance = | ||
originalPoints.zipWithNext().maxOf { it.first.distanceTo(it.second) } | ||
val interpolatedPoints = | ||
originalPoints.asSequence().addInterpolatedPoints(samplingDistance).toList() | ||
assertEquals(originalPoints.size, interpolatedPoints.size) | ||
} | ||
|
||
@Test | ||
fun `ensures promised sampling distance on single segment`() { | ||
val p1 = LatLon(-11.1, 36.7) | ||
val p2 = LatLon(-89.0, 61.0) | ||
val numIntermediatePoints = 100 | ||
val samplingDistance = p1.distanceTo(p2) / (numIntermediatePoints + 1) | ||
val originalPoints = listOf(p1, p2) | ||
val interpolatedPoints = | ||
originalPoints.asSequence().addInterpolatedPoints(samplingDistance).toList() | ||
assertEquals( | ||
numIntermediatePoints + 2, | ||
interpolatedPoints.size, | ||
"wrong number of points created" | ||
) | ||
assertCorrectSampling(originalPoints, interpolatedPoints, samplingDistance) | ||
} | ||
|
||
@Test | ||
fun `ensures promised sampling distance on multiple segments`() { | ||
val originalPoints = | ||
listOf(LatLon(0.0, 0.0), LatLon(1.0, 1.0), LatLon(2.1, 1.3), LatLon(0.0, 0.0)) | ||
val samplingDistance = | ||
originalPoints.zipWithNext().minOf { it.first.distanceTo(it.second) } / 100 | ||
val interpolatedPoints = | ||
originalPoints.asSequence().addInterpolatedPoints(samplingDistance).toList() | ||
assertCorrectSampling(originalPoints, interpolatedPoints, samplingDistance) | ||
} | ||
|
||
private fun assertCorrectSampling( | ||
originalPoints: List<LatLon>, | ||
interpolatedPoints: List<LatLon>, | ||
samplingDistance: Double, | ||
) { | ||
// some tolerance is needed due to rounding errors | ||
val maxToleratedDistance = samplingDistance * 1.001 | ||
val minToleratedDistance = samplingDistance * 0.999 | ||
|
||
val distances = interpolatedPoints.zipWithNext().map { it.first.distanceTo(it.second) } | ||
distances.forEachIndexed { index, distance -> | ||
assertTrue( | ||
distance <= maxToleratedDistance, | ||
"distance between consecutive points too big; $distance > $maxToleratedDistance" | ||
) | ||
|
||
// the only distance that may be smaller than samplingDistance is between the last | ||
// interpolated point of one segment and the original point starting the next segment | ||
if (distance < minToleratedDistance) { | ||
assertContains( | ||
originalPoints, interpolatedPoints[index + 1], | ||
"distance between two interpolated points too small ($distance < $minToleratedDistance" | ||
) | ||
} | ||
|
||
} | ||
|
||
originalPoints.forEach { | ||
assertContains( | ||
interpolatedPoints, it, | ||
"original point $it is missing in interpolated points" | ||
) | ||
} | ||
} | ||
} |
102 changes: 102 additions & 0 deletions
102
app/src/test/java/de/westnordost/streetcomplete/data/import/GpxImportDetermineBBoxesTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package de.westnordost.streetcomplete.data.import | ||
|
||
import de.westnordost.streetcomplete.data.osm.mapdata.BoundingBox | ||
import de.westnordost.streetcomplete.data.osm.mapdata.LatLon | ||
import de.westnordost.streetcomplete.util.math.area | ||
import de.westnordost.streetcomplete.util.math.isCompletelyInside | ||
import org.junit.Test | ||
import kotlin.test.assertEquals | ||
import kotlin.test.assertTrue | ||
|
||
class GpxImportDetermineBBoxesTest { | ||
@Test | ||
fun `gracefully handles empty sequence`() { | ||
assertEquals( | ||
0, | ||
emptySequence<BoundingBox>().determineBBoxes().count(), | ||
"empty sequence not retained" | ||
) | ||
} | ||
@Test | ||
fun `drops duplicates`() { | ||
val inputBoxes = listOf( | ||
BoundingBox(LatLon(-0.01, -0.01), LatLon(0.0, 0.0)), | ||
BoundingBox(LatLon(-0.01, -0.01), LatLon(0.0, 0.0)), | ||
) | ||
val downloadBoxes = inputBoxes.asSequence().determineBBoxes().toList() | ||
assertEquals( | ||
1, | ||
downloadBoxes.size, | ||
"failed to merge all boxes into one" | ||
) | ||
val downloadBBox = downloadBoxes[0].boundingBox | ||
inputBoxes.forEach { | ||
assertTrue( | ||
it.isCompletelyInside(downloadBBox), | ||
"input bounding box $it is not contained in download box $downloadBBox" | ||
) | ||
} | ||
assertEquals( | ||
inputBoxes[0].area(), | ||
downloadBBox.area(), | ||
1.0, | ||
"area to download is not the same as area of one input box" | ||
) | ||
} | ||
|
||
@Test | ||
fun `merges adjacent boxes forming a rectangle`() { | ||
val inputBoxes = listOf( | ||
BoundingBox(LatLon(0.00, 0.0), LatLon(0.01, 0.01)), | ||
BoundingBox(LatLon(0.01, 0.0), LatLon(0.02, 0.01)), | ||
BoundingBox(LatLon(0.02, 0.0), LatLon(0.03, 0.01)), | ||
) | ||
val downloadBoxes = inputBoxes.asSequence().determineBBoxes().toList() | ||
assertEquals( | ||
1, | ||
downloadBoxes.size, | ||
"failed to merge all boxes into one" | ||
) | ||
val downloadBBox = downloadBoxes[0].boundingBox | ||
inputBoxes.forEach { | ||
assertTrue( | ||
it.isCompletelyInside(downloadBBox), | ||
"input bounding box $it is not contained in download box $downloadBBox" | ||
) | ||
} | ||
assertEquals( | ||
inputBoxes.sumOf { it.area() }, | ||
downloadBBox.area(), | ||
1.0, | ||
"area to download is not the same as area of input boxes" | ||
) | ||
} | ||
|
||
@Test | ||
fun `partially merges boxes in an L-shape`() { | ||
val inputBoxes = listOf( | ||
BoundingBox(LatLon(0.00, 0.00), LatLon(0.01, 0.01)), | ||
BoundingBox(LatLon(0.01, 0.00), LatLon(0.02, 0.01)), | ||
BoundingBox(LatLon(0.00, 0.01), LatLon(0.01, 0.06)), | ||
) | ||
val downloadBoxes = inputBoxes.asSequence().determineBBoxes().toList() | ||
assertEquals( | ||
2, | ||
downloadBoxes.size, | ||
"failed to merge one side of the L-shape" | ||
) | ||
val downloadBBoxes = downloadBoxes.map { it.boundingBox } | ||
inputBoxes.forEach { | ||
assertTrue( | ||
downloadBBoxes.any { downloadBBox -> it.isCompletelyInside(downloadBBox) }, | ||
"input bounding box $it is not contained in any download box $downloadBBoxes" | ||
) | ||
} | ||
assertEquals( | ||
inputBoxes.sumOf { it.area() }, | ||
downloadBBoxes.sumOf { it.area() }, | ||
1.0, | ||
"area to download is not the same as area of input boxes" | ||
) | ||
} | ||
} |
Oops, something went wrong.