Skip to content

Commit

Permalink
[SEDONA-265] Migrate all ST functions to Sedona Inferred Expressions (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
zongsizhang authored Apr 19, 2023
1 parent 1b73ccf commit 7044c0c
Show file tree
Hide file tree
Showing 12 changed files with 390 additions and 397 deletions.
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;
}

}
2 changes: 1 addition & 1 deletion sql/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
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

0 comments on commit 7044c0c

Please sign in to comment.