Skip to content

Commit

Permalink
Avoid numerical errors during testing in vector tile factories (#74343)
Browse files Browse the repository at this point in the history
  • Loading branch information
iverase committed Jun 23, 2021
1 parent 0c11c64 commit 592b61a
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@
public class SimpleFeatureFactory {

private final int extent;
private final Rectangle rectangle;
private final double pointXScale, pointYScale;
private final double pointXScale, pointYScale, pointXTranslate, pointYTranslate;

public SimpleFeatureFactory(int z, int x, int y, int extent) {
this.extent = extent;
rectangle = FeatureFactoryUtils.getTileBounds(z, x, y);
final Rectangle rectangle = FeatureFactoryUtils.getTileBounds(z, x, y);
pointXScale = (double) extent / (rectangle.getMaxLon() - rectangle.getMinLon());
pointYScale = -(double) extent / (rectangle.getMaxLat() - rectangle.getMinLat());
pointYScale = (double) -extent / (rectangle.getMaxLat() - rectangle.getMinLat());
pointXTranslate = -pointXScale * rectangle.getMinX();
pointYTranslate = -pointYScale * rectangle.getMinY();
}

public void point(VectorTile.Tile.Feature.Builder featureBuilder, double lon, double lat) {
Expand Down Expand Up @@ -62,10 +63,10 @@ public void box(VectorTile.Tile.Feature.Builder featureBuilder, double minLon, d
}

private int lat(double lat) {
return (int) Math.round(pointYScale * (FeatureFactoryUtils.latToSphericalMercator(lat) - rectangle.getMinY())) + extent;
return (int) Math.round(pointYScale * FeatureFactoryUtils.latToSphericalMercator(lat) + pointYTranslate) + extent;
}

private int lon(double lon) {
return (int) Math.round(pointXScale * (FeatureFactoryUtils.lonToSphericalMercator(lon) - rectangle.getMinX()));
return (int) Math.round(pointXScale * FeatureFactoryUtils.lonToSphericalMercator(lon) + pointXTranslate);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.elasticsearch.test.ESTestCase;
import org.hamcrest.Matchers;

import java.util.Arrays;
import java.util.List;

public class FeatureFactoryTests extends ESTestCase {
Expand All @@ -26,6 +27,8 @@ public void testPoint() {
int x = randomIntBetween(0, (1 << z) - 1);
int y = randomIntBetween(0, (1 << z) - 1);
int extent = randomIntBetween(1 << 8, 1 << 14);
// check if we might have numerical error due to floating point arithmetic
assumeFalse("", hasNumericalError(z, x, y, extent));
Rectangle rectangle = GeoTileUtils.toBoundingBox(x, y, z);
SimpleFeatureFactory builder = new SimpleFeatureFactory(z, x, y, extent);
FeatureFactory factory = new FeatureFactory(z, x, y, extent);
Expand All @@ -44,6 +47,34 @@ public void testPoint() {
}
}

public void testIssue74341() {
int z = 1;
int x = 0;
int y = 0;
int extent = 1730;
// this is the typical case we need to guard from.
assertThat(hasNumericalError(z, x, y, extent), Matchers.equalTo(true));
double lon = -171.0;
double lat = 0.9999999403953552;
SimpleFeatureFactory builder = new SimpleFeatureFactory(z, x, y, extent);
FeatureFactory factory = new FeatureFactory(z, x, y, extent);
VectorTile.Tile.Feature.Builder featureBuilder = VectorTile.Tile.Feature.newBuilder();
builder.point(featureBuilder, lon, lat);
byte[] b1 = featureBuilder.build().toByteArray();
Point point = new Point(lon, lat);
List<VectorTile.Tile.Feature> features = factory.getFeatures(point, new UserDataIgnoreConverter());
assertThat(features.size(), Matchers.equalTo(1));
byte[] b2 = features.get(0).toByteArray();
assertThat(Arrays.equals(b1, b2), Matchers.equalTo(false));
}

private boolean hasNumericalError(int z, int x, int y, int extent) {
final Rectangle rectangle = FeatureFactoryUtils.getTileBounds(z, x, y);
final double xDiff = rectangle.getMaxLon() - rectangle.getMinLon();
final double yDiff = rectangle.getMaxLat() - rectangle.getMinLat();
return (double) -extent / yDiff != -1d / (yDiff / (double) extent) || (double) extent / xDiff != 1d / (xDiff / (double) extent);
}

public void testRectangle() {
int z = randomIntBetween(1, 10);
int x = randomIntBetween(0, (1 << z) - 1);
Expand Down

0 comments on commit 592b61a

Please sign in to comment.