diff --git a/pom.xml b/pom.xml index 2df3022..ee02238 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ 0.7.0 8.0.0 - 0.6.4${artifact.version.suffix} + 0.6.6${artifact.version.suffix} dremio-udf-gis GIS UDF extensions for Dremio https://github.com/sheinbergon/dremio-udf-gis @@ -72,6 +72,19 @@ jar provided + + com.dremio.sabot + dremio-sabot-vector-tools + ${dremio.version} + + + * + * + + + jar + provided + com.dremio.sabot dremio-sabot-logical @@ -160,12 +173,6 @@ ${commons-io.version} provided - - commons-io - commons-io - ${commons-io.version} - provided - org.jetbrains.kotlin diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STAngle2Lines.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STAngle2Lines.java index a94f00e..af11fc0 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STAngle2Lines.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STAngle2Lines.java @@ -25,27 +25,31 @@ @FunctionTemplate( name = "ST_Angle", scope = FunctionTemplate.FunctionScope.SIMPLE, - nulls = FunctionTemplate.NullHandling.NULL_IF_NULL) + nulls = FunctionTemplate.NullHandling.INTERNAL) public class STAngle2Lines implements SimpleFunction { @Param org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput1; @Param org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput2; - @Output - org.apache.arrow.vector.holders.Float8Holder output; + org.apache.arrow.vector.holders.NullableFloat8Holder output; public void setup() { } public void eval() { - org.locationtech.jts.geom.LineString l1 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toLineString(binaryInput1); - org.locationtech.jts.geom.LineString l2 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toLineString(binaryInput2); - output.value = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toAngleRadians( - l1.getStartPoint(), - l1.getEndPoint(), - l2.getStartPoint(), - l2.getEndPoint() - ); + if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.areHoldersSet(binaryInput1, binaryInput2)) { + org.locationtech.jts.geom.LineString l1 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toLineString(binaryInput1); + org.locationtech.jts.geom.LineString l2 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toLineString(binaryInput2); + double radians = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toAngleRadians( + l1.getStartPoint(), + l1.getEndPoint(), + l2.getStartPoint(), + l2.getEndPoint() + ); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setDoubleValue(output, radians); + } else { + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderNotSet(output); + } } } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STAngle3Points.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STAngle3Points.java index 6e792a6..03f4e68 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STAngle3Points.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STAngle3Points.java @@ -25,7 +25,7 @@ @FunctionTemplate( name = "ST_Angle", scope = FunctionTemplate.FunctionScope.SIMPLE, - nulls = FunctionTemplate.NullHandling.NULL_IF_NULL) + nulls = FunctionTemplate.NullHandling.INTERNAL) public class STAngle3Points implements SimpleFunction { @Param org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput1; @@ -33,17 +33,21 @@ public class STAngle3Points implements SimpleFunction { org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput2; @Param org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput3; - @Output - org.apache.arrow.vector.holders.Float8Holder output; + org.apache.arrow.vector.holders.NullableFloat8Holder output; public void setup() { } public void eval() { - org.locationtech.jts.geom.Point p1 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toPoint(binaryInput1); - org.locationtech.jts.geom.Point p2 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toPoint(binaryInput2); - org.locationtech.jts.geom.Point p3 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toPoint(binaryInput3); - output.value = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toAngleRadians(p2, p1, p2, p3); + if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.areHoldersSet(binaryInput1, binaryInput2, binaryInput3)) { + org.locationtech.jts.geom.Point p1 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toPoint(binaryInput1); + org.locationtech.jts.geom.Point p2 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toPoint(binaryInput2); + org.locationtech.jts.geom.Point p3 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toPoint(binaryInput3); + double radians = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toAngleRadians(p2, p1, p2, p3); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setDoubleValue(output, radians); + } else { + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderNotSet(output); + } } } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STAngle4Points.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STAngle4Points.java index 6d52e61..dd427da 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STAngle4Points.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STAngle4Points.java @@ -25,7 +25,7 @@ @FunctionTemplate( name = "ST_Angle", scope = FunctionTemplate.FunctionScope.SIMPLE, - nulls = FunctionTemplate.NullHandling.NULL_IF_NULL) + nulls = FunctionTemplate.NullHandling.INTERNAL) public class STAngle4Points implements SimpleFunction { @Param org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput1; @@ -35,18 +35,22 @@ public class STAngle4Points implements SimpleFunction { org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput3; @Param org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput4; - @Output - org.apache.arrow.vector.holders.Float8Holder output; + org.apache.arrow.vector.holders.NullableFloat8Holder output; public void setup() { } public void eval() { - org.locationtech.jts.geom.Point p1 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toPoint(binaryInput1); - org.locationtech.jts.geom.Point p2 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toPoint(binaryInput2); - org.locationtech.jts.geom.Point p3 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toPoint(binaryInput3); - org.locationtech.jts.geom.Point p4 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toPoint(binaryInput4); - output.value = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toAngleRadians(p1, p2, p3, p4); + if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.areHoldersSet(binaryInput1, binaryInput2, binaryInput3, binaryInput4)) { + org.locationtech.jts.geom.Point p1 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toPoint(binaryInput1); + org.locationtech.jts.geom.Point p2 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toPoint(binaryInput2); + org.locationtech.jts.geom.Point p3 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toPoint(binaryInput3); + org.locationtech.jts.geom.Point p4 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toPoint(binaryInput4); + double radians = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toAngleRadians(p1, p2, p3, p4); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setDoubleValue(output, radians); + } else { + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderNotSet(output); + } } } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STArea.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STArea.java index fc8ce79..33249fc 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STArea.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STArea.java @@ -25,19 +25,24 @@ @FunctionTemplate( name = "ST_Area", scope = FunctionTemplate.FunctionScope.SIMPLE, - nulls = FunctionTemplate.NullHandling.NULL_IF_NULL) + nulls = FunctionTemplate.NullHandling.INTERNAL, + costCategory = FunctionTemplate.FunctionCostCategory.MEDIUM) public class STArea implements SimpleFunction { @Param org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput; @Output - org.apache.arrow.vector.holders.Float8Holder output; + org.apache.arrow.vector.holders.NullableFloat8Holder output; public void setup() { } public void eval() { - org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); - output.value = geom.getArea(); + if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(binaryInput)) { + org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setDoubleValue(output, geom.getArea()); + } else { + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderNotSet(output); + } } } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STAsBinary.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STAsBinary.java new file mode 100644 index 0000000..cc9b910 --- /dev/null +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STAsBinary.java @@ -0,0 +1,55 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sheinbergon.dremio.udf.gis; + +import com.dremio.exec.expr.SimpleFunction; +import com.dremio.exec.expr.annotations.FunctionTemplate; +import com.dremio.exec.expr.annotations.Output; +import com.dremio.exec.expr.annotations.Param; + +import javax.inject.Inject; + +@FunctionTemplate( + name = "ST_AsBinary", + scope = FunctionTemplate.FunctionScope.SIMPLE, + nulls = FunctionTemplate.NullHandling.INTERNAL) +public class STAsBinary implements SimpleFunction { + + @Param + org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput; + + @Output + org.apache.arrow.vector.holders.NullableVarBinaryHolder wkbOutput; + + @Inject + org.apache.arrow.memory.ArrowBuf buffer; + + public void setup() { + } + + public void eval() { + if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(binaryInput)) { + org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(geom); + buffer = buffer.reallocIfNeeded(bytes.length); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, wkbOutput); + } else { + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderNotSet(wkbOutput); + } + } +} diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STAsEWKB.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STAsEWKB.java new file mode 100644 index 0000000..cdb41f2 --- /dev/null +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STAsEWKB.java @@ -0,0 +1,55 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sheinbergon.dremio.udf.gis; + +import com.dremio.exec.expr.SimpleFunction; +import com.dremio.exec.expr.annotations.FunctionTemplate; +import com.dremio.exec.expr.annotations.Output; +import com.dremio.exec.expr.annotations.Param; + +import javax.inject.Inject; + +@FunctionTemplate( + name = "ST_AsEWKB", + scope = FunctionTemplate.FunctionScope.SIMPLE, + nulls = FunctionTemplate.NullHandling.INTERNAL) +public class STAsEWKB implements SimpleFunction { + + @Param + org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput; + + @Output + org.apache.arrow.vector.holders.NullableVarBinaryHolder ewkbOutput; + + @Inject + org.apache.arrow.memory.ArrowBuf buffer; + + public void setup() { + } + + public void eval() { + if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(binaryInput)) { + org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(geom); + buffer = buffer.reallocIfNeeded(bytes.length); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, ewkbOutput); + } else { + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderNotSet(ewkbOutput); + } + } +} diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STAzimuth.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STAzimuth.java index 8328a3d..437b7df 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STAzimuth.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STAzimuth.java @@ -25,21 +25,26 @@ @FunctionTemplate( name = "ST_Azimuth", scope = FunctionTemplate.FunctionScope.SIMPLE, - nulls = FunctionTemplate.NullHandling.NULL_IF_NULL) + nulls = FunctionTemplate.NullHandling.INTERNAL) public class STAzimuth implements SimpleFunction { @Param org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput1; @Param org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput2; @Output - org.apache.arrow.vector.holders.Float8Holder output; + org.apache.arrow.vector.holders.NullableFloat8Holder output; public void setup() { } public void eval() { - org.locationtech.jts.geom.Point p1 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toPoint(binaryInput1); - org.locationtech.jts.geom.Point p2 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toPoint(binaryInput2); - output.value = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toAzimuthRadians(p1, p2); + if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.areHoldersSet(binaryInput1, binaryInput2)) { + org.locationtech.jts.geom.Point p1 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toPoint(binaryInput1); + org.locationtech.jts.geom.Point p2 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toPoint(binaryInput2); + double radians = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toAzimuthRadians(p1, p2); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setDoubleValue(output, radians); + } else { + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderNotSet(output); + } } } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STBuffer.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STBuffer.java index ef88db9..c8a1374 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STBuffer.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STBuffer.java @@ -23,14 +23,13 @@ import com.dremio.exec.expr.annotations.Param; import org.apache.arrow.memory.ArrowBuf; - - import javax.inject.Inject; @FunctionTemplate( name = "ST_Buffer", scope = FunctionTemplate.FunctionScope.SIMPLE, - nulls = FunctionTemplate.NullHandling.NULL_IF_NULL) + nulls = FunctionTemplate.NullHandling.INTERNAL, + costCategory = FunctionTemplate.FunctionCostCategory.MEDIUM) public class STBuffer implements SimpleFunction { @Param org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput; @@ -48,10 +47,14 @@ public void setup() { } public void eval() { - org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); - org.locationtech.jts.geom.Geometry buffered = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.buffer(geom, radiusInput.value, null); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(buffered); - buffer = buffer.reallocIfNeeded(bytes.length); - org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); + if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(binaryInput)) { + org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); + org.locationtech.jts.geom.Geometry buffered = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.buffer(geom, radiusInput.value, null); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(buffered); + buffer = buffer.reallocIfNeeded(bytes.length); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); + } else { + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderNotSet(binaryOutput); + } } } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STBufferParameters.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STBufferParameters.java index 337e175..07682a5 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STBufferParameters.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STBufferParameters.java @@ -28,7 +28,8 @@ @FunctionTemplate( name = "ST_Buffer", scope = FunctionTemplate.FunctionScope.SIMPLE, - nulls = FunctionTemplate.NullHandling.NULL_IF_NULL) + nulls = FunctionTemplate.NullHandling.INTERNAL, + costCategory = FunctionTemplate.FunctionCostCategory.MEDIUM) public class STBufferParameters implements SimpleFunction { @Param org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput; @@ -49,11 +50,15 @@ public void setup() { } public void eval() { - org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); - java.lang.String parameters = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toUTF8String(parametersInput); - org.locationtech.jts.geom.Geometry buffered = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.buffer(geom, radiusInput.value, parameters); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(buffered); - buffer = buffer.reallocIfNeeded(bytes.length); - org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); + if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(binaryInput)) { + org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); + java.lang.String parameters = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toUTF8String(parametersInput); + org.locationtech.jts.geom.Geometry buffered = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.buffer(geom, radiusInput.value, parameters); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(buffered); + buffer = buffer.reallocIfNeeded(bytes.length); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); + } else { + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderNotSet(binaryOutput); + } } } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STCentroid.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STCentroid.java index 9988781..9f18376 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STCentroid.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STCentroid.java @@ -48,7 +48,7 @@ public void eval() { org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); org.locationtech.jts.algorithm.Centroid centroid = new org.locationtech.jts.algorithm.Centroid(geom); org.locationtech.jts.geom.Point point = factory.createPoint(centroid.getCentroid()); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(point); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(point); buffer = buffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); } else { diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STCollect.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STCollect.java index 2793a69..403e0ff 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STCollect.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STCollect.java @@ -49,7 +49,7 @@ public void eval() { org.locationtech.jts.geom.Geometry geom1 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput1); org.locationtech.jts.geom.Geometry geom2 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput2); org.locationtech.jts.geom.GeometryCollection collection = org.sheinbergon.dremio.udf.gis.util.GeometryCollections.collect(geom1, geom2); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(collection); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(collection); buffer = buffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STCollectAggregate.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STCollectAggregate.java index bca4566..e0d044e 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STCollectAggregate.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STCollectAggregate.java @@ -52,30 +52,30 @@ public void setup() { value = new org.apache.arrow.vector.holders.NullableVarBinaryHolder(); indicator = new org.apache.arrow.vector.holders.NullableBitHolder(); org.locationtech.jts.geom.GeometryCollection collection = org.sheinbergon.dremio.udf.gis.util.GeometryCollections.empty(); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(collection); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(collection); valueBuffer = valueBuffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, valueBuffer, value); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderSet(value); - org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setValueFalse(indicator); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setBooleanValue(indicator, false); } @Override public void add() { if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(input)) { org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(input); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(geom); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(geom); final int required = value.end + bytes.length + Integer.BYTES; valueBuffer = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.enlargeBufferIfNeeded(valueBuffer, required); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.append(bytes, valueBuffer, value); - org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setValueTrue(indicator); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setBooleanValue(indicator, true); } } @Override public void output() { - if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isValueTrue(indicator)) { + if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.getBooleanValue(indicator)) { org.locationtech.jts.geom.GeometryCollection collection = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometryCollection(value); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(collection); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(collection); outputBuffer = outputBuffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, outputBuffer, output); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderSet(output); @@ -87,10 +87,10 @@ public void output() { @Override public void reset() { org.locationtech.jts.geom.GeometryCollection collection = org.sheinbergon.dremio.udf.gis.util.GeometryCollections.empty(); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(collection); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(collection); valueBuffer = valueBuffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, valueBuffer, value); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderSet(value); - org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setValueFalse(indicator); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setBooleanValue(indicator, false); } } \ No newline at end of file diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STConcaveHull.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STConcaveHull.java new file mode 100644 index 0000000..32ee70a --- /dev/null +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STConcaveHull.java @@ -0,0 +1,64 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sheinbergon.dremio.udf.gis; + +import com.dremio.exec.expr.SimpleFunction; +import com.dremio.exec.expr.annotations.FunctionTemplate; +import com.dremio.exec.expr.annotations.Output; +import com.dremio.exec.expr.annotations.Param; + +import javax.inject.Inject; + +@FunctionTemplate( + name = "ST_ConcaveHull", + scope = FunctionTemplate.FunctionScope.SIMPLE, + nulls = FunctionTemplate.NullHandling.INTERNAL) +public class STConcaveHull implements SimpleFunction { + @Param + org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput; + + @Param(constant = true) + org.apache.arrow.vector.holders.Float8Holder percentageConvexInput; + + @Param(constant = true) + org.apache.arrow.vector.holders.BitHolder allowHolesInput; + + @Output + org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryOutput; + + @Inject + org.apache.arrow.memory.ArrowBuf buffer; + + public void setup() { + } + + public void eval() { + if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(binaryInput)) { + org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); + org.locationtech.jts.algorithm.hull.ConcaveHull concaveHull = new org.locationtech.jts.algorithm.hull.ConcaveHull(geom); + concaveHull.setHolesAllowed(org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.getBooleanValue(allowHolesInput)); + concaveHull.setMaximumEdgeLengthRatio(percentageConvexInput.value); + org.locationtech.jts.geom.Geometry hull = concaveHull.getHull(); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(hull); + buffer = buffer.reallocIfNeeded(bytes.length); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); + } else { + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderNotSet(binaryOutput); + } + } +} diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STConcaveHullNoHolesAllowed.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STConcaveHullNoHolesAllowed.java new file mode 100644 index 0000000..8ca5075 --- /dev/null +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STConcaveHullNoHolesAllowed.java @@ -0,0 +1,60 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sheinbergon.dremio.udf.gis; + +import com.dremio.exec.expr.SimpleFunction; +import com.dremio.exec.expr.annotations.FunctionTemplate; +import com.dremio.exec.expr.annotations.Output; +import com.dremio.exec.expr.annotations.Param; + +import javax.inject.Inject; + +@FunctionTemplate( + name = "ST_ConcaveHull", + scope = FunctionTemplate.FunctionScope.SIMPLE, + nulls = FunctionTemplate.NullHandling.INTERNAL) +public class STConcaveHullNoHolesAllowed implements SimpleFunction { + @Param + org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput; + + @Param(constant = true) + org.apache.arrow.vector.holders.Float8Holder percentageConvexInput; + + @Output + org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryOutput; + + @Inject + org.apache.arrow.memory.ArrowBuf buffer; + + public void setup() { + } + + public void eval() { + if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(binaryInput)) { + org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); + org.locationtech.jts.algorithm.hull.ConcaveHull concaveHull = new org.locationtech.jts.algorithm.hull.ConcaveHull(geom); + concaveHull.setMaximumEdgeLengthRatio(percentageConvexInput.value); + org.locationtech.jts.geom.Geometry hull = concaveHull.getHull(); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(hull); + buffer = buffer.reallocIfNeeded(bytes.length); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); + } else { + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderNotSet(binaryOutput); + } + } +} diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STConvexHull.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STConvexHull.java index 2295538..d1775d6 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STConvexHull.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STConvexHull.java @@ -46,7 +46,7 @@ public void eval() { org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); org.locationtech.jts.algorithm.ConvexHull convexHull = new org.locationtech.jts.algorithm.ConvexHull(geom); org.locationtech.jts.geom.Geometry hull = convexHull.getConvexHull(); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(hull); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(hull); buffer = buffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); } else { diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STDifference.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STDifference.java index 537f8ff..39c1748 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STDifference.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STDifference.java @@ -50,7 +50,7 @@ public void eval() { org.locationtech.jts.geom.Geometry geom1 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput1); org.locationtech.jts.geom.Geometry geom2 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput2); org.locationtech.jts.geom.Geometry difference = geom1.difference(geom2); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(difference); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(difference); buffer = buffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STDistance.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STDistance.java index 03dd93f..98cb6e2 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STDistance.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STDistance.java @@ -25,7 +25,7 @@ @FunctionTemplate( name = "ST_Distance", scope = FunctionTemplate.FunctionScope.SIMPLE, - nulls = FunctionTemplate.NullHandling.NULL_IF_NULL) + nulls = FunctionTemplate.NullHandling.INTERNAL) public class STDistance implements SimpleFunction { @Param org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput1; @@ -34,14 +34,19 @@ public class STDistance implements SimpleFunction { org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput2; @Output - org.apache.arrow.vector.holders.Float8Holder output; + org.apache.arrow.vector.holders.NullableFloat8Holder output; public void setup() { } public void eval() { - org.locationtech.jts.geom.Geometry geom1 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput1); - org.locationtech.jts.geom.Geometry geom2 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput2); - output.value = geom1.distance(geom2); + if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.areHoldersSet(binaryInput1, binaryInput2)) { + org.locationtech.jts.geom.Geometry geom1 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput1); + org.locationtech.jts.geom.Geometry geom2 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput2); + double radians = geom1.distance(geom2); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setDoubleValue(output, radians); + } else { + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderNotSet(output); + } } } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STEnvelope.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STEnvelope.java index 546f926..e466c2c 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STEnvelope.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STEnvelope.java @@ -45,7 +45,7 @@ public void eval() { if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(binaryInput)) { org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); org.locationtech.jts.geom.Geometry envelope = geom.getEnvelope(); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(envelope); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(envelope); buffer = buffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); } else { diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromEWKB.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromEWKB.java new file mode 100644 index 0000000..8f3b1a3 --- /dev/null +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromEWKB.java @@ -0,0 +1,55 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sheinbergon.dremio.udf.gis; + +import com.dremio.exec.expr.SimpleFunction; +import com.dremio.exec.expr.annotations.FunctionTemplate; +import com.dremio.exec.expr.annotations.Output; +import com.dremio.exec.expr.annotations.Param; + +import javax.inject.Inject; + +@FunctionTemplate( + name = "ST_GeomFromEWKB", + scope = FunctionTemplate.FunctionScope.SIMPLE, + nulls = FunctionTemplate.NullHandling.INTERNAL) +public class STGeomFromEWKB implements SimpleFunction { + + @Param + org.apache.arrow.vector.holders.NullableVarBinaryHolder ewkbInput; + + @Output + org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryOutput; + + @Inject + org.apache.arrow.memory.ArrowBuf buffer; + + public void setup() { + } + + public void eval() { + if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(ewkbInput)) { + org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(ewkbInput); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(geom); + buffer = buffer.reallocIfNeeded(bytes.length); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); + } else { + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderNotSet(binaryOutput); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromEWKT.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromEWKT.java index 21923fa..5e9345d 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromEWKT.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromEWKT.java @@ -45,7 +45,7 @@ public void setup() { public void eval() { if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(ewktInput)) { org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometryFromEWKT(ewktInput); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(geom); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(geom); buffer = buffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); } else { diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromText.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromText.java index 0e1c006..53a9702 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromText.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromText.java @@ -45,7 +45,7 @@ public void setup() { public void eval() { if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(wktInput)) { org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(wktInput); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(geom); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(geom); buffer = buffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); } else { diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromTextSrid.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromTextSrid.java index 4bf1f4d..7da5ad0 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromTextSrid.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromTextSrid.java @@ -49,7 +49,7 @@ public void eval() { if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(wktInput)) { org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(wktInput); geom.setSRID(sridInput.value); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(geom); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(geom); buffer = buffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); } else { diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromWKB.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromWKB.java index 8a9426c..e88b22e 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromWKB.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromWKB.java @@ -45,7 +45,7 @@ public void setup() { public void eval() { if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(wkbInput)) { org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(wkbInput); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(geom); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(geom); buffer = buffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); } else { diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromWKBSrid.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromWKBSrid.java index 6089249..a758c08 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromWKBSrid.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STGeomFromWKBSrid.java @@ -49,7 +49,7 @@ public void eval() { if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(wkbInput)) { org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(wkbInput); geom.setSRID(sridInput.value); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(geom); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(geom); buffer = buffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); } else { diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STGeometryN.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STGeometryN.java index 3351396..436bd94 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STGeometryN.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STGeometryN.java @@ -51,7 +51,7 @@ public void eval() { if (0 <= index && index < size) { org.locationtech.jts.geom.Geometry nthGeom = geom.getGeometryN(index); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderSet(binaryOutput); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(nthGeom); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(nthGeom); buffer = buffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); } else { diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STIntersection.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STIntersection.java index c517d16..438e589 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STIntersection.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STIntersection.java @@ -49,7 +49,7 @@ public void eval() { org.locationtech.jts.geom.Geometry geom1 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput1); org.locationtech.jts.geom.Geometry geom2 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput2); org.locationtech.jts.geom.Geometry intersection = geom1.intersection(geom2); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(intersection); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(intersection); buffer = buffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STIsClosed.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STIsClosed.java index d281e79..b7b110b 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STIsClosed.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STIsClosed.java @@ -40,7 +40,7 @@ public void eval() { if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(binaryInput)) { org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); boolean result = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isClosed(geom); - org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setValue(output, result); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setBooleanValue(output, result); } else { org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderNotSet(output); } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STIsCollection.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STIsCollection.java index 53faf53..ca3379c 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STIsCollection.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STIsCollection.java @@ -40,7 +40,7 @@ public void eval() { if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(binaryInput)) { org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); boolean result = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isACollection(geom); - org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setValue(output, result); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setBooleanValue(output, result); } else { org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderNotSet(output); } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STIsEmpty.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STIsEmpty.java index a14c7d1..02c6cdb 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STIsEmpty.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STIsEmpty.java @@ -40,7 +40,7 @@ public void eval() { if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(binaryInput)) { org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); boolean result = geom.isEmpty(); - org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setValue(output, result); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setBooleanValue(output, result); } else { org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderNotSet(output); } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STIsSimple.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STIsSimple.java new file mode 100644 index 0000000..34b6c71 --- /dev/null +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STIsSimple.java @@ -0,0 +1,48 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sheinbergon.dremio.udf.gis; + +import com.dremio.exec.expr.SimpleFunction; +import com.dremio.exec.expr.annotations.FunctionTemplate; +import com.dremio.exec.expr.annotations.Output; +import com.dremio.exec.expr.annotations.Param; + +@FunctionTemplate( + name = "ST_IsSimple", + scope = FunctionTemplate.FunctionScope.SIMPLE, + nulls = FunctionTemplate.NullHandling.INTERNAL) +public class STIsSimple implements SimpleFunction { + @Param + org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput; + + @Output + org.apache.arrow.vector.holders.NullableBitHolder output; + + public void setup() { + } + + public void eval() { + if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(binaryInput)) { + org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); + boolean result = geom.isSimple(); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setBooleanValue(output, result); + } else { + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderNotSet(output); + } + } +} diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STLength.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STLength.java index ca54d18..59d4b71 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STLength.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STLength.java @@ -25,23 +25,27 @@ @FunctionTemplate( name = "ST_Length", scope = FunctionTemplate.FunctionScope.SIMPLE, - nulls = FunctionTemplate.NullHandling.NULL_IF_NULL) + nulls = FunctionTemplate.NullHandling.INTERNAL, + costCategory = FunctionTemplate.FunctionCostCategory.MEDIUM) public class STLength implements SimpleFunction { @Param org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput; @Output - org.apache.arrow.vector.holders.Float8Holder output; + org.apache.arrow.vector.holders.NullableFloat8Holder output; public void setup() { } public void eval() { - org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); - if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isLinear(geom)) { - output.value = geom.getLength(); + if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(binaryInput)) { + org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); + double length = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isLinear(geom) + ? geom.getLength() + : 0.0; + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setDoubleValue(output, length); } else { - output.value = 0.0; + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderNotSet(output); } } } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STPerimeter.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STPerimeter.java index d4d1992..5d1b726 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STPerimeter.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STPerimeter.java @@ -25,23 +25,25 @@ @FunctionTemplate( name = "ST_Perimeter", scope = FunctionTemplate.FunctionScope.SIMPLE, - nulls = FunctionTemplate.NullHandling.NULL_IF_NULL) + nulls = FunctionTemplate.NullHandling.INTERNAL, + costCategory = FunctionTemplate.FunctionCostCategory.MEDIUM) public class STPerimeter implements SimpleFunction { @Param org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput; @Output - org.apache.arrow.vector.holders.Float8Holder output; + org.apache.arrow.vector.holders.NullableFloat8Holder output; public void setup() { } public void eval() { - org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); - if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isAreal(geom)) { - output.value = geom.getLength(); + if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(binaryInput)) { + org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); + double perimeter = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isAreal(geom) ? geom.getLength() : 0.0; + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setDoubleValue(output, perimeter); } else { - output.value = 0.0; + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderNotSet(output); } } } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STPoint.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STPointSrid.java similarity index 97% rename from src/main/java/org/sheinbergon/dremio/udf/gis/STPoint.java rename to src/main/java/org/sheinbergon/dremio/udf/gis/STPointSrid.java index b674de5..647ed68 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STPoint.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STPointSrid.java @@ -31,7 +31,7 @@ name = "ST_Point", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = FunctionTemplate.NullHandling.NULL_IF_NULL) -public class STPoint implements SimpleFunction { +public class STPointSrid implements SimpleFunction { @Param org.apache.arrow.vector.holders.Float8Holder longitudeInput; @@ -58,7 +58,7 @@ public void eval() { org.locationtech.jts.geom.PrecisionModel precisionModel = new org.locationtech.jts.geom.PrecisionModel(); org.locationtech.jts.geom.GeometryFactory factory = new org.locationtech.jts.geom.GeometryFactory(precisionModel, srid()); org.locationtech.jts.geom.Point point = factory.createPoint(coordinate); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(point); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(point); buffer = buffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, output); } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STSimplify.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STSimplify.java new file mode 100644 index 0000000..63700f5 --- /dev/null +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STSimplify.java @@ -0,0 +1,60 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sheinbergon.dremio.udf.gis; + +import com.dremio.exec.expr.SimpleFunction; +import com.dremio.exec.expr.annotations.FunctionTemplate; +import com.dremio.exec.expr.annotations.Output; +import com.dremio.exec.expr.annotations.Param; +import org.apache.arrow.memory.ArrowBuf; + +import javax.inject.Inject; + +@FunctionTemplate( + name = "ST_Simplify", + scope = FunctionTemplate.FunctionScope.SIMPLE, + nulls = FunctionTemplate.NullHandling.INTERNAL, + costCategory = FunctionTemplate.FunctionCostCategory.COMPLEX) +public class STSimplify implements SimpleFunction { + @Param + org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryInput; + + @Param(constant = true) + org.apache.arrow.vector.holders.Float8Holder toleranceInput; + + @Output + org.apache.arrow.vector.holders.NullableVarBinaryHolder binaryOutput; + + @Inject + ArrowBuf buffer; + + public void setup() { + } + + public void eval() { + if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isHolderSet(binaryInput)) { + org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); + org.locationtech.jts.geom.Geometry simplified = org.locationtech.jts.simplify.DouglasPeuckerSimplifier.simplify(geom, toleranceInput.value); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(simplified); + buffer = buffer.reallocIfNeeded(bytes.length); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); + } else { + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderNotSet(binaryOutput); + } + } +} diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STSymDifference.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STSymDifference.java index b6005ec..0d2a6a8 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STSymDifference.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STSymDifference.java @@ -48,7 +48,7 @@ public void eval() { org.locationtech.jts.geom.Geometry geom1 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput1); org.locationtech.jts.geom.Geometry geom2 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput2); org.locationtech.jts.geom.Geometry difference = geom1.symDifference(geom2); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(difference); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(difference); buffer = buffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STTransformFromProj4ToSrid.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STTransformFromProj4ToSrid.java index b6ff5de..41ca963 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STTransformFromProj4ToSrid.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STTransformFromProj4ToSrid.java @@ -27,7 +27,8 @@ @FunctionTemplate( name = "ST_Transform", scope = FunctionTemplate.FunctionScope.SIMPLE, - nulls = FunctionTemplate.NullHandling.NULL_IF_NULL) + nulls = FunctionTemplate.NullHandling.NULL_IF_NULL, + costCategory = FunctionTemplate.FunctionCostCategory.COMPLEX) public class STTransformFromProj4ToSrid implements SimpleFunction { @Param @@ -54,7 +55,7 @@ public void eval() { String source = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toUTF8String(sourceProj4ParametersInput); org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); org.locationtech.jts.geom.Geometry result = org.sheinbergon.dremio.udf.gis.util.GeometryTransformation.transform(geom, source, target); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(result); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(result); buffer = buffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STTransformToProj4.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STTransformToProj4.java index 1633264..2579b76 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STTransformToProj4.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STTransformToProj4.java @@ -27,7 +27,8 @@ @FunctionTemplate( name = "ST_Transform", scope = FunctionTemplate.FunctionScope.SIMPLE, - nulls = FunctionTemplate.NullHandling.NULL_IF_NULL) + nulls = FunctionTemplate.NullHandling.NULL_IF_NULL, + costCategory = FunctionTemplate.FunctionCostCategory.COMPLEX) public class STTransformToProj4 implements SimpleFunction { @Param @@ -50,7 +51,7 @@ public void eval() { java.lang.String target = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toUTF8String(targetProj4ParametersInput); org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); org.locationtech.jts.geom.Geometry result = org.sheinbergon.dremio.udf.gis.util.GeometryTransformation.transform(geom, target); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(result); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(result); buffer = buffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STTransformToSrid.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STTransformToSrid.java index 64219bf..7612d57 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STTransformToSrid.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STTransformToSrid.java @@ -27,7 +27,8 @@ @FunctionTemplate( name = "ST_Transform", scope = FunctionTemplate.FunctionScope.SIMPLE, - nulls = FunctionTemplate.NullHandling.NULL_IF_NULL) + nulls = FunctionTemplate.NullHandling.NULL_IF_NULL, + costCategory = FunctionTemplate.FunctionCostCategory.COMPLEX) public class STTransformToSrid implements SimpleFunction { @Param @@ -50,7 +51,7 @@ public void eval() { int target = targetSridInput.value; org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput); org.locationtech.jts.geom.Geometry result = org.sheinbergon.dremio.udf.gis.util.GeometryTransformation.transform(geom, target); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(result); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(result); buffer = buffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STUnion.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STUnion.java index d09b318..8b70ab3 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STUnion.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STUnion.java @@ -50,7 +50,7 @@ public void eval() { org.locationtech.jts.geom.Geometry geom1 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput1); org.locationtech.jts.geom.Geometry geom2 = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(binaryInput2); org.locationtech.jts.geom.Geometry union = geom1.union(geom2); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(union); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(union); buffer = buffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, buffer, binaryOutput); } diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/STUnionAggregate.java b/src/main/java/org/sheinbergon/dremio/udf/gis/STUnionAggregate.java index f498cf9..1f034e3 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/STUnionAggregate.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/STUnionAggregate.java @@ -52,11 +52,11 @@ public void setup() { value = new org.apache.arrow.vector.holders.NullableVarBinaryHolder(); indicator = new org.apache.arrow.vector.holders.NullableBitHolder(); org.locationtech.jts.geom.Geometry empty = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.emptyGeometry(); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(empty); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(empty); valueBuffer = valueBuffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, valueBuffer, value); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderSet(value); - org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setValueFalse(indicator); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setBooleanValue(indicator, false); } @Override @@ -65,18 +65,18 @@ public void add() { org.locationtech.jts.geom.Geometry geom = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(input); org.locationtech.jts.geom.Geometry accumulated = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(value); org.locationtech.jts.geom.Geometry union = accumulated.union(geom); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(union); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(union); valueBuffer = valueBuffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, valueBuffer, value); - org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setValueTrue(indicator); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setBooleanValue(indicator, true); } } @Override public void output() { - if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.isValueTrue(indicator)) { + if (org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.getBooleanValue(indicator)) { org.locationtech.jts.geom.Geometry union = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toGeometry(value); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(union); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(union); outputBuffer = outputBuffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, outputBuffer, output); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderSet(output); @@ -88,10 +88,10 @@ public void output() { @Override public void reset() { org.locationtech.jts.geom.Geometry empty = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.emptyGeometry(); - byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toBinary(empty); + byte[] bytes = org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.toEWKB(empty); valueBuffer = valueBuffer.reallocIfNeeded(bytes.length); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.populate(bytes, valueBuffer, value); org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.markHolderSet(value); - org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setValueFalse(indicator); + org.sheinbergon.dremio.udf.gis.util.GeometryHelpers.setBooleanValue(indicator, false); } } \ No newline at end of file diff --git a/src/main/java/org/sheinbergon/dremio/udf/gis/util/GeometryHelpers.java b/src/main/java/org/sheinbergon/dremio/udf/gis/util/GeometryHelpers.java index 7766149..44de0ef 100644 --- a/src/main/java/org/sheinbergon/dremio/udf/gis/util/GeometryHelpers.java +++ b/src/main/java/org/sheinbergon/dremio/udf/gis/util/GeometryHelpers.java @@ -35,6 +35,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Set; @@ -52,7 +53,6 @@ public final class GeometryHelpers { private static final int GEOMETRY_DIMENSIONS = 2; private static final double AZIMUTH_NORTH_RADIANS = Angle.toRadians(90.0); - private static final String EWKT_TEMPLATE = "SRID=%d;%s"; private static final Pattern EWKT_REGEX_PATTERN = Pattern.compile("^\\s*SRID\\s*=\\s*(\\d+)\\s*;\\s*(.+)\\s*$"); private GeometryHelpers() { @@ -77,11 +77,24 @@ public static String toUTF8String(final @Nonnull NullableVarCharHolder holder) { holder.buffer); } + public static byte[] toEWKB(final @Nonnull Geometry geometry) { + int order = nativeWkbByteOrder(); + WKBWriter writer = new WKBWriter(GEOMETRY_DIMENSIONS, order, true); + return writer.write(geometry); + } + public static byte[] toBinary(final @Nonnull Geometry geometry) { - WKBWriter writer = new WKBWriter(GEOMETRY_DIMENSIONS, true); + int order = nativeWkbByteOrder(); + WKBWriter writer = new WKBWriter(GEOMETRY_DIMENSIONS, order, false); return writer.write(geometry); } + private static int nativeWkbByteOrder() { + return ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN) + ? ByteOrderValues.BIG_ENDIAN + : ByteOrderValues.LITTLE_ENDIAN; + } + public static byte[] toText( final @Nonnull Geometry geometry) { WKTWriter writer = new WKTWriter(GEOMETRY_DIMENSIONS); @@ -92,7 +105,13 @@ public static byte[] toEWKT( final @Nonnull Geometry geometry) { final WKTWriter writer = new WKTWriter(GEOMETRY_DIMENSIONS); final String wkt = writer.write(geometry); - return String.format(EWKT_TEMPLATE, geometry.getSRID(), wkt).getBytes(StandardCharsets.UTF_8); + String result; + if (geometry.getSRID() > 0) { + result = String.format("SRID=%d;%s", geometry.getSRID(), wkt); + } else { + result = wkt; + } + return result.getBytes(StandardCharsets.UTF_8); } public static byte[] toGeoJson(final @Nonnull Geometry geometry) { @@ -275,6 +294,17 @@ public static boolean isLinear( return geometry != null && LINEAR_TYPES.contains(geometry.getGeometryType()); } + public static boolean areHoldersSet(final @Nonnull ValueHolder... holders) { + boolean result = false; + for (ValueHolder holder : holders) { + result = isHolderSet(holder); + if (!result) { + break; + } + } + return result; + } + public static boolean isHolderSet(final @Nonnull ValueHolder holder) { if (holder instanceof NullableIntHolder) { return ((NullableIntHolder) holder).isSet == BIT_TRUE; @@ -314,6 +344,8 @@ public static void markHolderNotSet(final @Nonnull ValueHolder holder) { ((NullableVarCharHolder) holder).isSet = BIT_FALSE; } else if (holder instanceof NullableVarBinaryHolder) { ((NullableVarBinaryHolder) holder).isSet = BIT_FALSE; + } else if (holder instanceof NullableFloat8Holder) { + ((NullableFloat8Holder) holder).isSet = BIT_FALSE; } else { throw new IllegalArgumentException( String.format("Unsupported value holder type - %s", @@ -321,7 +353,7 @@ public static void markHolderNotSet(final @Nonnull ValueHolder holder) { } } - public static boolean isValueTrue(final @Nonnull ValueHolder holder) { + public static boolean getBooleanValue(final @Nonnull ValueHolder holder) { if (holder instanceof BitHolder) { return ((BitHolder) holder).value == BIT_TRUE; } else if (holder instanceof NullableBitHolder) { @@ -338,24 +370,7 @@ public static boolean isValueTrue(final @Nonnull ValueHolder holder) { } } - public static boolean isValueFalse(final @Nonnull ValueHolder holder) { - if (holder instanceof BitHolder) { - return ((BitHolder) holder).value == BIT_FALSE; - } else if (holder instanceof NullableBitHolder) { - NullableBitHolder nullable = (NullableBitHolder) holder; - if (nullable.isSet == BIT_TRUE) { - return ((NullableBitHolder) holder).value == BIT_FALSE; - } else { - throw new IllegalStateException("Cannot verify state of a not-set nullable bit holder"); - } - } else { - throw new IllegalArgumentException( - String.format("Unsupported value holder type - %s", - holder.getClass().getName())); - } - } - - public static void setValue(final @Nonnull ValueHolder holder, final boolean value) { + public static void setBooleanValue(final @Nonnull ValueHolder holder, final boolean value) { int bitValue = value ? BIT_TRUE : BIT_FALSE; if (holder instanceof BitHolder) { ((BitHolder) holder).value = bitValue; @@ -370,26 +385,12 @@ public static void setValue(final @Nonnull ValueHolder holder, final boolean val } } - public static void setValueFalse(final @Nonnull ValueHolder holder) { - if (holder instanceof BitHolder) { - ((BitHolder) holder).value = BIT_FALSE; - } else if (holder instanceof NullableBitHolder) { - NullableBitHolder nullable = (NullableBitHolder) holder; - nullable.value = BIT_FALSE; - nullable.isSet = BIT_TRUE; - } else { - throw new IllegalArgumentException( - String.format("Unsupported value holder type - %s", - holder.getClass().getName())); - } - } - - public static void setValueTrue(final @Nonnull ValueHolder holder) { - if (holder instanceof BitHolder) { - ((BitHolder) holder).value = BIT_TRUE; - } else if (holder instanceof NullableBitHolder) { - NullableBitHolder nullable = (NullableBitHolder) holder; - nullable.value = BIT_TRUE; + public static void setDoubleValue(final @Nonnull ValueHolder holder, final double value) { + if (holder instanceof Float8Holder) { + ((Float8Holder) holder).value = value; + } else if (holder instanceof NullableFloat8Holder) { + NullableFloat8Holder nullable = (NullableFloat8Holder) holder; + nullable.value = value; nullable.isSet = BIT_TRUE; } else { throw new IllegalArgumentException( diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAngle2LinesTests.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAngle2LinesTests.kt index 5ae0a60..c4b5629 100644 --- a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAngle2LinesTests.kt +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAngle2LinesTests.kt @@ -1,6 +1,6 @@ package org.sheinbergon.dremio.udf.gis -import org.apache.arrow.vector.holders.Float8Holder +import org.apache.arrow.vector.holders.NullableFloat8Holder import org.apache.arrow.vector.holders.NullableVarBinaryHolder import org.sheinbergon.dremio.udf.gis.spec.GeometryMeasurementFunSpec @@ -13,15 +13,22 @@ internal class STAngle2LinesTests : GeometryMeasurementFunSpec.Binary() { 2279, 928.625 ) + + testNullGeometryMeasurement( + "Calling ST_Area on a NULL input" + ) } override val function = STArea().apply { binaryInput = NullableVarBinaryHolder() - output = Float8Holder() + output = NullableFloat8Holder() } override val STArea.wkbInput1: NullableVarBinaryHolder get() = function.binaryInput - override val STArea.measurementOutput: Float8Holder get() = function.output + override val STArea.measurementOutput: NullableFloat8Holder get() = function.output } diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAsBinaryTests.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAsBinaryTests.kt new file mode 100644 index 0000000..1ad9603 --- /dev/null +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAsBinaryTests.kt @@ -0,0 +1,35 @@ +package org.sheinbergon.dremio.udf.gis + +import org.apache.arrow.vector.holders.NullableVarBinaryHolder +import org.sheinbergon.dremio.udf.gis.spec.GeometryOutputFunSpec +import org.sheinbergon.dremio.udf.gis.util.allocateBuffer + +internal class STAsBinaryTests : GeometryOutputFunSpec.NullableVarBinary() { + + init { + testGeometryWKTOutput( + "Calling ST_AsBinary on a POINT", + "POINT(0.5 0.5)", + "POINT (0.5 0.5)" + ) + + testGeometryWKTOutput( + "Calling ST_AsBinary on a POLYGON", + "POLYGON((0.0 0.0,1.23 0.0,1.0 1.0,0.19 1.0,0.0 0.0))", + "POLYGON ((0 0, 1.23 0, 1 1, 0.19 1, 0 0))" + ) + + testNullGeometryOutput( + "Calling ST_AsBinary on null input", + ) + } + + override val function = STAsBinary().apply { + binaryInput = NullableVarBinaryHolder() + wkbOutput = NullableVarBinaryHolder() + buffer = allocateBuffer() + } + + override val STAsBinary.wkbInput: NullableVarBinaryHolder get() = function.binaryInput + override val STAsBinary.output: NullableVarBinaryHolder get() = function.wkbOutput +} diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAsEWKBTests.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAsEWKBTests.kt new file mode 100644 index 0000000..07cda76 --- /dev/null +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAsEWKBTests.kt @@ -0,0 +1,37 @@ +package org.sheinbergon.dremio.udf.gis + +import org.apache.arrow.vector.holders.NullableVarBinaryHolder +import org.sheinbergon.dremio.udf.gis.spec.GeometryOutputFunSpec +import org.sheinbergon.dremio.udf.gis.util.allocateBuffer + +internal class STAsEWKBTests : GeometryOutputFunSpec.NullableVarBinary() { + + init { + testGeometryEWKTOutput( + "Calling ST_AsEWKB on a POINT with SRID 4326", + "POINT(0.5 0.5)", + 4326, + "SRID=4326;POINT (0.5 0.5)" + ) + + testGeometryEWKTOutput( + "Calling ST_AsEWKB on a POLYGON with SRID 0", + "POLYGON((0.0 0.0,1.23 0.0,1.0 1.0,0.19 1.0,0.0 0.0))", + 0, + "SRID=0;POLYGON ((0 0, 1.23 0, 1 1, 0.19 1, 0 0))" + ) + + testNullGeometryOutput( + "Calling ST_AsEWKB on null input", + ) + } + + override val function = STAsEWKB().apply { + binaryInput = NullableVarBinaryHolder() + ewkbOutput = NullableVarBinaryHolder() + buffer = allocateBuffer() + } + + override val STAsEWKB.wkbInput: NullableVarBinaryHolder get() = function.binaryInput + override val STAsEWKB.output: NullableVarBinaryHolder get() = function.ewkbOutput +} diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAsEWKTTests.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAsEWKTTests.kt index b3c24c7..ac53094 100644 --- a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAsEWKTTests.kt +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAsEWKTTests.kt @@ -5,7 +5,7 @@ import org.apache.arrow.vector.holders.NullableVarCharHolder import org.sheinbergon.dremio.udf.gis.spec.GeometryOutputFunSpec import org.sheinbergon.dremio.udf.gis.util.allocateBuffer -internal class STAsEWKTTests : GeometryOutputFunSpec() { +internal class STAsEWKTTests : GeometryOutputFunSpec.NullableVarChar() { init { testGeometryOutput( diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAsGeoJsonTests.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAsGeoJsonTests.kt index 28f8e20..eeb04d6 100644 --- a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAsGeoJsonTests.kt +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAsGeoJsonTests.kt @@ -5,7 +5,7 @@ import org.apache.arrow.vector.holders.NullableVarCharHolder import org.sheinbergon.dremio.udf.gis.spec.GeometryOutputFunSpec import org.sheinbergon.dremio.udf.gis.util.allocateBuffer -internal class STAsGeoJsonTests : GeometryOutputFunSpec() { +internal class STAsGeoJsonTests : GeometryOutputFunSpec.NullableVarChar() { init { testGeometryOutput( diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAsTextTests.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAsTextTests.kt index fad23e0..057a7a1 100644 --- a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAsTextTests.kt +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAsTextTests.kt @@ -5,7 +5,7 @@ import org.apache.arrow.vector.holders.NullableVarCharHolder import org.sheinbergon.dremio.udf.gis.spec.GeometryOutputFunSpec import org.sheinbergon.dremio.udf.gis.util.allocateBuffer -internal class STAsTextTests : GeometryOutputFunSpec() { +internal class STAsTextTests : GeometryOutputFunSpec.NullableVarChar() { init { testGeometryOutput( diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAzimuthTests.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAzimuthTests.kt index 331e294..7587aad 100644 --- a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAzimuthTests.kt +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STAzimuthTests.kt @@ -1,6 +1,6 @@ package org.sheinbergon.dremio.udf.gis -import org.apache.arrow.vector.holders.Float8Holder +import org.apache.arrow.vector.holders.NullableFloat8Holder import org.apache.arrow.vector.holders.NullableVarBinaryHolder import org.sheinbergon.dremio.udf.gis.spec.GeometryMeasurementFunSpec @@ -13,15 +13,19 @@ internal class STAzimuthTests : GeometryMeasurementFunSpec.Binary() { "POINT(75 100)", 0.7378150601204648 ) + + testNullGeometryMeasurement( + "Calling ST_Azimuth on a NULL input" + ) } override val function = STAzimuth().apply { binaryInput1 = NullableVarBinaryHolder() binaryInput2 = NullableVarBinaryHolder() - output = Float8Holder() + output = NullableFloat8Holder() } override val STAzimuth.wkbInput1: NullableVarBinaryHolder get() = function.binaryInput1 override val STAzimuth.wkbInput2: NullableVarBinaryHolder get() = function.binaryInput2 - override val STAzimuth.measurementOutput: Float8Holder get() = function.output + override val STAzimuth.measurementOutput: NullableFloat8Holder get() = function.output } diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STBufferParametersTests.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STBufferParametersTests.kt index d330aae..9c7d7de 100644 --- a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STBufferParametersTests.kt +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STBufferParametersTests.kt @@ -132,6 +132,13 @@ internal class STBufferParametersTests : GeometryProcessingFunSpec() { 1 0)) """.trimIndent() ) { function.radiusInput.value = 1.0 } + + testNullGeometryProcessing( + "Calling ST_Buffer on a NULL input" + ) } override val function = STBuffer().apply { diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STCollectAggregateTests.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STCollectAggregateTests.kt index 9962728..b0111dd 100644 --- a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STCollectAggregateTests.kt +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STCollectAggregateTests.kt @@ -8,7 +8,7 @@ import org.sheinbergon.dremio.udf.gis.util.allocateBuffer internal class STCollectAggregateTests : GeometryAggregationFunSpec() { init { - testGeometryAggegration( + testGeometryAggregration( "Calling ST_Collect on several POLYGONs returns a MULTIPOLYGON", arrayOf( "POLYGON((0 0, 4 0, 4 4, 0 4, 0 0), (1 1, 1 2, 2 2, 2 1, 1 1))", @@ -19,7 +19,7 @@ internal class STCollectAggregateTests : GeometryAggregationFunSpec() { + + init { + + beforeEach { + function.percentageConvexInput.reset() + } + + testGeometryProcessing( + "Calling ST_ConcaveHullNoHolesAllowed on a geometry collection", + "MULTIPOINT ((10 72), (53 76), (56 66), (63 58), (71 51), (81 48), (91 46), (101 45), (111 46), (121 47), (131 50), (140 55), (145 64), (144 74), (135 80), (125 83), (115 85), (105 87), (95 89), (85 91), (75 93), (65 95), (55 98), (45 102), (37 107), (29 114), (22 122), (19 132), (18 142), (21 151), (27 160), (35 167), (44 172), (54 175), (64 178), (74 180), (84 181), (94 181), (104 181), (114 181), (124 181), (134 179), (144 177), (153 173), (162 168), (171 162), (177 154), (182 145), (184 135), (139 132), (136 142), (128 149), (119 153), (109 155), (99 155), (89 155), (79 153), (69 150), (61 144), (63 134), (72 128), (82 125), (92 123), (102 121), (112 119), (122 118), (132 116), (142 113), (151 110), (161 106), (170 102), (178 96), (185 88), (189 78), (190 68), (189 58), (185 49), (179 41), (171 34), (162 29), (153 25), (143 23), (133 21), (123 19), (113 19), (102 19), (92 19), (82 19), (72 21), (62 22), (52 25), (43 29), (33 34), (25 41), (19 49), (14 58), (21 73), (31 74), (42 74), (173 134), (161 134), (150 133), (97 104), (52 117), (157 156), (94 171), (112 106), (169 73), (58 165), (149 40), (70 33), (147 157), (48 153), (140 96), (47 129), (173 55), (144 86), (159 67), (150 146), (38 136), (111 170), (124 94), (26 59), (60 41), (71 162), (41 64), (88 110), (122 34), (151 97), (157 56), (39 146), (88 33), (159 45), (47 56), (138 40), (129 165), (33 48), (106 31), (169 147), (37 122), (71 109), (163 89), (37 156), (82 170), (180 72), (29 142), (46 41), (59 155), (124 106), (157 80), (175 82), (56 50), (62 116), (113 95), (144 167))", + "POLYGON ((18 142, 21 151, 27 160, 35 167, 44 172, 54 175, 64 178, 74 180, 84 181, 94 181, 104 181, 114 181, 124 181, 134 179, 144 177, 153 173, 162 168, 171 162, 177 154, 182 145, 184 135, 173 134, 161 134, 150 133, 139 132, 136 142, 128 149, 119 153, 109 155, 99 155, 89 155, 79 153, 69 150, 61 144, 63 134, 72 128, 82 125, 92 123, 102 121, 112 119, 122 118, 132 116, 142 113, 151 110, 161 106, 170 102, 178 96, 185 88, 189 78, 190 68, 189 58, 185 49, 179 41, 171 34, 162 29, 153 25, 143 23, 133 21, 123 19, 113 19, 102 19, 92 19, 82 19, 72 21, 62 22, 52 25, 43 29, 33 34, 25 41, 19 49, 14 58, 10 72, 21 73, 31 74, 42 74, 53 76, 56 66, 63 58, 71 51, 81 48, 91 46, 101 45, 111 46, 121 47, 131 50, 140 55, 145 64, 144 74, 135 80, 125 83, 115 85, 105 87, 95 89, 85 91, 75 93, 65 95, 55 98, 45 102, 37 107, 29 114, 22 122, 19 132, 18 142))" + ) { function.percentageConvexInput.value = 0.1 } + + testNullGeometryProcessing( + "Calling ST_ConcaveHullNoHolesAllowed on a NULL input" + ) + } + + override val function = STConcaveHullNoHolesAllowed().apply { + percentageConvexInput = Float8Holder() + binaryInput = NullableVarBinaryHolder() + binaryOutput = NullableVarBinaryHolder() + buffer = allocateBuffer() + } + + override val STConcaveHullNoHolesAllowed.wkbInput: NullableVarBinaryHolder get() = function.binaryInput + override val STConcaveHullNoHolesAllowed.wkbOutput: NullableVarBinaryHolder get() = function.binaryOutput +} diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STConcaveHullTests.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STConcaveHullTests.kt new file mode 100644 index 0000000..ad235a1 --- /dev/null +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STConcaveHullTests.kt @@ -0,0 +1,47 @@ +@file:Suppress("MaximumLineLength") + +package org.sheinbergon.dremio.udf.gis + +import org.apache.arrow.vector.holders.BitHolder +import org.apache.arrow.vector.holders.Float8Holder +import org.apache.arrow.vector.holders.NullableVarBinaryHolder +import org.sheinbergon.dremio.udf.gis.spec.GeometryProcessingFunSpec +import org.sheinbergon.dremio.udf.gis.util.GeometryHelpers +import org.sheinbergon.dremio.udf.gis.util.allocateBuffer +import org.sheinbergon.dremio.udf.gis.util.reset + +@Suppress("MaxLineLength") +internal class STConcaveHullTests : GeometryProcessingFunSpec() { + + init { + + beforeEach { + function.allowHolesInput.reset() + function.percentageConvexInput.reset() + } + + testGeometryProcessing( + "Calling ST_ConcaveHull on a geometry collection", + "MULTIPOINT ((132 64), (114 64), (99 64), (81 64), (63 64), (57 49), (52 36), (46 20), (37 20), (26 20), (32 36), (39 55), (43 69), (50 84), (57 100), (63 118), (68 133), (74 149), (81 164), (88 180), (101 180), (112 180), (119 164), (126 149), (132 131), (139 113), (143 100), (150 84), (157 69), (163 51), (168 36), (174 20), (163 20), (150 20), (143 36), (139 49), (132 64), (99 151), (92 138), (88 124), (81 109), (74 93), (70 82), (83 82), (99 82), (112 82), (126 82), (121 96), (114 109), (110 122), (103 138), (99 151), (34 27), (43 31), (48 44), (46 58), (52 73), (63 73), (61 84), (72 71), (90 69), (101 76), (123 71), (141 62), (166 27), (150 33), (159 36), (146 44), (154 53), (152 62), (146 73), (134 76), (143 82), (141 91), (130 98), (126 104), (132 113), (128 127), (117 122), (112 133), (119 144), (108 147), (119 153), (110 171), (103 164), (92 171), (86 160), (88 142), (79 140), (72 124), (83 131), (79 118), (68 113), (63 102), (68 93), (35 45))", + "POLYGON ((43 69, 50 84, 57 100, 63 118, 68 133, 74 149, 81 164, 88 180, 101 180, 112 180, 119 164, 126 149, 132 131, 139 113, 143 100, 150 84, 157 69, 163 51, 168 36, 174 20, 163 20, 150 20, 143 36, 139 49, 132 64, 114 64, 99 64, 81 64, 63 64, 57 49, 52 36, 46 20, 37 20, 26 20, 32 36, 35 45, 39 55, 43 69), (88 124, 81 109, 74 93, 83 82, 99 82, 112 82, 121 96, 114 109, 110 122, 103 138, 92 138, 88 124))" + ) { + function.allowHolesInput.value = GeometryHelpers.BIT_TRUE + function.percentageConvexInput.value = 0.15 + } + + testNullGeometryProcessing( + "Calling ST_ConcaveHull on a NULL input" + ) + } + + override val function = STConcaveHull().apply { + percentageConvexInput = Float8Holder() + allowHolesInput = BitHolder() + binaryInput = NullableVarBinaryHolder() + binaryOutput = NullableVarBinaryHolder() + buffer = allocateBuffer() + } + + override val STConcaveHull.wkbInput: NullableVarBinaryHolder get() = function.binaryInput + override val STConcaveHull.wkbOutput: NullableVarBinaryHolder get() = function.binaryOutput +} diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STConvexHullTests.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STConvexHullTests.kt index a45c512..855d00c 100644 --- a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STConvexHullTests.kt +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STConvexHullTests.kt @@ -8,13 +8,13 @@ internal class STConvexHullTests : GeometryProcessingFunSpec() { init { testGeometryProcessing( - "Calling STConvexHull on a geometry collection", + "Calling ST_ConvexHull on a geometry collection", "GEOMETRYCOLLECTION(MULTILINESTRING((100 190,10 8),(150 10, 20 30)),MULTIPOINT(50 5, 150 30, 50 10, 10 10))", "POLYGON((50 5,10 8,10 10,100 190,150 30,150 10,50 5))" ) testNullGeometryProcessing( - "Calling STConvexHull on a NULL input" + "Calling ST_ConvexHull on a NULL input" ) } diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STDistanceTests.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STDistanceTests.kt index 88b05c6..f918921 100644 --- a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STDistanceTests.kt +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STDistanceTests.kt @@ -1,6 +1,6 @@ package org.sheinbergon.dremio.udf.gis -import org.apache.arrow.vector.holders.Float8Holder +import org.apache.arrow.vector.holders.NullableFloat8Holder import org.apache.arrow.vector.holders.NullableVarBinaryHolder import org.sheinbergon.dremio.udf.gis.spec.GeometryMeasurementFunSpec @@ -14,15 +14,22 @@ internal class STDistanceTests : GeometryMeasurementFunSpec.Binary() 4326, 0.0015056772638228177 ) + + testNullGeometryMeasurement( + "Calling ST_Distance on 2 LINESTRINGs, one of them is null", + "LINESTRING(0 0, 0.3 0.7, 1 1)", + null, + 4326 + ) } override val function = STDistance().apply { binaryInput1 = NullableVarBinaryHolder() binaryInput2 = NullableVarBinaryHolder() - output = Float8Holder() + output = NullableFloat8Holder() } override val STDistance.wkbInput1: NullableVarBinaryHolder get() = function.binaryInput1 override val STDistance.wkbInput2: NullableVarBinaryHolder get() = function.binaryInput2 - override val STDistance.measurementOutput: Float8Holder get() = function.output + override val STDistance.measurementOutput: NullableFloat8Holder get() = function.output } diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STGeomFromEWKBTests.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STGeomFromEWKBTests.kt new file mode 100644 index 0000000..428476f --- /dev/null +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STGeomFromEWKBTests.kt @@ -0,0 +1,40 @@ +package org.sheinbergon.dremio.udf.gis + +import org.apache.arrow.vector.holders.NullableVarBinaryHolder +import org.sheinbergon.dremio.udf.gis.spec.GeometryInputFunSpec +import org.sheinbergon.dremio.udf.gis.util.allocateBuffer + +internal class STGeomFromEWKBTests : GeometryInputFunSpec.NullableVarBinary() { + + init { + testGeometryInput( + "Calling ST_GeomFromEWKT on an EWKB POINT", + byteArrayOf(1, 1, 0, 0, 32, -26, 16, 0, 0, 0, 0, 0, 0, 0, 0, -32, 63, 0, 0, 0, 0, 0, 0, -32, 63), + byteArrayOf(1, 1, 0, 0, 32, -26, 16, 0, 0, 0, 0, 0, 0, 0, 0, -32, 63, 0, 0, 0, 0, 0, 0, -32, 63) + ) + + testInvalidGeometryInput( + "Calling ST_GeomFromEWKB on rubbish data", + byteArrayOf(12, 9, 8, 11, 2, 0, 9, 10, 0, 0, 11, 22, 33, 0, 0, 52, 53, 0, 22, 0, 0, 0, 0, -32, 63), + ) + + testGeometryInput( + "Calling ST_GeomFromEWKB on WKB", + byteArrayOf(1, 1, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -32, 63, 0, 0, 0, 0, 0, 0, -32, 63), + byteArrayOf(1, 1, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -32, 63, 0, 0, 0, 0, 0, 0, -32, 63), + ) + + testNullGeometryInput( + "Calling ST_GeomFromEWKB on null input" + ) + } + + override val function = STGeomFromEWKB().apply { + ewkbInput = NullableVarBinaryHolder() + binaryOutput = NullableVarBinaryHolder() + buffer = allocateBuffer() + } + + override val STGeomFromEWKB.input: NullableVarBinaryHolder get() = function.ewkbInput + override val STGeomFromEWKB.output: NullableVarBinaryHolder get() = function.binaryOutput +} diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STGeomFromEWKTTests.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STGeomFromEWKTTests.kt index 86f97e9..dbd77ac 100644 --- a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STGeomFromEWKTTests.kt +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STGeomFromEWKTTests.kt @@ -11,7 +11,7 @@ internal class STGeomFromEWKTTests : GeometryInputFunSpec.NullableVarChar() { + + init { + testGeometryAccessor( + "Calling ST_IsSimple on a self-intersecting Polygon", + "POLYGON((1 2, 3 4, 5 6, 1 2))", + false + ) + + testGeometryAccessor( + "Calling ST_IsSimple on a simple LineString", + "LINESTRING(2 1, 1 3, 6 6, 5 7, 5 6)", + true + ) + + testNullGeometryAccessor( + "Calling ST_IsSimple on null input returns false", + ) + } + + override val function = STIsSimple().apply { + binaryInput = NullableVarBinaryHolder() + output = NullableBitHolder() + } + + override val STIsSimple.wkbInput: NullableVarBinaryHolder get() = function.binaryInput + override val STIsSimple.output: NullableBitHolder get() = function.output +} diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STLengthTests.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STLengthTests.kt index 975c86b..f03f6b2 100644 --- a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STLengthTests.kt +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STLengthTests.kt @@ -1,6 +1,6 @@ package org.sheinbergon.dremio.udf.gis -import org.apache.arrow.vector.holders.Float8Holder +import org.apache.arrow.vector.holders.NullableFloat8Holder import org.apache.arrow.vector.holders.NullableVarBinaryHolder import org.sheinbergon.dremio.udf.gis.spec.GeometryMeasurementFunSpec @@ -27,12 +27,16 @@ internal class STLengthTests : GeometryMeasurementFunSpec.Unary() { 32608, 2.0 ) + + testNullGeometryMeasurement( + "Calling ST_Length on a NULL input" + ) } override val function = STLength().apply { binaryInput = NullableVarBinaryHolder() - output = Float8Holder() + output = NullableFloat8Holder() } override val STLength.wkbInput1: NullableVarBinaryHolder get() = function.binaryInput - override val STLength.measurementOutput: Float8Holder get() = function.output + override val STLength.measurementOutput: NullableFloat8Holder get() = function.output } diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STPerimeterTests.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STPerimeterTests.kt index 14dae0f..ee9592a 100644 --- a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STPerimeterTests.kt +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STPerimeterTests.kt @@ -1,6 +1,6 @@ package org.sheinbergon.dremio.udf.gis -import org.apache.arrow.vector.holders.Float8Holder +import org.apache.arrow.vector.holders.NullableFloat8Holder import org.apache.arrow.vector.holders.NullableVarBinaryHolder import org.sheinbergon.dremio.udf.gis.spec.GeometryMeasurementFunSpec @@ -27,12 +27,16 @@ internal class STPerimeterTests : GeometryMeasurementFunSpec.Unary( 2279, 122.63074400009504 ) + + testNullGeometryMeasurement( + "Calling ST_Perimeter on a NULL input" + ) } override val function = STPerimeter().apply { binaryInput = NullableVarBinaryHolder() - output = Float8Holder() + output = NullableFloat8Holder() } override val STPerimeter.wkbInput1: NullableVarBinaryHolder get() = function.binaryInput - override val STPerimeter.measurementOutput: Float8Holder get() = function.output + override val STPerimeter.measurementOutput: NullableFloat8Holder get() = function.output } diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STSimplifyTests.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STSimplifyTests.kt new file mode 100644 index 0000000..3cc9b36 --- /dev/null +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STSimplifyTests.kt @@ -0,0 +1,37 @@ +package org.sheinbergon.dremio.udf.gis + +import org.apache.arrow.vector.holders.Float8Holder +import org.apache.arrow.vector.holders.NullableVarBinaryHolder +import org.sheinbergon.dremio.udf.gis.spec.GeometryProcessingFunSpec +import org.sheinbergon.dremio.udf.gis.util.allocateBuffer +import org.sheinbergon.dremio.udf.gis.util.reset + +internal class STSimplifyTests : GeometryProcessingFunSpec() { + + init { + + beforeEach { + function.toleranceInput.reset() + } + + testGeometryProcessing( + name = "Calling ST_Simplify on a LINESTRING with a tolerance of 1000", + wkt = "LINESTRING(-122.306067 37.55412,-122.32328 37.561801,-122.325879 37.586852)", + expected = "LINESTRING (-122.30607 37.55412, -122.32588 37.58685)" + ) { function.toleranceInput.value = 1000.0 } + + testNullGeometryProcessing( + "Calling ST_Simplify on a NULL input" + ) + } + + override val function = STSimplify().apply { + binaryInput = NullableVarBinaryHolder() + toleranceInput = Float8Holder() + binaryOutput = NullableVarBinaryHolder() + buffer = allocateBuffer() + } + + override val STSimplify.wkbInput: NullableVarBinaryHolder get() = function.binaryInput + override val STSimplify.wkbOutput: NullableVarBinaryHolder get() = function.binaryOutput +} diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STUnionAggregateTests.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STUnionAggregateTests.kt index d7842c4..b0bee91 100644 --- a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STUnionAggregateTests.kt +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/STUnionAggregateTests.kt @@ -8,7 +8,7 @@ import org.sheinbergon.dremio.udf.gis.util.allocateBuffer internal class STUnionAggregateTests : GeometryAggregationFunSpec() { init { - testGeometryAggegration( + testGeometryAggregration( "Calling ST_UNION on several GEOMETRY types returns their union", arrayOf( "POLYGON((0 0, 4 0, 4 4, 0 4, 0 0))", @@ -19,7 +19,7 @@ internal class STUnionAggregateTests : GeometryAggregationFunSpec : FunSpec() { - protected fun testGeometryAggegration( + protected fun testGeometryAggregration( name: String, wkts: Array, expected: String @@ -27,12 +27,12 @@ abstract class GeometryAggregationFunSpec : FunSpec() { add() } output() - wkbOutput.valueIsAsDescribedIn(expected) + wkbOutput.valueIsAsDescribedInWKT(expected) setIndicator.valueIsTrue() } } - protected fun testGeometryAggegrationNoInput( + protected fun testGeometryAggregationNoInput( name: String, ) = test(name) { function.apply { diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/spec/GeometryInputFunSpec.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/spec/GeometryInputFunSpec.kt index ecec5f2..60f073e 100644 --- a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/spec/GeometryInputFunSpec.kt +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/spec/GeometryInputFunSpec.kt @@ -97,7 +97,7 @@ abstract class GeometryInputFunSpec : FunSpec() { } } + protected fun testNullGeometryMeasurement( + name: String, + wkt1: String? = null, + wkt2: String? = null, + wkt3: String? = null, + wkt4: String? = null, + srid: Int, + ) = test(name) { + function.apply { + wkt1?.also { wkbInput1.setFromWkt(wkt1, srid) } + wkt2?.also { wkbInput2.setFromWkt(wkt2, srid) } + wkt3?.also { wkbInput2.setFromWkt(wkt3, srid) } + wkt4?.also { wkbInput2.setFromWkt(wkt4, srid) } + setup() + eval() + measurementOutput.valueIsNotSet() + } + } + protected abstract val F.wkbInput4: NullableVarBinaryHolder } @@ -74,6 +96,23 @@ abstract class GeometryMeasurementFunSpec : FunSpec() { } } + protected fun testNullGeometryMeasurement( + name: String, + wkt1: String? = null, + wkt2: String? = null, + wkt3: String? = null, + srid: Int, + ) = test(name) { + function.apply { + wkt1?.also { wkbInput1.setFromWkt(wkt1, srid) } + wkt2?.also { wkbInput2.setFromWkt(wkt2, srid) } + wkt3?.also { wkbInput2.setFromWkt(wkt3, srid) } + setup() + eval() + measurementOutput.valueIsNotSet() + } + } + protected abstract val F.wkbInput3: NullableVarBinaryHolder } @@ -120,6 +159,21 @@ abstract class GeometryMeasurementFunSpec : FunSpec() { } } + protected fun testNullGeometryMeasurement( + name: String, + wkt1: String? = null, + wkt2: String? = null, + srid: Int, + ) = test(name) { + function.apply { + wkt1?.also { wkbInput1.setFromWkt(wkt1, srid) } + wkt2?.also { wkbInput2.setFromWkt(wkt2, srid) } + setup() + eval() + measurementOutput.valueIsNotSet() + } + } + protected abstract val F.wkbInput2: NullableVarBinaryHolder } @@ -150,15 +204,35 @@ abstract class GeometryMeasurementFunSpec : FunSpec() { } } + protected fun testNullGeometryMeasurement( + name: String, + precursor: suspend TestScope.() -> Unit = {} + ) = test(name) { + precursor(this) + function.apply { + GeometryHelpers.markHolderNotSet(wkbInput1) + setup() + eval() + measurementOutput.valueIsNotSet() + } + } + protected abstract val F.wkbInput1: NullableVarBinaryHolder } protected abstract val function: F - protected abstract val F.measurementOutput: Float8Holder + protected abstract val F.measurementOutput: NullableFloat8Holder - protected fun Float8Holder.isSetTo(double: Double) { + protected fun NullableFloat8Holder.isSetTo(double: Double) { run { value shouldBeExactly double + isSet shouldBe 1 + } + } + + protected fun NullableFloat8Holder.valueIsNotSet() { + run { + isSet shouldBe 0 } } } diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/spec/GeometryOperatorsFunSpec.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/spec/GeometryOperatorsFunSpec.kt index a26a629..c4cbba0 100644 --- a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/spec/GeometryOperatorsFunSpec.kt +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/spec/GeometryOperatorsFunSpec.kt @@ -6,7 +6,7 @@ import org.apache.arrow.vector.holders.NullableVarBinaryHolder import org.sheinbergon.dremio.udf.gis.util.release import org.sheinbergon.dremio.udf.gis.util.reset import org.sheinbergon.dremio.udf.gis.util.setFromWkt -import org.sheinbergon.dremio.udf.gis.util.valueIsAsDescribedIn +import org.sheinbergon.dremio.udf.gis.util.valueIsAsDescribedInWKT abstract class GeometryOperatorsFunSpec : FunSpec() { @@ -21,7 +21,7 @@ abstract class GeometryOperatorsFunSpec : FunSpec() { wkbInput2.setFromWkt(wkt2) setup() eval() - wkbOutput.valueIsAsDescribedIn(expected) + wkbOutput.valueIsAsDescribedInWKT(expected) } } diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/spec/GeometryOutputFunSpec.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/spec/GeometryOutputFunSpec.kt index 7dadf18..3b47295 100644 --- a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/spec/GeometryOutputFunSpec.kt +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/spec/GeometryOutputFunSpec.kt @@ -5,54 +5,110 @@ import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe import org.apache.arrow.vector.holders.NullableVarBinaryHolder import org.apache.arrow.vector.holders.NullableVarCharHolder +import org.apache.arrow.vector.holders.ValueHolder import org.sheinbergon.dremio.udf.gis.util.GeometryHelpers import org.sheinbergon.dremio.udf.gis.util.release import org.sheinbergon.dremio.udf.gis.util.reset import org.sheinbergon.dremio.udf.gis.util.setFromWkt +import org.sheinbergon.dremio.udf.gis.util.valueIsAsDescribedInEWKT +import org.sheinbergon.dremio.udf.gis.util.valueIsAsDescribedInWKT import org.sheinbergon.dremio.udf.gis.util.valueIsNotSet -abstract class GeometryOutputFunSpec : FunSpec() { +abstract class GeometryOutputFunSpec : FunSpec() { - init { - beforeEach { - function.output.reset() - function.wkbInput.reset() + abstract class NullableVarBinary : GeometryOutputFunSpec() { + init { + beforeEach { + function.output.reset() + } } - afterEach { - function.wkbInput.release() + protected fun testGeometryWKTOutput( + name: String, + wkt: String, + expected: String + ) = test(name) { + function.apply { + wkbInput.setFromWkt(wkt) + setup() + eval() + output.valueIsAsDescribedInWKT(expected) + } } - } - protected fun testGeometryOutput( - name: String, - wkt: String, - srid: Int, - result: String - ) = test(name) { - function.apply { - wkbInput.setFromWkt(wkt, srid) - setup() - eval() - output.valueIs(result) + protected fun testGeometryEWKTOutput( + name: String, + wkt: String, + srid: Int, + expected: String + ) = test(name) { + function.apply { + wkbInput.setFromWkt(wkt, srid) + setup() + eval() + output.valueIsAsDescribedInEWKT(expected) + } + } + + protected fun testNullGeometryOutput( + name: String + ) = test(name) { + function.apply { + wkbInput.isSet = 0 + setup() + eval() + output.valueIsNotSet() + } } } - protected fun testNullGeometryOutput( - name: String - ) = test(name) { - function.apply { - wkbInput.isSet = 0 - setup() - eval() - output.valueIsNotSet() + abstract class NullableVarChar : GeometryOutputFunSpec() { + init { + beforeEach { + function.output.reset() + } + } + + protected fun testGeometryOutput( + name: String, + wkt: String, + srid: Int, + expected: String + ) = test(name) { + function.apply { + wkbInput.setFromWkt(wkt, srid) + setup() + eval() + output.valueIs(expected) + } + } + + protected fun testNullGeometryOutput( + name: String + ) = test(name) { + function.apply { + wkbInput.isSet = 0 + setup() + eval() + output.valueIsNotSet() + } } + + private fun NullableVarCharHolder.valueIs(text: String) = + GeometryHelpers.toUTF8String(this) shouldBe text } - private fun NullableVarCharHolder.valueIs(text: String) = - GeometryHelpers.toUTF8String(this) shouldBe text + init { + beforeEach { + function.wkbInput.reset() + } + + afterEach { + function.wkbInput.release() + } + } protected abstract val function: F protected abstract val F.wkbInput: NullableVarBinaryHolder - protected abstract val F.output: NullableVarCharHolder + protected abstract val F.output: O } diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/spec/GeometryProcessingFunSpec.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/spec/GeometryProcessingFunSpec.kt index 0adb370..5c5c85d 100644 --- a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/spec/GeometryProcessingFunSpec.kt +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/spec/GeometryProcessingFunSpec.kt @@ -8,7 +8,7 @@ import org.sheinbergon.dremio.udf.gis.util.GeometryHelpers import org.sheinbergon.dremio.udf.gis.util.release import org.sheinbergon.dremio.udf.gis.util.reset import org.sheinbergon.dremio.udf.gis.util.setFromWkt -import org.sheinbergon.dremio.udf.gis.util.valueIsAsDescribedIn +import org.sheinbergon.dremio.udf.gis.util.valueIsAsDescribedInWKT import org.sheinbergon.dremio.udf.gis.util.valueIsNotSet abstract class GeometryProcessingFunSpec : FunSpec() { @@ -24,7 +24,7 @@ abstract class GeometryProcessingFunSpec : FunSpec() { wkbInput.setFromWkt(wkt) setup() eval() - wkbOutput.valueIsAsDescribedIn(expected) + wkbOutput.valueIsAsDescribedInWKT(expected) } } diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/spec/GeometryTransformationFunSpec.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/spec/GeometryTransformationFunSpec.kt index 05cf1f1..56c00c3 100644 --- a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/spec/GeometryTransformationFunSpec.kt +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/spec/GeometryTransformationFunSpec.kt @@ -7,7 +7,7 @@ import org.apache.arrow.vector.holders.NullableVarBinaryHolder import org.sheinbergon.dremio.udf.gis.util.release import org.sheinbergon.dremio.udf.gis.util.reset import org.sheinbergon.dremio.udf.gis.util.setFromWkt -import org.sheinbergon.dremio.udf.gis.util.valueIsAsDescribedIn +import org.sheinbergon.dremio.udf.gis.util.valueIsAsDescribedInWKT abstract class GeometryTransformationFunSpec : FunSpec() { @@ -23,7 +23,7 @@ abstract class GeometryTransformationFunSpec : FunSpec() { wkbInput.setFromWkt(wkt, sourceSrid) setup() eval() - wkbOutput.valueIsAsDescribedIn(expected) + wkbOutput.valueIsAsDescribedInWKT(expected) } } diff --git a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/util/ValueHolderTestSupport.kt b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/util/ValueHolderTestSupport.kt index a4ce986..b1f1ca2 100644 --- a/src/test/kotlin/org/sheinbergon/dremio/udf/gis/util/ValueHolderTestSupport.kt +++ b/src/test/kotlin/org/sheinbergon/dremio/udf/gis/util/ValueHolderTestSupport.kt @@ -13,6 +13,7 @@ import org.apache.arrow.vector.holders.NullableIntHolder import org.apache.arrow.vector.holders.NullableVarBinaryHolder import org.apache.arrow.vector.holders.NullableVarCharHolder import org.apache.arrow.vector.holders.VarCharHolder +import org.locationtech.jts.geom.Geometry import org.locationtech.jts.geom.PrecisionModel import org.locationtech.jts.io.WKBWriter import org.locationtech.jts.io.WKTReader @@ -72,13 +73,24 @@ internal fun NullableVarBinaryHolder.setBinary(bytes: ByteArray) { this.buffer = buffer } -internal fun NullableVarBinaryHolder.valueIsAsDescribedIn(text: String) { +internal fun NullableVarBinaryHolder.valueIsAsDescribedInWKT(text: String) = + valueIsAsDescribedIn(text, GeometryHelpers::toGeometry, GeometryHelpers::toBinary) + +internal fun NullableVarBinaryHolder.valueIsAsDescribedInEWKT(text: String) = + valueIsAsDescribedIn(text, GeometryHelpers::toGeometryFromEWKT, GeometryHelpers::toEWKB) + +internal fun NullableVarBinaryHolder.valueIsAsDescribedIn( + text: String, + adapter: (NullableVarCharHolder) -> Geometry, + serializer: (Geometry) -> ByteArray, +) { val evaluated = GeometryHelpers.toGeometry(this) val reduced = GeometryPrecisionReducer.reducePointwise(evaluated, SCALED_PRECISION_MODEL) + reduced.srid = evaluated.srid val expected = NullableVarCharHolder() .apply { setUtf8(text) } - .let(GeometryHelpers::toGeometry) - GeometryHelpers.toBinary(reduced) shouldBe GeometryHelpers.toBinary(expected) + .let(adapter) + serializer(reduced) shouldBe serializer(expected) } internal fun NullableVarBinaryHolder.valueIsNotSet() {