Skip to content

Commit

Permalink
Merge branch 'master' into better-plotting
Browse files Browse the repository at this point in the history
  • Loading branch information
djordon committed Jul 4, 2017
2 parents 7b7df7d + 0fddedf commit 2ad77e8
Show file tree
Hide file tree
Showing 23 changed files with 214 additions and 91 deletions.
112 changes: 73 additions & 39 deletions core/src/main/scala/org/partitioner/approximate.scala
Original file line number Diff line number Diff line change
@@ -1,67 +1,65 @@
package org.partitioner

import scala.collection.JavaConverters._

import com.vividsolutions.jts.densify.Densifier
import com.vividsolutions.jts.simplify.DouglasPeuckerSimplifier
import com.vividsolutions.jts.geom.{GeometryFactory, Geometry, Polygon, Coordinate, LineString, LinearRing}
import com.vividsolutions.jts.geom.{Polygon, Coordinate, LinearRing, Geometry}
import com.vividsolutions.jts.operation.union.CascadedPolygonUnion


object PolygonApproximator {
import GeometryUtils.IterablePolygon
val geometryFactory = new GeometryFactory()
import GeometryUtils.{IterablePolygon, geometryFactory}

private def polygon2vecs(pg: Polygon): List[Vec] = {
private def polygon2Vertices(pg: Polygon): List[Vertex] = {
val boundary: List[Coordinate] = pg.toList
Stream
.continually(boundary)
.flatten
.take(boundary.length * 2)
.toList
.sliding(2, 1)
.map(Vec.apply)
.map(Vertex.apply)
.toList
}

private def filterVecs(vecs: List[Vec]): List[Vec] = {
val reduced: List[Vec] = vecs
private def filterVertices(vertices: List[Vertex]): List[Vertex] = {
val reduced: List[Vertex] = vertices
.drop(2)
.foldLeft(vecs.take(2))(rectilinearFolder)
.foldLeft(vertices.take(2))(rectilinearFolder)

val boundary: List[Vec] = reduced.take(reduced.length / 2 + 1).tail
val last: Vec = boundary.last
val boundary: List[Vertex] = reduced.take(reduced.length / 2 + 1).tail
val last: Vertex = boundary.last

if (last == boundary.head) boundary else last :: boundary
}

private def isAxisAligned(a: List[Vec]): Boolean = {
private def isAxisAligned(a: List[Vertex]): Boolean = {
(a(0).coord.x == a(2).coord.x && a(1).coord.x == a(2).coord.x) ||
(a(0).coord.y == a(2).coord.y && a(1).coord.y == a(2).coord.y)
}

private def rectilinearFolder(a: List[Vec], b: Vec): List[Vec] = {
private def rectilinearFolder(a: List[Vertex], b: Vertex): List[Vertex] = {
if (isAxisAligned { b :: a.take(2) }) b :: a.tail else b :: a
}

private def vecs2polygon(vecs: List[Vec]): Polygon = {
geometryFactory.createPolygon(vecs.map(_.coord).toArray)
private def vertices2polygon(vertices: List[Vertex]): Polygon = {
geometryFactory.createPolygon(vertices.map(_.coord).toArray)
}

def removeAxisAlignedColinearity(pg: Polygon): Polygon = {
val shell: Polygon = removeAxisAlignedColinearitySimple(pg)
def removeAxisAlignedCollinearity(pg: Polygon): Polygon = {
val shell: Polygon = removeAxisAlignedCollinearitySimple(pg)
val holes: List[Polygon] = pg
.getHoles
.map(removeAxisAlignedColinearitySimple)
.map(removeAxisAlignedCollinearitySimple)

geometryFactory.createPolygon(
shell.getExteriorRing.asInstanceOf[LinearRing],
holes.map(_.getExteriorRing.asInstanceOf[LinearRing]).toArray
).norm.asInstanceOf[Polygon]
}

private def removeAxisAlignedColinearitySimple: Polygon => Polygon = {
polygon2vecs _ andThen filterVecs _ andThen vecs2polygon _
private def removeAxisAlignedCollinearitySimple: Polygon => Polygon = {
polygon2Vertices _ andThen filterVertices _ andThen vertices2polygon _
}

def simplify(polygon: Polygon, tolerance: Double): Polygon = {
Expand All @@ -84,20 +82,19 @@ object PolygonApproximator {


object OrthogonalPolygonBuilder {
import GeometryUtils.IterablePolygon
import PolygonApproximator.{densify, removeAxisAlignedColinearity, simplify}

val tol: Double = scala.math.pow(2, -12)
val geometryFactory = new GeometryFactory()
import GeometryUtils.{IterablePolygon, geometryFactory}
import PolygonApproximator.{densify, removeAxisAlignedCollinearity, simplify}

def coverCoordinates(points: Iterable[Coordinate]): Geometry = {
geometryFactory
.createLineString(points.toArray)
.getEnvelope
geometryFactory.createLineString(points.toArray).getEnvelope
}

def cover(polygon: Polygon, size: Int = 3, step: Int = 1): Polygon = {
val simpler: Polygon = removeAxisAlignedColinearity(polygon)
def createExteriorRingCover(
polygon: Polygon,
size: Int = 3,
step: Int = 1): Polygon = {

val simpler: Polygon = removeAxisAlignedCollinearity(polygon)
val length: Int = size.max(3)
val window: Int = step.min(length - 2).max(1)

Expand All @@ -106,30 +103,67 @@ object OrthogonalPolygonBuilder {
.map(coverCoordinates)
.toList

val newBoundary: LineString = CascadedPolygonUnion
.union(coveringRectangles.asJavaCollection)
.asInstanceOf[Polygon]
removeAxisAlignedCollinearity {
CascadedPolygonUnion
.union(coveringRectangles.asJavaCollection)
.asInstanceOf[Polygon]
}
}

def createExteriorCover(
polygon: Polygon,
size: Int = 3,
step: Int = 1): Polygon = {

val boundaryCover: Polygon = createExteriorRingCover(polygon, size, step)
geometryFactory.createPolygon(boundaryCover.getExteriorRing.getCoordinates)
}

private[partitioner] def createInteriorCover(
polygon: Polygon,
size: Int = 3,
step: Int = 1): List[Polygon] = {

val ext = geometryFactory.createPolygon(polygon.getExteriorRing.getCoordinates)
createExteriorRingCover(polygon, size, step).getHoles.filter(ext.covers)
}

def cover(polygon: Polygon, size: Int = 3, step: Int = 1): Polygon = {
val boundaryCover: Polygon = createExteriorRingCover(polygon, size, step)

val exterior: LinearRing = boundaryCover
.getExteriorRing

removeAxisAlignedColinearity(geometryFactory.createPolygon(newBoundary.getCoordinates))
.asInstanceOf[LinearRing]

val holes: Array[LinearRing] = polygon
.getHoles
.map(createExteriorRingCover(_: Polygon, size, step))
.flatMap(_.getHoles.filter(polygon.covers))
.map(_.getExteriorRing.asInstanceOf[LinearRing])
.toArray

geometryFactory
.createPolygon(exterior, holes)
.norm
.asInstanceOf[Polygon]
}

def approximate(
polygon: Polygon,
simplifyTolerance: Double = tol,
simplifyTolerance: Double = 0,
densifyTolerance: Double = Double.PositiveInfinity,
size: Int = 3,
step: Int = 1): Polygon = {

val approximator: Polygon => Polygon = Function.chain(Seq(
val transformer: Polygon => Polygon = Function.chain(Seq(
simplify(_: Polygon, simplifyTolerance),
densify(_: Polygon, densifyTolerance),
cover(_: Polygon, size, step)
createExteriorCover(_: Polygon, size, step)
))

val exterior: Polygon = geometryFactory.createPolygon(polygon.toArray)
val approximated: List[LinearRing] = (exterior :: polygon.getHoles)
.map { approximator(_).getExteriorRing.asInstanceOf[LinearRing] }
.map { transformer(_).getExteriorRing.asInstanceOf[LinearRing] }

geometryFactory
.createPolygon(approximated.head, approximated.tail.toArray)
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/scala/org/partitioner/geom.scala
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,10 @@ case class Chord(source: Corner, dest: Corner) extends CornerGeometry {
def yMin: Double = if (source.y < dest.y) source.y else dest.y
}

case class Vec(coord: Coordinate, angle: Double)
case class Vertex(coord: Coordinate, angle: Double)


object Vec {
object Vertex {
def apply(a: List[Coordinate]) =
new Vec(a(1), AngleJTS.toDegrees(AngleJTS.angle(a.head, a.tail.head)))
new Vertex(a(1), AngleJTS.toDegrees(AngleJTS.angle(a.head, a.tail.head)))
}
1 change: 1 addition & 0 deletions core/src/test/resources/non-rectilinear/polygon-00757

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions core/src/test/resources/non-rectilinear/polygon-00851

Large diffs are not rendered by default.

0 comments on commit 2ad77e8

Please sign in to comment.