getTypes() {
+ return types;
+ }
+
+ void update(Geometry geometry) {
+ if (!valid) {
+ return;
+ }
+ int code = getGeometryTypeCode(geometry);
+ if (code != UNKNOWN_TYPE_ID) {
+ types.add(code);
+ } else {
+ valid = false;
+ types.clear();
+ }
+ }
+
+ public void merge(GeometryTypes other) {
+ Preconditions.checkArgument(other != null, "Cannot merge with null GeometryTypes");
+ if (!valid) {
+ return;
+ }
+ if (!other.valid) {
+ valid = false;
+ types.clear();
+ return;
+ }
+ types.addAll(other.types);
+ }
+
+ public void reset() {
+ types.clear();
+ valid = true;
+ }
+
+ public void abort() {
+ valid = false;
+ types.clear();
+ }
+
+ public GeometryTypes copy() {
+ return new GeometryTypes(new HashSet<>(types));
+ }
+
+ @Override
+ public String toString() {
+ return "GeometryTypes{" + "types="
+ + types.stream().map(this::typeIdToString).collect(Collectors.toSet()) + '}';
+ }
+
+ private int getGeometryTypeId(Geometry geometry) {
+ switch (geometry.getGeometryType()) {
+ case Geometry.TYPENAME_POINT:
+ return 1;
+ case Geometry.TYPENAME_LINESTRING:
+ return 2;
+ case Geometry.TYPENAME_POLYGON:
+ return 3;
+ case Geometry.TYPENAME_MULTIPOINT:
+ return 4;
+ case Geometry.TYPENAME_MULTILINESTRING:
+ return 5;
+ case Geometry.TYPENAME_MULTIPOLYGON:
+ return 6;
+ case Geometry.TYPENAME_GEOMETRYCOLLECTION:
+ return 7;
+ default:
+ return UNKNOWN_TYPE_ID;
+ }
+ }
+
+ /**
+ * This is from the following spec proposed:
+ *
+ * The geometry types of all geometries, or an empty array if they are not
+ * known. This is borrowed from `geometry_types` column metadata of GeoParquet [1]
+ * except that values in the list are WKB (ISO variant) integer codes [2]. Table
+ * below shows the most common geometry types and their codes:
+ *
+ * | Type | XY | XYZ | XYM | XYZM |
+ * | :----------------- | :--- | :--- | :--- | :--: |
+ * | Point | 0001 | 1001 | 2001 | 3001 |
+ * | LineString | 0002 | 1002 | 2002 | 3002 |
+ * | Polygon | 0003 | 1003 | 2003 | 3003 |
+ * | MultiPoint | 0004 | 1004 | 2004 | 3004 |
+ * | MultiLineString | 0005 | 1005 | 2005 | 3005 |
+ * | MultiPolygon | 0006 | 1006 | 2006 | 3006 |
+ * | GeometryCollection | 0007 | 1007 | 2007 | 3007 |
+ *
+ * In addition, the following rules are used:
+ * - A list of multiple values indicates that multiple geometry types are
+ * present (e.g. `[0003, 0006]`).
+ * - An empty array explicitly signals that the geometry types are not known.
+ * - The geometry types in the list must be unique (e.g. `[0001, 0001]`
+ * is not valid).
+ *
+ * Please refer to links below for more detail:
+ * [1] https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry#Well-known_binary
+ * [2] https://github.com/opengeospatial/geoparquet/blob/v1.0.0/format-specs/geoparquet.md?plain=1#L91
+ */
+ private int getGeometryTypeCode(Geometry geometry) {
+ int typeId = getGeometryTypeId(geometry);
+ if (typeId == UNKNOWN_TYPE_ID) {
+ return UNKNOWN_TYPE_ID;
+ }
+ Coordinate[] coordinates = geometry.getCoordinates();
+ boolean hasZ = false;
+ boolean hasM = false;
+ for (Coordinate coordinate : coordinates) {
+ if (!Double.isNaN(coordinate.getZ())) {
+ hasZ = true;
+ }
+ if (!Double.isNaN(coordinate.getM())) {
+ hasM = true;
+ }
+ if (hasZ && hasM) {
+ break;
+ }
+ }
+ if (hasZ) {
+ typeId += 1000;
+ }
+ if (hasM) {
+ typeId += 2000;
+ }
+ return typeId;
+ }
+
+ private String typeIdToString(int typeId) {
+ String typeString;
+ switch (typeId % 1000) {
+ case 1:
+ typeString = Geometry.TYPENAME_POINT;
+ break;
+ case 2:
+ typeString = Geometry.TYPENAME_LINESTRING;
+ break;
+ case 3:
+ typeString = Geometry.TYPENAME_POLYGON;
+ break;
+ case 4:
+ typeString = Geometry.TYPENAME_MULTIPOINT;
+ break;
+ case 5:
+ typeString = Geometry.TYPENAME_MULTILINESTRING;
+ break;
+ case 6:
+ typeString = Geometry.TYPENAME_MULTIPOLYGON;
+ break;
+ case 7:
+ typeString = Geometry.TYPENAME_GEOMETRYCOLLECTION;
+ break;
+ default:
+ return "Unknown";
+ }
+ if (typeId >= 3000) {
+ typeString += " (XYZM)";
+ } else if (typeId >= 2000) {
+ typeString += " (XYM)";
+ } else if (typeId >= 1000) {
+ typeString += " (XYZ)";
+ } else {
+ typeString += " (XY)";
+ }
+ return typeString;
+ }
+}
diff --git a/parquet-column/src/main/java/org/apache/parquet/column/statistics/geometry/GeometryUtils.java b/parquet-column/src/main/java/org/apache/parquet/column/statistics/geometry/GeometryUtils.java
new file mode 100644
index 0000000000..f91eafe49b
--- /dev/null
+++ b/parquet-column/src/main/java/org/apache/parquet/column/statistics/geometry/GeometryUtils.java
@@ -0,0 +1,56 @@
+/*
+ * 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.apache.parquet.column.statistics.geometry;
+
+import org.locationtech.jts.geom.CoordinateSequence;
+import org.locationtech.jts.geom.CoordinateSequenceFilter;
+import org.locationtech.jts.geom.Geometry;
+
+class GeometryUtils {
+
+ public static void normalizeLongitude(Geometry geometry) {
+ if (geometry == null || geometry.isEmpty()) {
+ return;
+ }
+
+ geometry.apply(new CoordinateSequenceFilter() {
+ @Override
+ public void filter(CoordinateSequence seq, int i) {
+ double x = seq.getX(i);
+ // Normalize the longitude to be within -180 to 180 range
+ while (x > 180) x -= 360;
+ while (x < -180) x += 360;
+ seq.setOrdinate(i, CoordinateSequence.X, x);
+ }
+
+ @Override
+ public boolean isDone() {
+ return false; // Continue processing until all coordinates are processed
+ }
+
+ @Override
+ public boolean isGeometryChanged() {
+ return true; // The geometry is changed as we are modifying the coordinates
+ }
+ });
+
+ geometry.geometryChanged(); // Notify the geometry that its coordinates have been changed
+ }
+}
diff --git a/parquet-column/src/main/java/org/apache/parquet/internal/column/columnindex/ColumnIndex.java b/parquet-column/src/main/java/org/apache/parquet/internal/column/columnindex/ColumnIndex.java
index 86099717df..3b6a210b4e 100644
--- a/parquet-column/src/main/java/org/apache/parquet/internal/column/columnindex/ColumnIndex.java
+++ b/parquet-column/src/main/java/org/apache/parquet/internal/column/columnindex/ColumnIndex.java
@@ -21,6 +21,7 @@
import java.nio.ByteBuffer;
import java.util.List;
import java.util.PrimitiveIterator;
+import org.apache.parquet.column.statistics.geometry.GeometryStatistics;
import org.apache.parquet.filter2.predicate.FilterPredicate.Visitor;
import org.apache.parquet.internal.filter2.columnindex.ColumnIndexFilter;
@@ -71,4 +72,12 @@ default List getRepetitionLevelHistogram() {
default List getDefinitionLevelHistogram() {
throw new UnsupportedOperationException("Definition level histogram is not implemented");
}
+
+ /**
+ * @return the unmodifiable list of the geometry statistics for each page;
+ * used for converting to the related thrift object
+ */
+ default List getGeometryStatistics() {
+ throw new UnsupportedOperationException("Geometry statistics is not implemented");
+ }
}
diff --git a/parquet-column/src/main/java/org/apache/parquet/internal/column/columnindex/ColumnIndexBuilder.java b/parquet-column/src/main/java/org/apache/parquet/internal/column/columnindex/ColumnIndexBuilder.java
index ffbb82197b..7e41a976c8 100644
--- a/parquet-column/src/main/java/org/apache/parquet/internal/column/columnindex/ColumnIndexBuilder.java
+++ b/parquet-column/src/main/java/org/apache/parquet/internal/column/columnindex/ColumnIndexBuilder.java
@@ -38,8 +38,10 @@
import java.util.Set;
import java.util.function.IntPredicate;
import org.apache.parquet.column.MinMax;
+import org.apache.parquet.column.statistics.BinaryStatistics;
import org.apache.parquet.column.statistics.SizeStatistics;
import org.apache.parquet.column.statistics.Statistics;
+import org.apache.parquet.column.statistics.geometry.GeometryStatistics;
import org.apache.parquet.filter2.predicate.Operators.And;
import org.apache.parquet.filter2.predicate.Operators.Contains;
import org.apache.parquet.filter2.predicate.Operators.Eq;
@@ -56,6 +58,7 @@
import org.apache.parquet.filter2.predicate.Operators.UserDefined;
import org.apache.parquet.filter2.predicate.UserDefinedPredicate;
import org.apache.parquet.io.api.Binary;
+import org.apache.parquet.schema.LogicalTypeAnnotation;
import org.apache.parquet.schema.PrimitiveComparator;
import org.apache.parquet.schema.PrimitiveStringifier;
import org.apache.parquet.schema.PrimitiveType;
@@ -105,6 +108,8 @@ int translate(int arrayIndex) {
private long[] repLevelHistogram;
// might be null
private long[] defLevelHistogram;
+ // might be null
+ private GeometryStatistics[] geometryStatistics;
static String truncate(String str) {
if (str.length() <= MAX_VALUE_LENGTH_FOR_TOSTRING) {
@@ -182,6 +187,17 @@ public List getDefinitionLevelHistogram() {
return LongLists.unmodifiable(LongArrayList.wrap(defLevelHistogram));
}
+ @Override
+ public List getGeometryStatistics() {
+ List geomStats = new ArrayList<>();
+ if (geometryStatistics != null) {
+ for (GeometryStatistics stats : geometryStatistics) {
+ geomStats.add(stats.copy());
+ }
+ }
+ return geomStats;
+ }
+
@Override
public String toString() {
try (Formatter formatter = new Formatter()) {
@@ -494,6 +510,7 @@ public long getMinMaxSize() {
private int nextPageIndex;
private LongList repLevelHistogram = new LongArrayList();
private LongList defLevelHistogram = new LongArrayList();
+ private List geometryStatistics = new ArrayList<>();
/**
* @return a no-op builder that does not collect statistics objects and therefore returns {@code null} at
@@ -584,10 +601,46 @@ public static ColumnIndex build(
List maxValues,
List repLevelHistogram,
List defLevelHistogram) {
+ return build(type, boundaryOrder, nullPages, nullCounts, minValues, maxValues, null, null, null);
+ }
+
+ /**
+ * @param type
+ * the primitive type
+ * @param boundaryOrder
+ * the boundary order of the min/max values
+ * @param nullPages
+ * the null pages (one boolean value for each page that signifies whether the page consists of nulls
+ * entirely)
+ * @param nullCounts
+ * the number of null values for each page
+ * @param minValues
+ * the min values for each page
+ * @param maxValues
+ * the max values for each page
+ * @param repLevelHistogram
+ * the repetition level histogram for all levels of each page
+ * @param defLevelHistogram
+ * the definition level histogram for all levels of each page
+ * @param geometryStatistics
+ * the geometry statistics for each page (apply to GEOMETRY logical type only)
+ * @return the newly created {@link ColumnIndex} object based on the specified arguments
+ */
+ public static ColumnIndex build(
+ PrimitiveType type,
+ BoundaryOrder boundaryOrder,
+ List nullPages,
+ List nullCounts,
+ List minValues,
+ List maxValues,
+ List repLevelHistogram,
+ List defLevelHistogram,
+ List geometryStatistics) {
ColumnIndexBuilder builder = createNewBuilder(type, Integer.MAX_VALUE);
- builder.fill(nullPages, nullCounts, minValues, maxValues, repLevelHistogram, defLevelHistogram);
+ builder.fill(
+ nullPages, nullCounts, minValues, maxValues, repLevelHistogram, defLevelHistogram, geometryStatistics);
ColumnIndexBase> columnIndex = builder.build(type);
columnIndex.boundaryOrder = requireNonNull(boundaryOrder);
return columnIndex;
@@ -635,6 +688,16 @@ public void add(Statistics> stats, SizeStatistics sizeStats) {
defLevelHistogram = null;
}
+ if (type.getLogicalTypeAnnotation() instanceof LogicalTypeAnnotation.GeometryLogicalTypeAnnotation) {
+ assert stats instanceof BinaryStatistics;
+ BinaryStatistics binaryStats = (BinaryStatistics) stats;
+ if (geometryStatistics != null && binaryStats.getGeometryStatistics() != null) {
+ geometryStatistics.add(binaryStats.getGeometryStatistics());
+ } else {
+ geometryStatistics = null;
+ }
+ }
+
++nextPageIndex;
}
@@ -648,7 +711,8 @@ private void fill(
List minValues,
List maxValues,
List repLevelHistogram,
- List defLevelHistogram) {
+ List defLevelHistogram,
+ List geometryStatistics) {
clear();
int pageCount = nullPages.size();
if ((nullCounts != null && nullCounts.size() != pageCount)
@@ -695,6 +759,9 @@ private void fill(
if (defLevelHistogram != null) {
this.defLevelHistogram.addAll(defLevelHistogram);
}
+ if (geometryStatistics != null) {
+ this.geometryStatistics.addAll(geometryStatistics);
+ }
}
/**
@@ -731,6 +798,10 @@ private ColumnIndexBase> build(PrimitiveType type) {
if (defLevelHistogram != null && !defLevelHistogram.isEmpty()) {
columnIndex.defLevelHistogram = defLevelHistogram.toLongArray();
}
+ if (geometryStatistics != null && !geometryStatistics.isEmpty()) {
+ columnIndex.geometryStatistics = new GeometryStatistics[geometryStatistics.size()];
+ geometryStatistics.toArray(columnIndex.geometryStatistics);
+ }
return columnIndex;
}
@@ -777,6 +848,7 @@ private void clear() {
pageIndexes.clear();
repLevelHistogram.clear();
defLevelHistogram.clear();
+ geometryStatistics.clear();
}
abstract void clearMinMax();
diff --git a/parquet-column/src/main/java/org/apache/parquet/schema/LogicalTypeAnnotation.java b/parquet-column/src/main/java/org/apache/parquet/schema/LogicalTypeAnnotation.java
index 05629dd388..6e0a28d263 100644
--- a/parquet-column/src/main/java/org/apache/parquet/schema/LogicalTypeAnnotation.java
+++ b/parquet-column/src/main/java/org/apache/parquet/schema/LogicalTypeAnnotation.java
@@ -33,6 +33,7 @@
import static org.apache.parquet.schema.PrimitiveStringifier.TIME_STRINGIFIER;
import static org.apache.parquet.schema.PrimitiveStringifier.TIME_UTC_STRINGIFIER;
+import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -146,6 +147,22 @@ protected LogicalTypeAnnotation fromString(List params) {
protected LogicalTypeAnnotation fromString(List params) {
return float16Type();
}
+ },
+ GEOMETRY {
+ @Override
+ protected LogicalTypeAnnotation fromString(List params) {
+ if (params.size() < 2) {
+ throw new RuntimeException(
+ "Expecting at least 2 parameters for geometry logical type, got " + params.size());
+ }
+ GeometryEncoding encoding = GeometryEncoding.valueOf(params.get(0));
+ Edges edges = Edges.valueOf(params.get(1));
+ String crs = params.size() > 2 ? params.get(2) : null;
+ String crs_encoding = params.size() > 3 ? params.get(3) : null;
+ ByteBuffer metadata =
+ params.size() > 4 ? ByteBuffer.wrap(params.get(4).getBytes()) : null;
+ return geometryType(encoding, edges, crs, crs_encoding, metadata);
+ }
};
protected abstract LogicalTypeAnnotation fromString(List params);
@@ -316,6 +333,11 @@ public static Float16LogicalTypeAnnotation float16Type() {
return Float16LogicalTypeAnnotation.INSTANCE;
}
+ public static GeometryLogicalTypeAnnotation geometryType(
+ GeometryEncoding encoding, Edges edges, String crs, String crs_encoding, ByteBuffer metadata) {
+ return new GeometryLogicalTypeAnnotation(encoding, edges, crs, crs_encoding, metadata);
+ }
+
public static class StringLogicalTypeAnnotation extends LogicalTypeAnnotation {
private static final StringLogicalTypeAnnotation INSTANCE = new StringLogicalTypeAnnotation();
@@ -1091,6 +1113,129 @@ public int hashCode() {
}
}
+ /**
+ * Allowed for physical type: BYTE_ARRAY.
+ *
+ * Well-known binary (WKB) representations of geometries. It supports 2D or
+ * 3D geometries of the standard geometry types (Point, LineString, Polygon,
+ * MultiPoint, MultiLineString, MultiPolygon, and GeometryCollection). This
+ * is the preferred option for maximum portability.
+ *
+ * This encoding enables GeometryStatistics to be set in the column chunk
+ * and page index.
+ */
+ public enum GeometryEncoding {
+ WKB
+ }
+
+ /**
+ * Interpretation for edges of GEOMETRY logical type, i.e. whether the edge
+ * between points represent a straight cartesian line or the shortest line on
+ * the sphere. Please note that it only applies to polygons.
+ */
+ public enum Edges {
+ PLANAR,
+ SPHERICAL
+ }
+
+ public static class GeometryLogicalTypeAnnotation extends LogicalTypeAnnotation {
+ private final GeometryEncoding encoding;
+ private final Edges edges;
+ private final String crs;
+ private final String crs_encoding;
+ private final ByteBuffer metadata;
+
+ private GeometryLogicalTypeAnnotation(
+ GeometryEncoding encoding, Edges edges, String crs, String crs_encoding, ByteBuffer metadata) {
+ Preconditions.checkArgument(encoding != null, "Geometry encoding is required");
+ Preconditions.checkArgument(edges != null, "Geometry edges is required");
+ this.encoding = encoding;
+ this.edges = edges;
+ this.crs = crs;
+ this.crs_encoding = crs_encoding;
+ this.metadata = metadata;
+ }
+
+ @Override
+ @Deprecated
+ public OriginalType toOriginalType() {
+ return null;
+ }
+
+ @Override
+ public Optional accept(LogicalTypeAnnotationVisitor logicalTypeAnnotationVisitor) {
+ return logicalTypeAnnotationVisitor.visit(this);
+ }
+
+ @Override
+ LogicalTypeToken getType() {
+ return LogicalTypeToken.GEOMETRY;
+ }
+
+ @Override
+ protected String typeParametersAsString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("(");
+ sb.append(encoding);
+ sb.append(",");
+ sb.append(edges);
+ if (crs != null && !crs.isEmpty()) {
+ sb.append(",");
+ sb.append(crs);
+ }
+ // TODO: Fix it: there's a high probability that crs itself contains comma,
+ // so this may introduce ambiguity to the generated type parameters.
+ if (metadata != null) {
+ sb.append(",");
+ sb.append(metadata);
+ }
+ sb.append(")");
+ return sb.toString();
+ }
+
+ public GeometryEncoding getEncoding() {
+ return encoding;
+ }
+
+ public Edges getEdges() {
+ return edges;
+ }
+
+ public String getCrs() {
+ return crs;
+ }
+
+ public String getCrs_encoding() {
+ return crs_encoding;
+ }
+
+ public ByteBuffer getMetadata() {
+ return metadata;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof GeometryLogicalTypeAnnotation)) {
+ return false;
+ }
+ GeometryLogicalTypeAnnotation other = (GeometryLogicalTypeAnnotation) obj;
+ return (encoding == other.encoding) && (edges == other.edges) && crs.equals(other.crs);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(encoding, crs, edges);
+ }
+
+ @Override
+ PrimitiveStringifier valueStringifier(PrimitiveType primitiveType) {
+ if (encoding == GeometryEncoding.WKB) {
+ return PrimitiveStringifier.WKB_STRINGIFIER;
+ }
+ return super.valueStringifier(primitiveType);
+ }
+ }
+
/**
* Implement this interface to visit a logical type annotation in the schema.
* The default implementation for each logical type specific visitor method is empty.
@@ -1162,5 +1307,9 @@ default Optional visit(MapKeyValueTypeAnnotation mapKeyValueLogicalType) {
default Optional visit(Float16LogicalTypeAnnotation float16LogicalType) {
return empty();
}
+
+ default Optional visit(GeometryLogicalTypeAnnotation geometryLogicalType) {
+ return empty();
+ }
}
}
diff --git a/parquet-column/src/main/java/org/apache/parquet/schema/PrimitiveStringifier.java b/parquet-column/src/main/java/org/apache/parquet/schema/PrimitiveStringifier.java
index c46e94367f..bb5c8a9474 100644
--- a/parquet-column/src/main/java/org/apache/parquet/schema/PrimitiveStringifier.java
+++ b/parquet-column/src/main/java/org/apache/parquet/schema/PrimitiveStringifier.java
@@ -35,6 +35,9 @@
import java.util.concurrent.TimeUnit;
import javax.naming.OperationNotSupportedException;
import org.apache.parquet.io.api.Binary;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.io.ParseException;
+import org.locationtech.jts.io.WKBReader;
/**
* Class that provides string representations for the primitive values. These string values are to be used for
@@ -449,4 +452,20 @@ String stringifyNotNull(Binary value) {
return Float16.toFloatString(value);
}
};
+
+ static final PrimitiveStringifier WKB_STRINGIFIER = new BinaryStringifierBase("WKB_STRINGIFIER") {
+
+ @Override
+ String stringifyNotNull(Binary value) {
+
+ Geometry geometry;
+ try {
+ WKBReader reader = new WKBReader();
+ geometry = reader.read(value.getBytesUnsafe());
+ return geometry.toText();
+ } catch (ParseException e) {
+ return BINARY_INVALID;
+ }
+ }
+ };
}
diff --git a/parquet-column/src/main/java/org/apache/parquet/schema/PrimitiveType.java b/parquet-column/src/main/java/org/apache/parquet/schema/PrimitiveType.java
index e74d7cde02..f08e20333d 100644
--- a/parquet-column/src/main/java/org/apache/parquet/schema/PrimitiveType.java
+++ b/parquet-column/src/main/java/org/apache/parquet/schema/PrimitiveType.java
@@ -271,6 +271,14 @@ public Optional visit(
LogicalTypeAnnotation.BsonLogicalTypeAnnotation bsonLogicalType) {
return of(PrimitiveComparator.UNSIGNED_LEXICOGRAPHICAL_BINARY_COMPARATOR);
}
+
+ @Override
+ public Optional visit(
+ LogicalTypeAnnotation.GeometryLogicalTypeAnnotation geometryLogicalType) {
+ // ColumnOrder is undefined for GEOMETRY logical type. Use the default comparator for
+ // now.
+ return of(PrimitiveComparator.UNSIGNED_LEXICOGRAPHICAL_BINARY_COMPARATOR);
+ }
})
.orElseThrow(() -> new ShouldNeverHappenException(
"No comparator logic implemented for BINARY logical type: " + logicalType));
diff --git a/parquet-column/src/main/java/org/apache/parquet/schema/Types.java b/parquet-column/src/main/java/org/apache/parquet/schema/Types.java
index 5bc2f89f47..45985c7a31 100644
--- a/parquet-column/src/main/java/org/apache/parquet/schema/Types.java
+++ b/parquet-column/src/main/java/org/apache/parquet/schema/Types.java
@@ -571,6 +571,15 @@ public Optional visit(
return checkBinaryPrimitiveType(enumLogicalType);
}
+ @Override
+ public Optional visit(
+ LogicalTypeAnnotation.GeometryLogicalTypeAnnotation geometryLogicalType) {
+ if (geometryLogicalType.getEncoding() != LogicalTypeAnnotation.GeometryEncoding.WKB) {
+ throw new RuntimeException("Only WKB geometry encoding is supported for now");
+ }
+ return checkBinaryPrimitiveType(geometryLogicalType);
+ }
+
private Optional checkFixedPrimitiveType(
int l, LogicalTypeAnnotation logicalTypeAnnotation) {
Preconditions.checkState(
diff --git a/parquet-hadoop/pom.xml b/parquet-hadoop/pom.xml
index ce023b5185..95a15ef098 100644
--- a/parquet-hadoop/pom.xml
+++ b/parquet-hadoop/pom.xml
@@ -135,6 +135,12 @@
jar
compile