Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SEDONA-265] Migrate all ST functions to Sedona Inferred Expressions #820

Merged
merged 2 commits into from
Apr 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions common/src/main/java/org/apache/sedona/common/Constructors.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,25 @@
import org.apache.sedona.common.enums.FileDataSplitter;
import org.apache.sedona.common.enums.GeometryType;
import org.apache.sedona.common.utils.FormatUtils;
import org.apache.sedona.common.utils.GeoHashDecoder;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.PrecisionModel;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKBReader;
import org.locationtech.jts.io.WKTReader;
import org.locationtech.jts.io.gml2.GMLReader;
import org.locationtech.jts.io.kml.KMLReader;
import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;

public class Constructors {

private static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory();

public static Geometry geomFromWKT(String wkt, int srid) throws ParseException {
if (wkt == null) {
return null;
Expand All @@ -33,6 +43,10 @@ public static Geometry geomFromWKT(String wkt, int srid) throws ParseException {
return new WKTReader(geometryFactory).read(wkt);
}

public static Geometry geomFromWKB(byte[] wkb) throws ParseException {
return new WKBReader().read(wkb);
}

public static Geometry mLineFromText(String wkt, int srid) throws ParseException {
if (wkt == null || !wkt.startsWith("MULTILINESTRING")) {
return null;
Expand Down Expand Up @@ -100,4 +114,56 @@ public static Geometry geomFromText(String geomString, FileDataSplitter fileData
throw new RuntimeException(e);
}
}

public static Geometry pointFromText(String geomString, String geomFormat) {
return geomFromText(geomString, geomFormat, GeometryType.POINT);
}

public static Geometry polygonFromText(String geomString, String geomFormat) {
return geomFromText(geomString, geomFormat, GeometryType.POLYGON);
}

public static Geometry lineStringFromText(String geomString, String geomFormat) {
return geomFromText(geomString, geomFormat, GeometryType.LINESTRING);
}

public static Geometry lineFromText(String geomString) {
FileDataSplitter fileDataSplitter = FileDataSplitter.WKT;
Geometry geometry = Constructors.geomFromText(geomString, fileDataSplitter);
if(geometry.getGeometryType().contains("LineString")) {
return geometry;
} else {
return null;
}
}

public static Geometry polygonFromEnvelope(double minX, double minY, double maxX, double maxY) {
Coordinate[] coordinates = new Coordinate[5];
coordinates[0] = new Coordinate(minX, minY);
coordinates[1] = new Coordinate(minX, maxY);
coordinates[2] = new Coordinate(maxX, maxY);
coordinates[3] = new Coordinate(maxX, minY);
coordinates[4] = coordinates[0];
return GEOMETRY_FACTORY.createPolygon(coordinates);
}

public static Geometry geomFromGeoHash(String geoHash, Integer precision) {
System.out.println(geoHash);
System.out.println(precision);
try {
return GeoHashDecoder.decode(geoHash, precision);
} catch (GeoHashDecoder.InvalidGeoHashException e) {
return null;
}
}

public static Geometry geomFromGML(String gml) throws IOException, ParserConfigurationException, SAXException {
return new GMLReader().read(gml, GEOMETRY_FACTORY);
}

public static Geometry geomFromKML(String kml) throws ParseException {
return new KMLReader().read(kml);
}


}
105 changes: 100 additions & 5 deletions common/src/main/java/org/apache/sedona/common/Functions.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,8 @@
package org.apache.sedona.common;

import com.google.common.geometry.S2CellId;
import com.google.common.geometry.S2Point;
import com.google.common.geometry.S2Region;
import com.google.common.geometry.S2RegionCoverer;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.sedona.common.geometryObjects.Circle;
import org.apache.sedona.common.subDivide.GeometrySubDivider;
import org.apache.sedona.common.utils.GeomUtils;
import org.apache.sedona.common.utils.GeometryGeoHashEncoder;
import org.apache.sedona.common.utils.GeometrySplitter;
Expand All @@ -37,6 +34,7 @@
import org.locationtech.jts.operation.valid.IsSimpleOp;
import org.locationtech.jts.operation.valid.IsValidOp;
import org.locationtech.jts.precision.GeometryPrecisionReducer;
import org.locationtech.jts.simplify.TopologyPreservingSimplifier;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
Expand All @@ -48,7 +46,6 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;


Expand Down Expand Up @@ -575,4 +572,102 @@ public static Long[] s2CellIDs(Geometry input, int level) {
}
return S2Utils.roundCellsToSameLevel(new ArrayList<>(cellIds), level).stream().map(S2CellId::id).collect(Collectors.toList()).toArray(new Long[cellIds.size()]);
}


// create static function named simplifyPreserveTopology
public static Geometry simplifyPreserveTopology(Geometry geometry, double distanceTolerance) {
return TopologyPreservingSimplifier.simplify(geometry, distanceTolerance);
}

public static String geometryType(Geometry geometry) {
return "ST_" + geometry.getGeometryType();
}

public static Geometry startPoint(Geometry geometry) {
if (geometry instanceof LineString) {
LineString line = (LineString) geometry;
return line.getStartPoint();
}
return null;
}

public static Geometry endPoint(Geometry geometry) {
if (geometry instanceof LineString) {
LineString line = (LineString) geometry;
return line.getEndPoint();
}
return null;
}

public static Geometry[] dump(Geometry geometry) {
int numGeom = geometry.getNumGeometries();
if (geometry instanceof GeometryCollection) {
Geometry[] geoms = new Geometry[geometry.getNumGeometries()];
for (int i = 0; i < numGeom; i++) {
geoms[i] = geometry.getGeometryN(i);
}
return geoms;
} else {
return new Geometry[] {geometry};
}
}

public static Geometry[] dumpPoints(Geometry geometry) {
return Arrays.stream(geometry.getCoordinates()).map(GEOMETRY_FACTORY::createPoint).toArray(Point[]::new);
}

public static Geometry symDifference(Geometry leftGeom, Geometry rightGeom) {
return leftGeom.symDifference(rightGeom);
}

public static Geometry union(Geometry leftGeom, Geometry rightGeom) {
return leftGeom.union(rightGeom);
}

public static Geometry createMultiGeometryFromOneElement(Geometry geometry) {
if (geometry instanceof Circle) {
return GEOMETRY_FACTORY.createGeometryCollection(new Circle[] {(Circle) geometry});
} else if (geometry instanceof GeometryCollection) {
return geometry;
} else if (geometry instanceof LineString) {
return GEOMETRY_FACTORY.createMultiLineString(new LineString[]{(LineString) geometry});
} else if (geometry instanceof Point) {
return GEOMETRY_FACTORY.createMultiPoint(new Point[] {(Point) geometry});
} else if (geometry instanceof Polygon) {
return GEOMETRY_FACTORY.createMultiPolygon(new Polygon[] {(Polygon) geometry});
} else {
return GEOMETRY_FACTORY.createGeometryCollection();
}
}

public static Geometry[] subDivide(Geometry geometry, int maxVertices) {
return GeometrySubDivider.subDivide(geometry, maxVertices);
}

public static Geometry makePolygon(Geometry shell, Geometry[] holes) {
try {
if (holes != null) {
LinearRing[] interiorRings = Arrays.stream(holes).filter(
h -> h != null && !h.isEmpty() && h instanceof LineString && ((LineString) h).isClosed()
).map(
h -> GEOMETRY_FACTORY.createLinearRing(h.getCoordinates())
).toArray(LinearRing[]::new);
if (interiorRings.length != 0) {
return GEOMETRY_FACTORY.createPolygon(
GEOMETRY_FACTORY.createLinearRing(shell.getCoordinates()),
Arrays.stream(holes).filter(
h -> h != null && !h.isEmpty() && h instanceof LineString && ((LineString) h).isClosed()
).map(
h -> GEOMETRY_FACTORY.createLinearRing(h.getCoordinates())
).toArray(LinearRing[]::new)
);
}
}
return GEOMETRY_FACTORY.createPolygon(
GEOMETRY_FACTORY.createLinearRing(shell.getCoordinates())
);
} catch (IllegalArgumentException e) {
return null;
}
}
}
52 changes: 52 additions & 0 deletions common/src/main/java/org/apache/sedona/common/Predicates.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Licensed 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.sedona.common;

import org.locationtech.jts.geom.Geometry;

public class Predicates {
public static boolean contains(Geometry leftGeometry, Geometry rightGeometry) {
return leftGeometry.contains(rightGeometry);
}
public static boolean intersects(Geometry leftGeometry, Geometry rightGeometry) {
return leftGeometry.intersects(rightGeometry);
}
public static boolean within(Geometry leftGeometry, Geometry rightGeometry) {
return leftGeometry.within(rightGeometry);
}
public static boolean covers(Geometry leftGeometry, Geometry rightGeometry) {
return leftGeometry.covers(rightGeometry);
}
public static boolean coveredBy(Geometry leftGeometry, Geometry rightGeometry) {
return leftGeometry.coveredBy(rightGeometry);
}
public static boolean crosses(Geometry leftGeometry, Geometry rightGeometry) {
return leftGeometry.crosses(rightGeometry);
}
public static boolean overlaps(Geometry leftGeometry, Geometry rightGeometry) {
return leftGeometry.overlaps(rightGeometry);
}
public static boolean touches(Geometry leftGeometry, Geometry rightGeometry) {
return leftGeometry.touches(rightGeometry);
}
public static boolean equals(Geometry leftGeometry, Geometry rightGeometry) {
return leftGeometry.symDifference(rightGeometry).isEmpty();
}
public static boolean disjoint(Geometry leftGeometry, Geometry rightGeometry) {
return leftGeometry.disjoint(rightGeometry);
}
public static boolean orderingEquals(Geometry leftGeometry, Geometry rightGeometry) {
return leftGeometry.equalsExact(rightGeometry);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* Licensed 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.sedona.common.utils;

import org.locationtech.jts.geom.Geometry;

public class GeoHashDecoder {
private static final int[] bits = new int[] {16, 8, 4, 2, 1};
private static final String base32 = "0123456789bcdefghjkmnpqrstuvwxyz";

public static class InvalidGeoHashException extends Exception {
public InvalidGeoHashException(String message) {
super(message);
}
}

public static Geometry decode(String geohash, Integer precision) throws InvalidGeoHashException {
return decodeGeoHashBBox(geohash, precision).getBbox().toPolygon();
}

private static class LatLon {
public Double[] lons;

public Double[] lats;

public LatLon(Double[] lons, Double[] lats) {
this.lons = lons;
this.lats = lats;
}

BBox getBbox() {
return new BBox(
lons[0],
lons[1],
lats[0],
lats[1]
);
}
}

private static LatLon decodeGeoHashBBox(String geohash, Integer precision) throws InvalidGeoHashException {
LatLon latLon = new LatLon(new Double[] {-180.0, 180.0}, new Double[] {-90.0, 90.0});
String geoHashLowered = geohash.toLowerCase();
int geoHashLength = geohash.length();
int targetPrecision = geoHashLength;
if (precision != null) {
if (precision < 0) throw new InvalidGeoHashException("Precision can not be negative");
else targetPrecision = Math.min(geoHashLength, precision);
}
boolean isEven = true;

for (int i = 0; i < targetPrecision ; i++){
char c = geoHashLowered.charAt(i);
byte cd = (byte) base32.indexOf(c);
if (cd == -1){
throw new InvalidGeoHashException(String.format("Invalid character '%s' found at index %d", c, i));
}
for (int j = 0;j < 5; j++){
byte mask = (byte) bits[j];
int index = (mask & cd) == 0 ? 1 : 0;
if (isEven){
latLon.lons[index] = (latLon.lons[0] + latLon.lons[1]) / 2;
}
else {
latLon.lats[index] = (latLon.lats[0] + latLon.lats[1]) / 2;
}
isEven = !isEven;
}
}
return latLon;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ object Catalog {
function[ST_LineInterpolatePoint](),
function[ST_SubDivideExplode](),
function[ST_SubDivide](),
function[ST_MakePolygon](),
function[ST_MakePolygon](null),
function[ST_GeoHash](),
function[ST_GeomFromGeoHash](null),
function[ST_Collect](),
Expand Down
Loading