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

Add ST_Simplify #36

Closed
wants to merge 14 commits into from
Closed
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
77 changes: 0 additions & 77 deletions .github/linters/ruff.toml

This file was deleted.

9 changes: 4 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ repos:
hooks:
- id: identity
- id: check-hooks-apply
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 24.8.0
hooks:
- id: black-jupyter
- repo: https://github.com/codespell-project/codespell
rev: v2.3.0
hooks:
Expand All @@ -18,11 +22,6 @@ repos:
description: Check spelling with codespell
args: [--ignore-words=.github/linters/codespell.txt]
exclude: ^docs/image|^spark/common/src/test/resources|^docs/usecases|^tools/maven/scalafmt
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.10
hooks:
- id: ruff
args: [--config=.github/linters/ruff.toml, --fix]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
Expand Down
54 changes: 54 additions & 0 deletions common/src/main/java/org/apache/sedona/common/Functions.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.apache.sedona.common.sphere.Spheroid;
import org.apache.sedona.common.subDivide.GeometrySubDivider;
import org.apache.sedona.common.utils.*;
import org.locationtech.jts.algorithm.Angle;
import org.locationtech.jts.algorithm.MinimumBoundingCircle;
import org.locationtech.jts.algorithm.Orientation;
import org.locationtech.jts.algorithm.construct.LargestEmptyCircle;
Expand Down Expand Up @@ -59,6 +60,7 @@
import org.locationtech.jts.operation.valid.TopologyValidationError;
import org.locationtech.jts.precision.GeometryPrecisionReducer;
import org.locationtech.jts.precision.MinimumClearance;
import org.locationtech.jts.simplify.DouglasPeuckerSimplifier;
import org.locationtech.jts.simplify.PolygonHullSimplifier;
import org.locationtech.jts.simplify.TopologyPreservingSimplifier;
import org.locationtech.jts.simplify.VWSimplifier;
Expand Down Expand Up @@ -1353,6 +1355,45 @@ public static Integer dimension(Geometry geometry) {
return dimension;
}

public static Geometry project(Geometry point, double distance, double azimuth, boolean lenient) {
if (!point.getClass().getSimpleName().equals("Point")) {
if (lenient) {
return point.getFactory().createPoint();
} else {
throw new IllegalArgumentException(
String.format(
"Input geometry is %s. It should be a Point type geometry",
point.getClass().getSimpleName()));
}
}

// Normalize azimuth if it is out of (-360, 360) range
// by calculating the number of orbits and subtracting it
int orbit = (int) Math.floor(azimuth / Angle.PI_TIMES_2);
azimuth -= Angle.PI_TIMES_2 * orbit;
// Convert azimuth to conventional slope
double slope = Angle.PI_TIMES_2 - azimuth + Angle.PI_OVER_2;
if (slope > Angle.PI_TIMES_2) slope -= Angle.PI_TIMES_2;
if (slope < -Angle.PI_TIMES_2) slope += Angle.PI_TIMES_2;

Coordinate projectedCoordinate = Angle.project(point.getCoordinate(), slope, distance);

if (Functions.hasZ(point)) {
projectedCoordinate.setZ(point.getCoordinate().getZ());
}

if (Functions.hasM(point)) {
CoordinateXYZM projectedCoordinateM = new CoordinateXYZM(projectedCoordinate);
projectedCoordinateM.setM(point.getCoordinate().getM());
return point.getFactory().createPoint(projectedCoordinateM);
}
return point.getFactory().createPoint(projectedCoordinate);
}

public static Geometry project(Geometry point, double distance, double azimuth) {
return project(point, distance, azimuth, false);
}

/**
* get the coordinates of a geometry and transform to Google s2 cell id
*
Expand Down Expand Up @@ -1503,6 +1544,10 @@ public static Geometry[] h3ToGeom(long[] cells) {
return polygons.toArray(new Polygon[0]);
}

public static Geometry simplify(Geometry geom, double distanceTolerance) {
return DouglasPeuckerSimplifier.simplify(geom, distanceTolerance);
}

// create static function named simplifyPreserveTopology
public static Geometry simplifyPreserveTopology(Geometry geometry, double distanceTolerance) {
return TopologyPreservingSimplifier.simplify(geometry, distanceTolerance);
Expand Down Expand Up @@ -2230,6 +2275,15 @@ public static Geometry rotateX(Geometry geometry, double angle) {
return affine(geometry, 1, 0, 0, 0, cosAngle, -sinAngle, 0, sinAngle, cosAngle, 0, 0, 0);
}

public static Geometry rotateY(Geometry geometry, double angle) {
if (GeomUtils.isAnyGeomEmpty(geometry)) {
return geometry;
}
double sinAngle = Math.sin(angle);
double cosAngle = Math.cos(angle);
return affine(geometry, cosAngle, 0, sinAngle, 0, 1, 0, -sinAngle, 0, cosAngle, 0, 0, 0);
}

/**
* Rotates a geometry by a given angle in radians.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,11 @@ public static boolean relateMatch(String matrix1, String matrix2) {
}

public static boolean knn(Geometry leftGeometry, Geometry rightGeometry, int k) {
return knn(leftGeometry, rightGeometry, k, false);
throw new UnsupportedOperationException("KNN predicate is not supported");
}

public static boolean knn(
Geometry leftGeometry, Geometry rightGeometry, int k, boolean useSpheroid) {
// This should only be used as a test predicate used with extra join condition
return true;
throw new UnsupportedOperationException("KNN predicate is not supported");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.apache.sedona.common.utils.RasterUtils;
import org.geotools.coverage.GridSampleDimension;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.processing.CannotCropException;
import org.geotools.coverage.processing.operation.Crop;
import org.locationtech.jts.geom.Geometry;
import org.opengis.parameter.ParameterValueGroup;
Expand Down Expand Up @@ -273,10 +274,17 @@ private static void ensureBandAppend(GridCoverage2D raster, int band) {
* @param geometry Specify ROI
* @param noDataValue no-Data value for empty cells
* @param crop Specifies to keep the original extent or not
* @param lenient Return null if the raster and geometry do not intersect when set to true,
* otherwise will throw an exception
* @return A clip Raster with defined ROI by the geometry
*/
public static GridCoverage2D clip(
GridCoverage2D raster, int band, Geometry geometry, double noDataValue, boolean crop)
GridCoverage2D raster,
int band,
Geometry geometry,
double noDataValue,
boolean crop,
boolean lenient)
throws FactoryException, TransformException {

// Selecting the band from original raster
Expand All @@ -296,7 +304,16 @@ public static GridCoverage2D clip(
parameters.parameter(Crop.PARAMNAME_DEST_NODATA).setValue(new double[] {noDataValue});
parameters.parameter(Crop.PARAMNAME_ROI).setValue(geometry);

GridCoverage2D newRaster = (GridCoverage2D) cropObject.doOperation(parameters, null);
GridCoverage2D newRaster;
try {
newRaster = (GridCoverage2D) cropObject.doOperation(parameters, null);
} catch (CannotCropException e) {
if (lenient) {
return null;
} else {
throw e;
}
}

if (!crop) {
double[] metadataOriginal = RasterAccessors.metadata(raster);
Expand Down Expand Up @@ -383,6 +400,22 @@ public static GridCoverage2D clip(
return newRaster;
}

/**
* Return a clipped raster with the specified ROI by the geometry
*
* @param raster Raster to clip
* @param band Band number to perform clipping
* @param geometry Specify ROI
* @param noDataValue no-Data value for empty cells
* @param crop Specifies to keep the original extent or not
* @return A clip Raster with defined ROI by the geometry
*/
public static GridCoverage2D clip(
GridCoverage2D raster, int band, Geometry geometry, double noDataValue, boolean crop)
throws FactoryException, TransformException {
return clip(raster, band, geometry, noDataValue, crop, true);
}

/**
* Return a clipped raster with the specified ROI by the geometry.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,41 @@ public void dimensionGeometryEmpty() {
assertEquals(actualResult, expectedResult);
}

@Test
public void project() throws ParseException {
Geometry point = GEOMETRY_FACTORY.createPoint(new Coordinate(0, 0));
String actual = Functions.asWKT(Functions.project(point, 100000, Math.toRadians(45.0)));
String expected = "POINT (70710.67811865476 70710.67811865475)";
assertEquals(expected, actual);

actual =
Functions.asWKT(Functions.project(Constructors.makeEnvelope(0, 1, 0, 1), 10, 10, true));
expected = "POINT EMPTY";
assertEquals(expected, actual);

point = Constructors.geomFromWKT("POINT Z(10 15 12)", 1111);
Geometry actualPoint = Functions.project(point, 1000, Math.toRadians(300.0));
actual = Functions.asWKT(actualPoint);
expected = "POINT Z(-856.0254037844385 515.0000000000003 12)";
assertEquals(expected, actual);
assertEquals(1111, actualPoint.getSRID());

point = Constructors.geomFromWKT("POINT M(10 15 12)", 1111);
actual = Functions.asWKT(Functions.project(point, 1000, Math.toRadians(300.0)));
expected = "POINT M(-856.0254037844385 515.0000000000003 12)";
assertEquals(expected, actual);

point = Constructors.geomFromWKT("POINT ZM(10 15 12 2)", 1111);
actual = Functions.asWKT(Functions.project(point, 1000, Math.toRadians(300.0)));
expected = "POINT ZM(-856.0254037844385 515.0000000000003 12 2)";
assertEquals(expected, actual);

point = Constructors.geomFromWKT("POINT(2 -1)", 0);
actual = Functions.asWKT(Functions.project(point, 100, Math.toRadians(470)));
expected = Functions.asWKT(Functions.project(point, 100, Math.toRadians(110)));
assertEquals(expected, actual);
}

private static boolean intersects(Set<?> s1, Set<?> s2) {
Set<?> copy = new HashSet<>(s1);
copy.retainAll(s2);
Expand Down Expand Up @@ -1755,6 +1790,29 @@ public void removeRepeatedPointsGeometryCollection() throws ParseException {
assertEquals(6000, actualSRID);
}

@Test
public void simplify() throws ParseException {
Geometry geom = Constructors.geomFromWKT("POINT (1 2)", 1111);
geom = Functions.buffer(geom, 10, false, "quad_segs=12");
int actualPoints = Functions.nPoints(Functions.simplify(geom, 0.1));
int expectedPoints = 33;
assertEquals(expectedPoints, actualPoints);

actualPoints = Functions.nPoints(Functions.simplify(geom, 0.5));
expectedPoints = 17;
assertEquals(expectedPoints, actualPoints);

actualPoints = Functions.nPoints(Functions.simplify(geom, 1));
expectedPoints = 9;
assertEquals(expectedPoints, actualPoints);

Geometry actual = Functions.simplify(geom, 10);
actualPoints = Functions.nPoints(actual);
expectedPoints = 4;
assertEquals(expectedPoints, actualPoints);
assertEquals(1111, actual.getSRID());
}

@Test
public void simplifyVW() throws ParseException {
Geometry geom = Constructors.geomFromEWKT("LINESTRING(5 2, 3 8, 6 20, 7 25, 10 10)");
Expand Down Expand Up @@ -3951,6 +4009,21 @@ public void rotateX() throws ParseException {
assertEquals(expected, actual);
}

@Test
public void rotateY() throws ParseException {
Geometry lineString = Constructors.geomFromEWKT("LINESTRING (50 160, 50 50, 100 50)");
String actual = Functions.asEWKT(Functions.rotateY(lineString, Math.PI));
String expected = "LINESTRING (-50 160, -50 50, -100 50)";
assertEquals(expected, actual);

lineString = Constructors.geomFromWKT("LINESTRING(1 2 3, 1 1 1)", 1234);
Geometry geomActual = Functions.rotateY(lineString, Math.PI / 2);
actual = Functions.asWKT(geomActual);
expected = "LINESTRING Z(3 2 -0.9999999999999998, 1 1 -0.9999999999999999)";
assertEquals(1234, geomActual.getSRID());
assertEquals(expected, actual);
}

@Test
public void rotate() throws ParseException {
Geometry lineString = Constructors.geomFromEWKT("LINESTRING (50 160, 50 50, 100 50)");
Expand Down
Loading
Loading