Skip to content

Commit

Permalink
Merge pull request #170 from wri/feature/geom_errs
Browse files Browse the repository at this point in the history
Fix geom errors
  • Loading branch information
jterry64 authored Jan 11, 2023
2 parents cc21f5b + aedd334 commit 2855dca
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 17 deletions.
47 changes: 32 additions & 15 deletions src/main/scala/org/globalforestwatch/util/GfwGeometryFixer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import org.locationtech.jts.geom.{Geometry, GeometryFactory, MultiPolygon, Polyg
import org.locationtech.jts.geom.util.GeometryFixer
import org.locationtech.jts.operation.overlay.snap.GeometrySnapper
import org.locationtech.jts.operation.overlayng.OverlayNGRobust
import scala.collection.JavaConverters._

import scala.collection.JavaConverters._
import scala.annotation.tailrec

case class GfwGeometryFixer(geom: Geometry, keepCollapsed: Boolean = false) {
Expand All @@ -30,27 +30,33 @@ case class GfwGeometryFixer(geom: Geometry, keepCollapsed: Boolean = false) {

private val maxSnapTolerance: Double = snapTolerance * 1000
def fix(): Geometry = {
if (geom.getNumGeometries == 0) {
geom.copy()
} else {

// doing a cheap trick here to eliminate sliver holes and other artifacts. However this might change geometry type.
// so we need to do this early on to avoid winding code. This block is not part of the original Java implementation.
val preFixedGeometry =
geom match {
case poly: Polygon => ironPolygons(poly)
case multi: MultiPolygon => ironPolygons(multi)
case _ => geom
}
try {
if (geom.getNumGeometries == 0) {
geom.copy()
} else {

// doing a cheap trick here to eliminate sliver holes and other artifacts. However this might change geometry type.
// so we need to do this early on to avoid winding code. This block is not part of the original Java implementation.
val preFixedGeometry =
geom match {
case poly: Polygon => ironPolygons(poly)
case multi: MultiPolygon => ironPolygons(multi)
case _ => geom
}

GeometryFixer.fix(preFixedGeometry)
GeometryFixer.fix(preFixedGeometry)
}
} catch {
case e: Throwable => // TODO: narrow down exception
println(s"Error fixing geometry: ${geom.toText}")
throw e
}
}

/** Ironing out potential sliver artifacts such as holes that resemble lines. Should only be used with Polygons or MultiPolygons.
*/
private def ironPolygons(geom: Geometry): Geometry = {
val bufferedGeom: Geometry = geom.buffer(0.0001).buffer(-0.0001)
val bufferedGeom: Geometry = geom.buffer(0.0001).buffer(-0.0001).buffer(0)
val polygons: Geometry = extractPolygons(bufferedGeom)
reduce(gpr)(polygons)
}
Expand All @@ -67,6 +73,17 @@ case class GfwGeometryFixer(geom: Geometry, keepCollapsed: Boolean = false) {
} yield {
multiGeometry.getGeometryN(i) match {
case geom: Polygon => List(Some(geom))
// if (!geom.isValid) {
// val fixedGeom = geom.buffer(0)
//
// if (!fixedGeom.isValid) {
// throw new Exception(f"Geometry could not be fixed: ${geom}")
// }
//
// loop(fixedGeom)
// } else {
// List(Some(geom))
// }
case multiGeom: MultiPolygon => loop(multiGeom)
case _ => List()
}
Expand Down
5 changes: 3 additions & 2 deletions src/main/scala/org/globalforestwatch/util/RDDAdapter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ object RDDAdapter {
def toSpatialRDDfromLocationRdd(rdd: RDD[Location[Geometry]], spark: SparkSession): SpatialRDD[Geometry] = {
val spatialRDD = new SpatialRDD[Geometry]()
spatialRDD.rawSpatialRDD = rdd.map { case Location(id, geom) =>
geom.setUserData(id)
geom
val fixedGeom = GfwGeometryFixer.fix(geom)
fixedGeom.setUserData(id)
fixedGeom
}.toJavaRDD()

spatialRDD.analyze()
Expand Down

0 comments on commit 2855dca

Please sign in to comment.