This is an automated email from the ASF dual-hosted git repository.
jiayu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sedona.git
The following commit(s) were added to refs/heads/master by this push:
new e936ecdea4 [SEDONA-740] Fix rasterization rounding issue (#2065)
e936ecdea4 is described below
commit e936ecdea4b920b6409bd6a45a5f1d1c9314f79b
Author: Pranav Toggi <[email protected]>
AuthorDate: Sun Jul 6 22:35:01 2025 -0700
[SEDONA-740] Fix rasterization rounding issue (#2065)
* Fix bug; add tests
* address comments
---
.../apache/sedona/common/raster/Rasterization.java | 17 ++---
.../sedona/common/raster/FunctionEditorsTest.java | 10 +--
.../common/raster/RasterBandAccessorsTest.java | 76 +++++++++++++++++----
.../common/raster/RasterConstructorsTest.java | 2 +-
spark/common/src/test/resources/raster/test7.tiff | Bin 0 -> 65920 bytes
spark/common/src/test/resources/raster/test8.tiff | Bin 0 -> 65920 bytes
.../scala/org/apache/sedona/sql/rasterIOTest.scala | 2 +-
.../org/apache/sedona/sql/rasteralgebraTest.scala | 16 ++---
8 files changed, 86 insertions(+), 37 deletions(-)
diff --git
a/common/src/main/java/org/apache/sedona/common/raster/Rasterization.java
b/common/src/main/java/org/apache/sedona/common/raster/Rasterization.java
index b62a615b1c..730272c6e0 100644
--- a/common/src/main/java/org/apache/sedona/common/raster/Rasterization.java
+++ b/common/src/main/java/org/apache/sedona/common/raster/Rasterization.java
@@ -549,14 +549,14 @@ public class Rasterization {
// Calculate scan line limits to iterate between for each segment
// Using BigDecimal to avoid floating point errors
double yStart =
- Math.ceil(
+ Math.round(
(BigDecimal.valueOf(params.upperLeftY)
.subtract(BigDecimal.valueOf(worldP1.y))
.divide(BigDecimal.valueOf(params.scaleY),
RoundingMode.CEILING))
.doubleValue());
double yEnd =
- Math.floor(
+ Math.round(
(BigDecimal.valueOf(params.upperLeftY)
.subtract(BigDecimal.valueOf(worldP2.y))
.divide(BigDecimal.valueOf(params.scaleY),
RoundingMode.FLOOR))
@@ -581,17 +581,14 @@ public class Rasterization {
}
} else {
double slope = (worldP2.y - worldP1.y) / (worldP2.x - worldP1.x);
- // System.out.println("slope: " + slope);
-
+ double xMin = (geomExtent.getMinX() - params.upperLeftX) /
params.scaleX;
+ double xMax = (geomExtent.getMaxX() - params.upperLeftX) /
params.scaleX;
for (double y = yStart; y >= yEnd; y--) {
double xIntercept = p1X + ((p1Y - y) / slope);
- if ((xIntercept < 0) || (xIntercept >=
params.writableRaster.getWidth())) {
- continue; // skip xIntercepts outside geomExtent
- }
- if (!scanlineIntersections.containsKey(y)) {
- scanlineIntersections.put(y, new TreeSet<>());
+ if ((xIntercept < xMin) || (xIntercept >= xMax)) {
+ continue; // Skip xIntercepts outside geomExtent
}
- scanlineIntersections.get(y).add(xIntercept);
+ scanlineIntersections.computeIfAbsent(y, k -> new
TreeSet<>()).add(xIntercept);
}
}
}
diff --git
a/common/src/test/java/org/apache/sedona/common/raster/FunctionEditorsTest.java
b/common/src/test/java/org/apache/sedona/common/raster/FunctionEditorsTest.java
index 679c4c2920..37c6d4ed73 100644
---
a/common/src/test/java/org/apache/sedona/common/raster/FunctionEditorsTest.java
+++
b/common/src/test/java/org/apache/sedona/common/raster/FunctionEditorsTest.java
@@ -85,11 +85,11 @@ public class FunctionEditorsTest extends RasterTestBase {
expected =
new double[] {
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0,
- 0.0, 0.0, 0.0, 0.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 0.0, 0.0,
0.0, 0.0, 10.0, 10.0,
- 10.0, 10.0, 10.0, 10.0, 0.0, 0.0, 0.0, 0.0, 10.0, 10.0, 0.0, 0.0,
10.0, 10.0, 0.0, 0.0,
- 0.0, 0.0, 10.0, 10.0, 0.0, 0.0, 10.0, 10.0, 0.0, 0.0, 0.0, 0.0,
10.0, 10.0, 10.0, 10.0,
- 10.0, 10.0, 0.0, 0.0, 0.0, 0.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0,
0.0, 0.0, 0.0, 0.0,
- 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0
+ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 10.0, 10.0, 10.0,
+ 10.0, 10.0, 10.0, 0.0, 0.0, 0.0, 0.0, 10.0, 10.0, 0.0, 0.0, 10.0,
10.0, 0.0, 0.0, 0.0,
+ 0.0, 10.0, 10.0, 0.0, 0.0, 10.0, 10.0, 0.0, 0.0, 0.0, 0.0, 10.0,
10.0, 10.0, 10.0, 10.0,
+ 10.0, 0.0, 0.0, 0.0, 0.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 0.0,
0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0
};
assertArrayEquals(actual, expected, 0.0);
}
diff --git
a/common/src/test/java/org/apache/sedona/common/raster/RasterBandAccessorsTest.java
b/common/src/test/java/org/apache/sedona/common/raster/RasterBandAccessorsTest.java
index 54613223dd..d953d91ece 100644
---
a/common/src/test/java/org/apache/sedona/common/raster/RasterBandAccessorsTest.java
+++
b/common/src/test/java/org/apache/sedona/common/raster/RasterBandAccessorsTest.java
@@ -100,11 +100,11 @@ public class RasterBandAccessorsTest extends
RasterTestBase {
0);
Double actualZonalStats = RasterBandAccessors.getZonalStats(raster,
extent, "mode");
- assertEquals(10.0, actualZonalStats, FP_TOLERANCE);
+ assertNull(actualZonalStats);
String actualZonalStatsAll =
Arrays.toString(RasterBandAccessors.getZonalStatsAll(raster, extent));
- String expectedZonalStatsAll = "[1.0, 10.0, 10.0, 10.0, 10.0, 0.0, 0.0,
10.0, 10.0]";
+ String expectedZonalStatsAll = "[0.0, null, null, null, null, null, null,
null, null]";
assertEquals(expectedZonalStatsAll, actualZonalStatsAll);
}
@@ -132,19 +132,19 @@ public class RasterBandAccessorsTest extends
RasterTestBase {
Geometry geom = Constructors.geomFromWKT(polygon,
RasterAccessors.srid(raster));
double actual = RasterBandAccessors.getZonalStats(raster, geom, 1, "sum",
false, false);
- double expected = 1.0896994E7;
+ double expected = 1.0795427E7;
assertEquals(expected, actual, 0d);
actual = RasterBandAccessors.getZonalStats(raster, geom, 2, "mean", false,
false);
- expected = 220.76055239764008;
+ expected = 220.7711988936036;
assertEquals(expected, actual, FP_TOLERANCE);
actual = RasterBandAccessors.getZonalStats(raster, geom, 1, "count");
- expected = 185953.0;
+ expected = 185104.0;
assertEquals(expected, actual, 0.1d);
actual = RasterBandAccessors.getZonalStats(raster, geom, 3, "variance",
false, false);
- expected = 13560.056183580346;
+ expected = 13553.040611690152;
assertEquals(expected, actual, FP_TOLERANCE);
actual = RasterBandAccessors.getZonalStats(raster, geom, "max");
@@ -156,7 +156,7 @@ public class RasterBandAccessorsTest extends RasterTestBase
{
assertEquals(expected, actual, 1E-1);
actual = RasterBandAccessors.getZonalStats(raster, geom, 1, "sd", false,
false);
- expected = 92.53811912983977;
+ expected = 92.3801832204387;
assertEquals(expected, actual, FP_TOLERANCE);
geom =
@@ -220,6 +220,58 @@ public class RasterBandAccessorsTest extends
RasterTestBase {
assertEquals(expected, actual, FP_TOLERANCE);
}
+ @Test
+ public void testRasterization1() throws FactoryException, ParseException,
IOException {
+ GridCoverage2D raster = rasterFromGeoTiff(resourceFolder +
"raster/test7.tiff");
+ String wkt =
+ "POLYGON ((-0.897979307443705 52.22640443968169, -0.897982946766236
52.22638696176784, -0.89799206869722 52.22637025568977, -0.898006322680796
52.22635496345145, -0.89802516094114 52.22634167272326, -0.898047859533683
52.2263308942584, -0.898073546166002 52.22632304226535, -0.898101233719275
52.22631841849023, -0.898129858182076 52.22631720062118, -0.89815831953887
52.22631943546002, -0.898185524042003 52.2263250371237, -0.898210426242843
52.22633379034471, -0.898232069167008 52. [...]
+ Geometry geom = Constructors.geomFromWKT(wkt, 4326);
+
+ double[] actual =
+ Arrays.stream(RasterBandAccessors.getZonalStatsAll(raster, geom, 1,
false, false))
+ .mapToDouble(Double::doubleValue)
+ .toArray();
+
+ double[] expected = new double[] {14.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0};
+ assertArrayEquals(expected, actual, FP_TOLERANCE);
+ }
+
+ @Test
+ public void testRasterization2() throws FactoryException, ParseException,
IOException {
+ GridCoverage2D raster =
+ rasterFromGeoTiff(resourceFolder +
"raster_geotiff_color/FAA_UTM18N_NAD83.tif");
+
+ Geometry geom =
+ Constructors.geomFromWKT(
+ "POLYGON ((223494.02395197155 4217428.006125106, 223479.8395471539
4217398.373656692, 223505.34101226972 4217373.172295491, 223535.14466627932
4217360.826244754, 223565.4369792635 4217365.465341343, 223601.56141698774
4217373.684776339, 223638.23068926093 4217400.554304458, 223640.98980568675
4217424.9074383825, 223639.6956443374 4217443.73089679, 223617.52307020774
4217455.706515524, 223608.8979089979 4217478.55688384, 223580.76530214178
4217483.17859071, 223544.9615263639 4 [...]
+ 26918);
+
+ double[] actual =
+ Arrays.stream(RasterBandAccessors.getZonalStatsAll(raster, geom, 1,
false, true))
+ .mapToDouble(Double::doubleValue)
+ .toArray();
+
+ double[] expected = new double[] {7.0, 1785.0, 255.0, 255.0, 255.0, 0.0,
0.0, 255.0, 255.0};
+ assertArrayEquals(expected, actual, FP_TOLERANCE);
+ }
+
+ @Test
+ public void testRasterization3() throws FactoryException, ParseException,
IOException {
+ GridCoverage2D raster = rasterFromGeoTiff(resourceFolder +
"raster/test8.tiff");
+
+ Geometry geom =
+ Constructors.geomFromWKT(
+ "POLYGON((0.719049156542861 52.250273602713776,0.71904724344863
52.2502560225192,0.71903977906225 52.250239009194175,0.719027050241651
52.250223216550275,0.719009546152358 52.25020925148843,0.718987939468514
52.25019765067622,0.718963060522109 52.25018885992432,0.718935865393908
52.25018321705467,0.718907399172285 52.25018093891829,0.718878755791851
52.250182113062095,0.71885103599512 52.250186694364565,0.718825305032585
52.250194506769795,0.718802551726619 52.25020525005296, [...]
+ 4326);
+ double[] actual =
+ Arrays.stream(RasterBandAccessors.getZonalStatsAll(raster, geom, 1,
true, true))
+ .mapToDouble(Double::doubleValue)
+ .toArray();
+
+ double[] expected = new double[] {2.0, 10.0, 5.0, 5.0, 5.0, 0.0, 0.0, 5.0,
5.0};
+ assertArrayEquals(expected, actual, FP_TOLERANCE);
+ }
+
@Test
public void testZonalStatsAll() throws IOException, FactoryException,
ParseException {
GridCoverage2D raster =
@@ -234,13 +286,13 @@ public class RasterBandAccessorsTest extends
RasterTestBase {
.toArray();
double[] expected =
new double[] {
- 185953.0,
- 1.0896994E7,
- 58.600796975566816,
+ 185104.0,
+ 1.0795427E7,
+ 58.32087367104147,
0.0,
0.0,
- 92.53811912983977,
- 8563.303492088418,
+ 92.3801832204387,
+ 8534.098251841822,
0.0,
255.0
};
diff --git
a/common/src/test/java/org/apache/sedona/common/raster/RasterConstructorsTest.java
b/common/src/test/java/org/apache/sedona/common/raster/RasterConstructorsTest.java
index bff8261d48..f42d4acc5e 100644
---
a/common/src/test/java/org/apache/sedona/common/raster/RasterConstructorsTest.java
+++
b/common/src/test/java/org/apache/sedona/common/raster/RasterConstructorsTest.java
@@ -129,7 +129,7 @@ public class RasterConstructorsTest extends RasterTestBase {
expected =
new double[] {
- 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0,
1.0, 1.0, 0.0, 0.0, 1.0,
+ 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0,
1.0, 1.0, 0.0, 0.0, 1.0,
1.0, 0.0
};
assertArrayEquals(expected, actual, 0.1d);
diff --git a/spark/common/src/test/resources/raster/test7.tiff
b/spark/common/src/test/resources/raster/test7.tiff
new file mode 100644
index 0000000000..cf510b5d77
Binary files /dev/null and b/spark/common/src/test/resources/raster/test7.tiff
differ
diff --git a/spark/common/src/test/resources/raster/test8.tiff
b/spark/common/src/test/resources/raster/test8.tiff
new file mode 100644
index 0000000000..64be625abf
Binary files /dev/null and b/spark/common/src/test/resources/raster/test8.tiff
differ
diff --git
a/spark/common/src/test/scala/org/apache/sedona/sql/rasterIOTest.scala
b/spark/common/src/test/scala/org/apache/sedona/sql/rasterIOTest.scala
index a62b975454..8b36f32272 100644
--- a/spark/common/src/test/scala/org/apache/sedona/sql/rasterIOTest.scala
+++ b/spark/common/src/test/scala/org/apache/sedona/sql/rasterIOTest.scala
@@ -86,7 +86,7 @@ class rasterIOTest extends TestBaseScala with BeforeAndAfter
with GivenWhenThen
rasterDf.write.format("raster").mode(SaveMode.Overwrite).save(tempDir +
"/raster-written")
df = sparkSession.read.format("binaryFile").load(tempDir +
"/raster-written/*")
rasterDf = df.selectExpr("RS_FromGeoTiff(content)")
- assert(rasterCount == 6)
+ assert(rasterCount == 8)
assert(rasterDf.count() == 0)
}
diff --git
a/spark/common/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala
b/spark/common/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala
index be0c69a722..1b6490c234 100644
--- a/spark/common/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala
+++ b/spark/common/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala
@@ -1634,11 +1634,11 @@ class rasteralgebraTest extends TestBaseScala with
BeforeAndAfter with GivenWhen
|""".stripMargin)
var actual = df.selectExpr("RS_ZonalStats(raster, geom, 1,
'mode')").first().get(0)
- assertEquals(10.0, actual)
+ assertNull(actual)
val statsDf = df.selectExpr("RS_ZonalStatsAll(raster, geom) as stats")
actual = statsDf.first().getStruct(0).toSeq.slice(0, 9)
- val expected = Seq(1.0, 10.0, 10.0, 10.0, 10.0, 0.0, 0.0, 10.0, 10.0)
+ val expected = Seq(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
assertTrue(expected.equals(actual))
}
@@ -1651,20 +1651,20 @@ class rasteralgebraTest extends TestBaseScala with
BeforeAndAfter with GivenWhen
"ST_GeomFromWKT('POLYGON ((236722 4204770, 243900 4204770, 243900
4197590, 221170 4197590, 236722 4204770))', 26918) as geom")
var actual =
df.selectExpr("RS_ZonalStats(raster, geom, 1, 'sum', false,
true)").first().get(0)
- assertEquals(1.0896994e7, actual)
+ assertEquals(1.0795427e7, actual)
actual =
df.selectExpr("RS_ZonalStats(raster, geom, 1, 'count', false,
false)").first().get(0)
- assertEquals(185953.0, actual)
+ assertEquals(185104.0, actual)
actual = df.selectExpr("RS_ZonalStats(raster, geom, 1, 'mean', true,
false)").first().get(0)
assertEquals(58.650240700685295, actual)
actual = df.selectExpr("RS_ZonalStats(raster, geom, 1,
'variance')").first().get(0)
- assertEquals(8563.303492088418, actual)
+ assertEquals(8534.098251841822, actual)
actual = df.selectExpr("RS_ZonalStats(raster, geom,
'sd')").first().get(0)
- assertEquals(92.53811912983977, actual)
+ assertEquals(92.3801832204387, actual)
// Test with a polygon in EPSG:4326
actual = df
@@ -1721,8 +1721,8 @@ class rasteralgebraTest extends TestBaseScala with
BeforeAndAfter with GivenWhen
.getStruct(0)
.toSeq
.slice(0, 9)
- val expected = Seq(185953.0, 1.0896994e7, 58.600796975566816, 0.0, 0.0,
92.53811912983977,
- 8563.303492088418, 0.0, 255.0)
+ val expected = Seq(185104.0, 1.0795427e7, 58.32087367104147, 0.0, 0.0,
92.3801832204387,
+ 8534.098251841822, 0.0, 255.0)
assertTrue(expected.equals(actual))
// Test with a polygon that does not intersect the raster in lenient mode