diff --git a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/BingTileFunctions.java b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/BingTileFunctions.java index d85cb117b5edc..f4366771b7836 100644 --- a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/BingTileFunctions.java +++ b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/BingTileFunctions.java @@ -25,6 +25,7 @@ import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.RowType; import com.facebook.presto.spi.type.StandardTypes; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import io.airlift.slice.Slice; @@ -63,9 +64,12 @@ public class BingTileFunctions { private static final int TILE_PIXELS = 256; - private static final double MAX_LATITUDE = 85.05112878; + + @VisibleForTesting + static final double MAX_LATITUDE = 85.05112878; private static final double MIN_LATITUDE = -85.05112878; - private static final double MIN_LONGITUDE = -180; + @VisibleForTesting + static final double MIN_LONGITUDE = -180; private static final double MAX_LONGITUDE = 180; private static final double EARTH_RADIUS_KM = 6371.01; private static final int OPTIMIZED_TILING_MIN_ZOOM_LEVEL = 10; @@ -366,7 +370,22 @@ public static Block geometryToBingTiles(@SqlType(GEOMETRY_TYPE_NAME) Slice input boolean pointOrRectangle = isPointOrRectangle(ogcGeometry, envelope); BingTile leftUpperTile = latitudeLongitudeToTile(envelope.getYMax(), envelope.getXMin(), zoomLevel); - BingTile rightLowerTile = getTileCoveringLowerRightCorner(envelope, zoomLevel); + BingTile rightLowerTile = latitudeLongitudeToTile(envelope.getYMin(), envelope.getXMax(), zoomLevel); + + // If the tile covering the lower right corner of the envelope overlaps the envelope only + // at the border then return a tile shifted to the left and/or top + int deltaX = 0; + int deltaY = 0; + Point upperLeftCorner = tileXYToLatitudeLongitude(rightLowerTile.getX(), rightLowerTile.getY(), rightLowerTile.getZoomLevel()); + if (rightLowerTile.getX() > leftUpperTile.getX() && upperLeftCorner.getX() == envelope.getXMax()) { + deltaX = -1; + } + if (rightLowerTile.getY() > leftUpperTile.getY() && upperLeftCorner.getY() == envelope.getYMin()) { + deltaY = -1; + } + if (deltaX != 0 || deltaY != 0) { + rightLowerTile = BingTile.fromCoordinates(rightLowerTile.getX() + deltaX, rightLowerTile.getY() + deltaY, rightLowerTile.getZoomLevel()); + } // XY coordinates start at (0,0) in the left upper corner and increase left to right and top to bottom long tileCount = (long) (rightLowerTile.getX() - leftUpperTile.getX() + 1) * (rightLowerTile.getY() - leftUpperTile.getY() + 1); @@ -404,29 +423,6 @@ public static Block geometryToBingTiles(@SqlType(GEOMETRY_TYPE_NAME) Slice input return blockBuilder.build(); } - private static BingTile getTileCoveringLowerRightCorner(Envelope envelope, int zoomLevel) - { - BingTile tile = latitudeLongitudeToTile(envelope.getYMin(), envelope.getXMax(), zoomLevel); - - // If the tile covering the lower right corner of the envelope overlaps the envelope only - // at the border then return a tile shifted to the left and/or top - int deltaX = 0; - int deltaY = 0; - Point upperLeftCorner = tileXYToLatitudeLongitude(tile.getX(), tile.getY(), tile.getZoomLevel()); - if (upperLeftCorner.getX() == envelope.getXMax()) { - deltaX = -1; - } - if (upperLeftCorner.getY() == envelope.getYMin()) { - deltaY = -1; - } - - if (deltaX != 0 || deltaY != 0) { - return BingTile.fromCoordinates(tile.getX() + deltaX, tile.getY() + deltaY, tile.getZoomLevel()); - } - - return tile; - } - private static void checkGeometryToBingTilesLimits(OGCGeometry ogcGeometry, Envelope envelope, boolean pointOrRectangle, long tileCount, int zoomLevel) { if (pointOrRectangle) { diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestBingTileFunctions.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestBingTileFunctions.java index 5c7e015755f3f..6f505cb5c15d8 100644 --- a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestBingTileFunctions.java +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestBingTileFunctions.java @@ -36,6 +36,8 @@ import static com.facebook.presto.operator.aggregation.AggregationTestUtils.assertAggregation; import static com.facebook.presto.operator.scalar.ApplyFunction.APPLY_FUNCTION; import static com.facebook.presto.plugin.geospatial.BingTile.fromCoordinates; +import static com.facebook.presto.plugin.geospatial.BingTileFunctions.MAX_LATITUDE; +import static com.facebook.presto.plugin.geospatial.BingTileFunctions.MIN_LONGITUDE; import static com.facebook.presto.plugin.geospatial.BingTileType.BING_TILE; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; @@ -458,6 +460,12 @@ public void testGeometryToBingTiles() assertGeometryToBingTiles("POLYGON EMPTY", 10, emptyList()); assertGeometryToBingTiles("GEOMETRYCOLLECTION EMPTY", 10, emptyList()); + // Geometries at MIN_LONGITUDE/MAX_LATITUDE + assertGeometryToBingTiles("LINESTRING (-180 -79.19245, -180 -79.17133464081945)", 8, ImmutableList.of("22200000")); + assertGeometryToBingTiles(format("POINT (%s 0)", MIN_LONGITUDE), 5, ImmutableList.of("20000")); + assertGeometryToBingTiles(format("POINT (0 %s)", MAX_LATITUDE), 5, ImmutableList.of("10000")); + assertGeometryToBingTiles(format("POINT (%s %s)", MIN_LONGITUDE, MAX_LATITUDE), 5, ImmutableList.of("00000")); + // Invalid input // Longitude out of range assertInvalidFunction("geometry_to_bing_tiles(ST_Point(600, 30.12), 10)", "Longitude span for the geometry must be in [-180.00, 180.00] range");