diff --git a/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/KdbTree.java b/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/KdbTree.java index a6e8ff1fe011..fe9fceb7e778 100644 --- a/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/KdbTree.java +++ b/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/KdbTree.java @@ -37,6 +37,11 @@ /** * 2-dimensional K-D-B Tree * see https://en.wikipedia.org/wiki/K-D-B-tree + * + * This KdbTree is open: it covers the entire plane, and the border nodes extend + * out to infinity. For each node, the right (xMax) and top (yMax) are not + * considered part of the node. This means, for intersection/containment checks, + * a strict inequality `<` must be used for xMax and yMax. */ public class KdbTree { @@ -277,12 +282,11 @@ public int next() } } - public static KdbTree buildKdbTree(int maxItemsPerNode, Rectangle extent, List items) + public static KdbTree buildKdbTree(int maxItemsPerNode, List items) { checkArgument(maxItemsPerNode > 0, "maxItemsPerNode must be > 0"); - requireNonNull(extent, "extent is null"); requireNonNull(items, "items is null"); - return new KdbTree(buildKdbTreeNode(maxItemsPerNode, 0, extent, items, new LeafIdAllocator())); + return new KdbTree(buildKdbTreeNode(maxItemsPerNode, 0, Rectangle.getUniverseRectangle(), items, new LeafIdAllocator())); } private static Node buildKdbTreeNode(int maxItemsPerNode, int level, Rectangle extent, List items, LeafIdAllocator leafIdAllocator) @@ -298,7 +302,7 @@ private static Node buildKdbTreeNode(int maxItemsPerNode, int level, Rectangle e } // Split over longer side - boolean splitVertically = extent.getWidth() >= extent.getHeight(); + boolean splitVertically = shouldSplitVertically(extent); Optional> splitResult = trySplit(splitVertically ? BY_X : BY_Y, maxItemsPerNode, level, extent, items, leafIdAllocator); if (!splitResult.isPresent()) { // Try spitting by the other side @@ -312,6 +316,37 @@ private static Node buildKdbTreeNode(int maxItemsPerNode, int level, Rectangle e return newInternal(extent, splitResult.get().getLeft(), splitResult.get().getRight()); } + /* + * Split vertically if the extent is wider than it is tall. Consider a + * doubly-infinite "longer" than half-infinite, and half-infinite longer + * than finite. + */ + private static boolean shouldSplitVertically(Rectangle extent) + { + int numHorizontalInfinities = 0; + if (extent.getXMax() == Double.POSITIVE_INFINITY) { + numHorizontalInfinities += 1; + } + if (extent.getXMin() == Double.NEGATIVE_INFINITY) { + numHorizontalInfinities += 1; + } + + int numVerticalInfinities = 0; + if (extent.getYMax() == Double.POSITIVE_INFINITY) { + numVerticalInfinities += 1; + } + if (extent.getYMin() == Double.NEGATIVE_INFINITY) { + numVerticalInfinities += 1; + } + + if (numHorizontalInfinities == numVerticalInfinities) { + return extent.getWidth() >= extent.getHeight(); + } + else { + return numHorizontalInfinities > numVerticalInfinities; + } + } + private static final class SplitResult { private final T left; diff --git a/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/Rectangle.java b/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/Rectangle.java index 282b458ebf1e..839c3b227e3d 100644 --- a/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/Rectangle.java +++ b/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/Rectangle.java @@ -30,6 +30,11 @@ public final class Rectangle implements HasExtent { private static final int INSTANCE_SIZE = ClassLayout.parseClass(Rectangle.class).instanceSize(); + private static final Rectangle UNIVERSE_RECTANGLE = new Rectangle( + Double.NEGATIVE_INFINITY, + Double.NEGATIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY); private final double xMin; private final double yMin; @@ -51,6 +56,11 @@ public Rectangle( this.yMax = yMax; } + public static Rectangle getUniverseRectangle() + { + return UNIVERSE_RECTANGLE; + } + @JsonProperty public double getXMin() { diff --git a/presto-geospatial-toolkit/src/test/java/com/facebook/presto/geospatial/TestKdbTree.java b/presto-geospatial-toolkit/src/test/java/com/facebook/presto/geospatial/TestKdbTree.java index 5fda7e9b5556..75b5c77724cb 100644 --- a/presto-geospatial-toolkit/src/test/java/com/facebook/presto/geospatial/TestKdbTree.java +++ b/presto-geospatial-toolkit/src/test/java/com/facebook/presto/geospatial/TestKdbTree.java @@ -22,6 +22,8 @@ import java.util.Set; import static com.facebook.presto.geospatial.KdbTree.buildKdbTree; +import static java.lang.Double.NEGATIVE_INFINITY; +import static java.lang.Double.POSITIVE_INFINITY; import static org.testng.Assert.assertEquals; public class TestKdbTree @@ -29,7 +31,6 @@ public class TestKdbTree @Test public void testSerde() { - Rectangle extent = new Rectangle(0, 0, 9, 4); ImmutableList.Builder rectangles = ImmutableList.builder(); for (double x = 0; x < 10; x += 1) { for (double y = 0; y < 5; y += 1) { @@ -37,9 +38,9 @@ public void testSerde() } } - testSerializationRoundtrip(buildKdbTree(100, extent, rectangles.build())); - testSerializationRoundtrip(buildKdbTree(20, extent, rectangles.build())); - testSerializationRoundtrip(buildKdbTree(10, extent, rectangles.build())); + testSerializationRoundtrip(buildKdbTree(100, rectangles.build())); + testSerializationRoundtrip(buildKdbTree(20, rectangles.build())); + testSerializationRoundtrip(buildKdbTree(10, rectangles.build())); } private void testSerializationRoundtrip(KdbTree tree) @@ -57,7 +58,6 @@ public void testSinglePartition() private void testSinglePartition(double width, double height) { - Rectangle extent = new Rectangle(0, 0, 9, 4); ImmutableList.Builder rectangles = ImmutableList.builder(); for (double x = 0; x < 10; x += 1) { for (double y = 0; y < 5; y += 1) { @@ -65,13 +65,13 @@ private void testSinglePartition(double width, double height) } } - KdbTree tree = buildKdbTree(100, extent, rectangles.build()); + KdbTree tree = buildKdbTree(100, rectangles.build()); assertEquals(tree.getLeaves().size(), 1); Map.Entry entry = Iterables.getOnlyElement(tree.getLeaves().entrySet()); assertEquals(entry.getKey().intValue(), 0); - assertEquals(entry.getValue(), extent); + assertEquals(entry.getValue(), Rectangle.getUniverseRectangle()); } @Test @@ -83,7 +83,6 @@ public void testSplitVertically() private void testSplitVertically(double width, double height) { - Rectangle extent = new Rectangle(0, 0, 9, 4); ImmutableList.Builder rectangles = ImmutableList.builder(); for (int x = 0; x < 10; x++) { for (int y = 0; y < 5; y++) { @@ -91,16 +90,22 @@ private void testSplitVertically(double width, double height) } } - KdbTree treeCopy = buildKdbTree(25, extent, rectangles.build()); + KdbTree tree = buildKdbTree(25, rectangles.build()); - Map leafNodes = treeCopy.getLeaves(); + Map leafNodes = tree.getLeaves(); assertEquals(leafNodes.size(), 2); assertEquals(leafNodes.keySet(), ImmutableSet.of(0, 1)); - assertEquals(leafNodes.get(0), new Rectangle(0, 0, 4.5, 4)); - assertEquals(leafNodes.get(1), new Rectangle(4.5, 0, 9, 4)); + assertEquals(leafNodes.get(0), new Rectangle(NEGATIVE_INFINITY, NEGATIVE_INFINITY, 4.5, POSITIVE_INFINITY)); + assertEquals(leafNodes.get(1), new Rectangle(4.5, NEGATIVE_INFINITY, POSITIVE_INFINITY, POSITIVE_INFINITY)); - assertPartitions(treeCopy, new Rectangle(1, 1, 2, 2), ImmutableSet.of(0)); - assertPartitions(treeCopy, new Rectangle(1, 1, 5, 2), ImmutableSet.of(0, 1)); + assertPartitions(tree, new Rectangle(1, 1, 2, 2), ImmutableSet.of(0)); + assertPartitions(tree, new Rectangle(1, 1, 5, 2), ImmutableSet.of(0, 1)); + assertPartitions(tree, new Rectangle(5, 1, 5, 2), ImmutableSet.of(1)); + // Partitions do not contain right or top border, but contain left and bottom borders + assertPartitions(tree, new Rectangle(4.5, 0, 4.5, 0), ImmutableSet.of(1)); + // "Outside" partitions + assertPartitions(tree, new Rectangle(-6, 2, -6, 2), ImmutableSet.of(0)); + assertPartitions(tree, new Rectangle(20, 2, 20, 2), ImmutableSet.of(1)); } @Test @@ -112,7 +117,6 @@ public void testSplitHorizontally() private void testSplitHorizontally(double width, double height) { - Rectangle extent = new Rectangle(0, 0, 4, 9); ImmutableList.Builder rectangles = ImmutableList.builder(); for (int x = 0; x < 5; x++) { for (int y = 0; y < 10; y++) { @@ -120,27 +124,31 @@ private void testSplitHorizontally(double width, double height) } } - KdbTree tree = buildKdbTree(25, extent, rectangles.build()); + KdbTree tree = buildKdbTree(25, rectangles.build()); Map leafNodes = tree.getLeaves(); - assertEquals(leafNodes.size(), 2); - assertEquals(leafNodes.keySet(), ImmutableSet.of(0, 1)); - assertEquals(leafNodes.get(0), new Rectangle(0, 0, 4, 4.5)); - assertEquals(leafNodes.get(1), new Rectangle(0, 4.5, 4, 9)); + assertEquals(leafNodes.size(), 3); + assertEquals(leafNodes.keySet(), ImmutableSet.of(0, 1, 2)); + assertEquals(leafNodes.get(0), new Rectangle(NEGATIVE_INFINITY, NEGATIVE_INFINITY, 2.5, 4.5)); + assertEquals(leafNodes.get(1), new Rectangle(NEGATIVE_INFINITY, 4.5, 2.5, POSITIVE_INFINITY)); + assertEquals(leafNodes.get(2), new Rectangle(2.5, NEGATIVE_INFINITY, POSITIVE_INFINITY, POSITIVE_INFINITY)); // points inside and outside partitions assertPartitions(tree, new Rectangle(1, 1, 1, 1), ImmutableSet.of(0)); assertPartitions(tree, new Rectangle(1, 6, 1, 6), ImmutableSet.of(1)); - assertPartitions(tree, new Rectangle(5, 1, 5, 1), ImmutableSet.of()); + assertPartitions(tree, new Rectangle(5, 1, 5, 1), ImmutableSet.of(2)); // point on the border separating two partitions assertPartitions(tree, new Rectangle(1, 4.5, 1, 4.5), ImmutableSet.of(1)); + assertPartitions(tree, new Rectangle(2.5, 4.5, 2.5, 4.5), ImmutableSet.of(2)); + assertPartitions(tree, new Rectangle(2.5, 1, 2.5, 1), ImmutableSet.of(2)); + assertPartitions(tree, new Rectangle(2.5, 5, 2.5, 5), ImmutableSet.of(2)); // rectangles assertPartitions(tree, new Rectangle(1, 1, 2, 2), ImmutableSet.of(0)); assertPartitions(tree, new Rectangle(1, 6, 2, 7), ImmutableSet.of(1)); assertPartitions(tree, new Rectangle(1, 1, 2, 5), ImmutableSet.of(0, 1)); - assertPartitions(tree, new Rectangle(5, 1, 6, 2), ImmutableSet.of()); + assertPartitions(tree, new Rectangle(5, 1, 6, 2), ImmutableSet.of(2)); } private void assertPartitions(KdbTree kdbTree, Rectangle envelope, Set partitions) @@ -159,7 +167,6 @@ public void testEvenDistribution() private void testEvenDistribution(double width, double height) { - Rectangle extent = new Rectangle(0, 0, 9, 4); ImmutableList.Builder rectangles = ImmutableList.builder(); for (int x = 0; x < 10; x++) { for (int y = 0; y < 5; y++) { @@ -167,17 +174,17 @@ private void testEvenDistribution(double width, double height) } } - KdbTree tree = buildKdbTree(10, extent, rectangles.build()); + KdbTree tree = buildKdbTree(10, rectangles.build()); Map leafNodes = tree.getLeaves(); assertEquals(leafNodes.size(), 6); assertEquals(leafNodes.keySet(), ImmutableSet.of(0, 1, 2, 3, 4, 5)); - assertEquals(leafNodes.get(0), new Rectangle(0, 0, 2.5, 2.5)); - assertEquals(leafNodes.get(1), new Rectangle(0, 2.5, 2.5, 4)); - assertEquals(leafNodes.get(2), new Rectangle(2.5, 0, 4.5, 4)); - assertEquals(leafNodes.get(3), new Rectangle(4.5, 0, 7.5, 2.5)); - assertEquals(leafNodes.get(4), new Rectangle(4.5, 2.5, 7.5, 4)); - assertEquals(leafNodes.get(5), new Rectangle(7.5, 0, 9, 4)); + assertEquals(leafNodes.get(0), new Rectangle(NEGATIVE_INFINITY, NEGATIVE_INFINITY, 2.5, 2.5)); + assertEquals(leafNodes.get(1), new Rectangle(2.5, NEGATIVE_INFINITY, 4.5, 2.5)); + assertEquals(leafNodes.get(2), new Rectangle(NEGATIVE_INFINITY, 2.5, 4.5, POSITIVE_INFINITY)); + assertEquals(leafNodes.get(3), new Rectangle(4.5, NEGATIVE_INFINITY, 7.5, 2.5)); + assertEquals(leafNodes.get(4), new Rectangle(7.5, NEGATIVE_INFINITY, POSITIVE_INFINITY, 2.5)); + assertEquals(leafNodes.get(5), new Rectangle(4.5, 2.5, POSITIVE_INFINITY, POSITIVE_INFINITY)); } @Test @@ -189,7 +196,6 @@ public void testSkewedDistribution() private void testSkewedDistribution(double width, double height) { - Rectangle extent = new Rectangle(0, 0, 9, 4); ImmutableList.Builder rectangles = ImmutableList.builder(); for (int x = 0; x < 10; x++) { for (int y = 0; y < 5; y++) { @@ -203,20 +209,20 @@ private void testSkewedDistribution(double width, double height) } } - KdbTree tree = buildKdbTree(10, extent, rectangles.build()); + KdbTree tree = buildKdbTree(10, rectangles.build()); Map leafNodes = tree.getLeaves(); assertEquals(leafNodes.size(), 9); assertEquals(leafNodes.keySet(), ImmutableSet.of(0, 1, 2, 3, 4, 5, 6, 7, 8)); - assertEquals(leafNodes.get(0), new Rectangle(0, 0, 1.5, 2.5)); - assertEquals(leafNodes.get(1), new Rectangle(1.5, 0, 3.5, 2.5)); - assertEquals(leafNodes.get(2), new Rectangle(0, 2.5, 3.5, 4)); - assertEquals(leafNodes.get(3), new Rectangle(3.5, 0, 5.1, 1.75)); - assertEquals(leafNodes.get(4), new Rectangle(3.5, 1.75, 5.1, 4)); - assertEquals(leafNodes.get(5), new Rectangle(5.1, 0, 5.9, 1.75)); - assertEquals(leafNodes.get(6), new Rectangle(5.9, 0, 9, 1.75)); - assertEquals(leafNodes.get(7), new Rectangle(5.1, 1.75, 7.5, 4)); - assertEquals(leafNodes.get(8), new Rectangle(7.5, 1.75, 9, 4)); + assertEquals(leafNodes.get(0), new Rectangle(NEGATIVE_INFINITY, NEGATIVE_INFINITY, 1.5, 2.5)); + assertEquals(leafNodes.get(1), new Rectangle(1.5, NEGATIVE_INFINITY, 3.5, 2.5)); + assertEquals(leafNodes.get(2), new Rectangle(3.5, NEGATIVE_INFINITY, 5.1, 2.5)); + assertEquals(leafNodes.get(3), new Rectangle(NEGATIVE_INFINITY, 2.5, 2.5, POSITIVE_INFINITY)); + assertEquals(leafNodes.get(4), new Rectangle(2.5, 2.5, 5.1, POSITIVE_INFINITY)); + assertEquals(leafNodes.get(5), new Rectangle(5.1, NEGATIVE_INFINITY, 5.9, 1.75)); + assertEquals(leafNodes.get(6), new Rectangle(5.9, NEGATIVE_INFINITY, POSITIVE_INFINITY, 1.75)); + assertEquals(leafNodes.get(7), new Rectangle(5.1, 1.75, 7.5, POSITIVE_INFINITY)); + assertEquals(leafNodes.get(8), new Rectangle(7.5, 1.75, POSITIVE_INFINITY, POSITIVE_INFINITY)); } @Test @@ -228,7 +234,6 @@ public void testCantSplitVertically() private void testCantSplitVertically(double width, double height) { - Rectangle extent = new Rectangle(0, 0, 9 + width, 4 + height); ImmutableList.Builder rectangles = ImmutableList.builder(); for (int y = 0; y < 5; y++) { for (int i = 0; i < 10; i++) { @@ -237,21 +242,21 @@ private void testCantSplitVertically(double width, double height) } } - KdbTree tree = buildKdbTree(10, extent, rectangles.build()); + KdbTree tree = buildKdbTree(10, rectangles.build()); Map leafNodes = tree.getLeaves(); assertEquals(leafNodes.size(), 10); assertEquals(leafNodes.keySet(), ImmutableSet.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); - assertEquals(leafNodes.get(0), new Rectangle(0, 0, 4.5, 0.5)); - assertEquals(leafNodes.get(1), new Rectangle(0, 0.5, 4.5, 1.5)); - assertEquals(leafNodes.get(2), new Rectangle(0, 1.5, 4.5, 2.5)); - assertEquals(leafNodes.get(3), new Rectangle(0, 2.5, 4.5, 3.5)); - assertEquals(leafNodes.get(4), new Rectangle(0, 3.5, 4.5, 4 + height)); - assertEquals(leafNodes.get(5), new Rectangle(4.5, 0, 9 + width, 0.5)); - assertEquals(leafNodes.get(6), new Rectangle(4.5, 0.5, 9 + width, 1.5)); - assertEquals(leafNodes.get(7), new Rectangle(4.5, 1.5, 9 + width, 2.5)); - assertEquals(leafNodes.get(8), new Rectangle(4.5, 2.5, 9 + width, 3.5)); - assertEquals(leafNodes.get(9), new Rectangle(4.5, 3.5, 9 + width, 4 + height)); + assertEquals(leafNodes.get(0), new Rectangle(NEGATIVE_INFINITY, NEGATIVE_INFINITY, 4.5, 0.5)); + assertEquals(leafNodes.get(1), new Rectangle(NEGATIVE_INFINITY, 0.5, 4.5, 1.5)); + assertEquals(leafNodes.get(2), new Rectangle(NEGATIVE_INFINITY, 1.5, 4.5, 2.5)); + assertEquals(leafNodes.get(3), new Rectangle(NEGATIVE_INFINITY, 2.5, 4.5, 3.5)); + assertEquals(leafNodes.get(4), new Rectangle(NEGATIVE_INFINITY, 3.5, 4.5, POSITIVE_INFINITY)); + assertEquals(leafNodes.get(5), new Rectangle(4.5, NEGATIVE_INFINITY, POSITIVE_INFINITY, 0.5)); + assertEquals(leafNodes.get(6), new Rectangle(4.5, 0.5, POSITIVE_INFINITY, 1.5)); + assertEquals(leafNodes.get(7), new Rectangle(4.5, 1.5, POSITIVE_INFINITY, 2.5)); + assertEquals(leafNodes.get(8), new Rectangle(4.5, 2.5, POSITIVE_INFINITY, 3.5)); + assertEquals(leafNodes.get(9), new Rectangle(4.5, 3.5, POSITIVE_INFINITY, POSITIVE_INFINITY)); } @Test @@ -263,7 +268,6 @@ public void testCantSplit() private void testCantSplit(double width, double height) { - Rectangle extent = new Rectangle(0, 0, 9 + width, 4 + height); ImmutableList.Builder rectangles = ImmutableList.builder(); for (int i = 0; i < 10; i++) { for (int j = 0; j < 5; j++) { @@ -272,12 +276,12 @@ private void testCantSplit(double width, double height) } } - KdbTree tree = buildKdbTree(10, extent, rectangles.build()); + KdbTree tree = buildKdbTree(10, rectangles.build()); Map leafNodes = tree.getLeaves(); assertEquals(leafNodes.size(), 2); assertEquals(leafNodes.keySet(), ImmutableSet.of(0, 1)); - assertEquals(leafNodes.get(0), new Rectangle(0, 0, 4.5, 4 + height)); - assertEquals(leafNodes.get(1), new Rectangle(4.5, 0, 9 + width, 4 + height)); + assertEquals(leafNodes.get(0), new Rectangle(NEGATIVE_INFINITY, NEGATIVE_INFINITY, 4.5, POSITIVE_INFINITY)); + assertEquals(leafNodes.get(1), new Rectangle(4.5, NEGATIVE_INFINITY, POSITIVE_INFINITY, POSITIVE_INFINITY)); } } diff --git a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningInternalAggregateFunction.java b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningInternalAggregateFunction.java index 07eb2d4261fe..6fa20ba122ea 100644 --- a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningInternalAggregateFunction.java +++ b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningInternalAggregateFunction.java @@ -55,18 +55,13 @@ public static void input(SpatialPartitioningState state, @SqlType(GEOMETRY_TYPE_ return; } - Rectangle extent = new Rectangle(envelope.getXMin(), envelope.getYMin(), envelope.getXMax(), envelope.getYMax()); - if (state.getCount() == 0) { state.setPartitionCount(toIntExact(partitionCount)); - state.setExtent(extent); state.setSamples(new ArrayList<>()); } - else { - state.setExtent(state.getExtent().merge(extent)); - } // use reservoir sampling + Rectangle extent = new Rectangle(envelope.getXMin(), envelope.getYMin(), envelope.getXMax(), envelope.getYMax()); List samples = state.getSamples(); if (samples.size() <= MAX_SAMPLE_COUNT) { samples.add(extent); @@ -98,11 +93,7 @@ public static void output(SpatialPartitioningState state, BlockBuilder out) int partitionCount = state.getPartitionCount(); int maxItemsPerNode = (samples.size() + partitionCount - 1) / partitionCount; - Rectangle envelope = state.getExtent(); - - // Add a small buffer on the right and upper sides - Rectangle paddedExtent = new Rectangle(envelope.getXMin(), envelope.getYMin(), Math.nextUp(envelope.getXMax()), Math.nextUp(envelope.getYMax())); - VARCHAR.writeString(out, KdbTreeUtils.toJson(buildKdbTree(maxItemsPerNode, paddedExtent, samples))); + VARCHAR.writeString(out, KdbTreeUtils.toJson(buildKdbTree(maxItemsPerNode, samples))); } } diff --git a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningState.java b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningState.java index fa54c2878f11..63781e520eb0 100644 --- a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningState.java +++ b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningState.java @@ -31,10 +31,6 @@ public interface SpatialPartitioningState void setCount(long count); - Rectangle getExtent(); - - void setExtent(Rectangle envelope); - List getSamples(); void setSamples(List samples); diff --git a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningStateFactory.java b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningStateFactory.java index 9d480dd8a20a..8a7cf660f7a0 100644 --- a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningStateFactory.java +++ b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningStateFactory.java @@ -30,6 +30,8 @@ public class SpatialPartitioningStateFactory implements AccumulatorStateFactory { + private static final int ENVELOPE_SIZE = toIntExact(new Envelope(1, 2, 3, 4).estimateMemorySize()); + @Override public SpatialPartitioningState createSingleState() { @@ -58,14 +60,11 @@ public static final class GroupedSpatialPartitioningState implements GroupedAccumulatorState, SpatialPartitioningState { private static final int INSTANCE_SIZE = ClassLayout.parseClass(GroupedSpatialPartitioningState.class).instanceSize(); - private static final int ENVELOPE_SIZE = toIntExact(new Envelope(1, 2, 3, 4).estimateMemorySize()); private long groupId; private final IntBigArray partitionCounts = new IntBigArray(); private final LongBigArray counts = new LongBigArray(); - private final ObjectBigArray envelopes = new ObjectBigArray<>(); private final ObjectBigArray> samples = new ObjectBigArray<>(); - private int envelopeCount; private int samplesCount; @Override @@ -92,21 +91,6 @@ public void setCount(long count) counts.set(groupId, count); } - @Override - public Rectangle getExtent() - { - return envelopes.get(groupId); - } - - @Override - public void setExtent(Rectangle envelope) - { - if (envelopes.get(groupId) == null) { - envelopeCount++; - } - envelopes.set(groupId, envelope); - } - @Override public List getSamples() { @@ -127,7 +111,7 @@ public void setSamples(List samples) @Override public long getEstimatedSize() { - return INSTANCE_SIZE + partitionCounts.sizeOf() + counts.sizeOf() + envelopes.sizeOf() + samples.sizeOf() + ENVELOPE_SIZE * (envelopeCount + samplesCount); + return INSTANCE_SIZE + partitionCounts.sizeOf() + counts.sizeOf() + samples.sizeOf() + ENVELOPE_SIZE * samplesCount; } @Override @@ -141,7 +125,6 @@ public void ensureCapacity(long size) { partitionCounts.ensureCapacity(size); counts.ensureCapacity(size); - envelopes.ensureCapacity(size); samples.ensureCapacity(size); } } @@ -153,7 +136,6 @@ public static final class SingleSpatialPartitioningState private int partitionCount; private long count; - private Rectangle envelope; private List samples = new ArrayList<>(); @Override @@ -180,18 +162,6 @@ public void setCount(long count) this.count = count; } - @Override - public Rectangle getExtent() - { - return envelope; - } - - @Override - public void setExtent(Rectangle envelope) - { - this.envelope = envelope; - } - @Override public List getSamples() { @@ -207,7 +177,7 @@ public void setSamples(List samples) @Override public long getEstimatedSize() { - return INSTANCE_SIZE + (envelope != null ? envelope.getEstimatedSizeInBytes() * (1 + samples.size()) : 0); + return INSTANCE_SIZE + samples.size() * ENVELOPE_SIZE; } } } diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestGeoFunctions.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestGeoFunctions.java index 3c391c9050c7..72d2359705a0 100644 --- a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestGeoFunctions.java +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestGeoFunctions.java @@ -63,31 +63,32 @@ public void testSpatialPartitions() assertSpatialPartitions(kdbTreeJson, "POINT EMPTY", null); // points inside partitions assertSpatialPartitions(kdbTreeJson, "POINT (0 0)", ImmutableList.of(0)); - assertSpatialPartitions(kdbTreeJson, "POINT (3 1)", ImmutableList.of(2)); + assertSpatialPartitions(kdbTreeJson, "POINT (3 1)", ImmutableList.of(1)); // point on the border between two partitions - assertSpatialPartitions(kdbTreeJson, "POINT (1 2.5)", ImmutableList.of(1)); + assertSpatialPartitions(kdbTreeJson, "POINT (1 2.5)", ImmutableList.of(2)); // point at the corner of three partitions - assertSpatialPartitions(kdbTreeJson, "POINT (4.5 2.5)", ImmutableList.of(4)); + assertSpatialPartitions(kdbTreeJson, "POINT (4.5 2.5)", ImmutableList.of(5)); // points outside - assertSpatialPartitions(kdbTreeJson, "POINT (2 6)", ImmutableList.of()); - assertSpatialPartitions(kdbTreeJson, "POINT (3 -1)", ImmutableList.of()); - assertSpatialPartitions(kdbTreeJson, "POINT (10 3)", ImmutableList.of()); + assertSpatialPartitions(kdbTreeJson, "POINT (-10 -10)", ImmutableList.of(0)); + assertSpatialPartitions(kdbTreeJson, "POINT (-10 10)", ImmutableList.of(2)); + assertSpatialPartitions(kdbTreeJson, "POINT (10 -10)", ImmutableList.of(4)); + assertSpatialPartitions(kdbTreeJson, "POINT (10 10)", ImmutableList.of(5)); // geometry within a partition assertSpatialPartitions(kdbTreeJson, "MULTIPOINT (5 0.1, 6 2)", ImmutableList.of(3)); // geometries spanning multiple partitions - assertSpatialPartitions(kdbTreeJson, "MULTIPOINT (5 0.1, 5.5 3, 6 2)", ImmutableList.of(4, 3)); - assertSpatialPartitions(kdbTreeJson, "MULTIPOINT (3 2, 8 3)", ImmutableList.of(5, 4, 3, 2)); + assertSpatialPartitions(kdbTreeJson, "MULTIPOINT (5 0.1, 5.5 3, 6 2)", ImmutableList.of(5, 3)); + assertSpatialPartitions(kdbTreeJson, "MULTIPOINT (3 2, 8 3)", ImmutableList.of(5, 4, 3, 2, 1)); // geometry outside - assertSpatialPartitions(kdbTreeJson, "MULTIPOINT (2 6, 3 7)", ImmutableList.of()); + assertSpatialPartitions(kdbTreeJson, "MULTIPOINT (2 6, 5 7)", ImmutableList.of(5, 2)); // with distance assertSpatialPartitions(kdbTreeJson, "POINT EMPTY", 1.2, null); assertSpatialPartitions(kdbTreeJson, "POINT (1 1)", 1.2, ImmutableList.of(0)); assertSpatialPartitions(kdbTreeJson, "POINT (1 1)", 2.3, ImmutableList.of(2, 1, 0)); assertSpatialPartitions(kdbTreeJson, "MULTIPOINT (5 0.1, 6 2)", 0.2, ImmutableList.of(3)); - assertSpatialPartitions(kdbTreeJson, "MULTIPOINT (5 0.1, 6 2)", 1.2, ImmutableList.of(4, 3, 2)); - assertSpatialPartitions(kdbTreeJson, "MULTIPOINT (2 6, 3 7)", 1.2, ImmutableList.of()); + assertSpatialPartitions(kdbTreeJson, "MULTIPOINT (5 0.1, 6 2)", 1.2, ImmutableList.of(5, 3, 2, 1)); + assertSpatialPartitions(kdbTreeJson, "MULTIPOINT (2 6, 3 7)", 1.2, ImmutableList.of(2)); } private static String makeKdbTreeJson() @@ -98,7 +99,7 @@ private static String makeKdbTreeJson() rectangles.add(new Rectangle(x, y, x + 1, y + 2)); } } - return KdbTreeUtils.toJson(buildKdbTree(10, new Rectangle(0, 0, 9, 4), rectangles.build())); + return KdbTreeUtils.toJson(buildKdbTree(10, rectangles.build())); } private void assertSpatialPartitions(String kdbTreeJson, String wkt, List expectedPartitions) diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestKdbTreeCasts.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestKdbTreeCasts.java index c552dc7bbe3f..a9e58ecb11cc 100644 --- a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestKdbTreeCasts.java +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestKdbTreeCasts.java @@ -55,6 +55,6 @@ private String makeKdbTreeJson() rectangles.add(new Rectangle(x, y, x + 1, y + 2)); } } - return KdbTreeUtils.toJson(buildKdbTree(100, new Rectangle(0, 0, 9, 4), rectangles.build())); + return KdbTreeUtils.toJson(buildKdbTree(100, rectangles.build())); } } diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSpatialPartitioningInternalAggregation.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSpatialPartitioningInternalAggregation.java index c443be11c2c5..dcfe2b13b1ba 100644 --- a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSpatialPartitioningInternalAggregation.java +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSpatialPartitioningInternalAggregation.java @@ -79,8 +79,7 @@ public void test(int partitionCount) Block partitionCountBlock = BlockAssertions.createRLEBlock(partitionCount, geometries.size()); - Rectangle expectedExtent = new Rectangle(-10, -10, Math.nextUp(10.0), Math.nextUp(10.0)); - String expectedValue = getSpatialPartitioning(expectedExtent, geometries, partitionCount); + String expectedValue = getSpatialPartitioning(geometries, partitionCount); AccumulatorFactory accumulatorFactory = function.bind(Ints.asList(0, 1, 2), Optional.empty()); Page page = new Page(geometryBlock, partitionCountBlock); @@ -164,7 +163,7 @@ private Block makeGeometryBlock(List geometries) return builder.build(); } - private String getSpatialPartitioning(Rectangle extent, List geometries, int partitionCount) + private String getSpatialPartitioning(List geometries, int partitionCount) { ImmutableList.Builder rectangles = ImmutableList.builder(); for (OGCGeometry geometry : geometries) { @@ -173,6 +172,6 @@ private String getSpatialPartitioning(Rectangle extent, List geomet rectangles.add(new Rectangle(envelope.getXMin(), envelope.getYMin(), envelope.getXMax(), envelope.getYMax())); } - return KdbTreeUtils.toJson(buildKdbTree(roundToInt(geometries.size() * 1.0 / partitionCount, CEILING), extent, rectangles.build())); + return KdbTreeUtils.toJson(buildKdbTree(roundToInt(geometries.size() * 1.0 / partitionCount, CEILING), rectangles.build())); } }