Permalink
Browse files

Use JTS CoordinateSequences instead of Coordinates

This (potentially, depending on the type of CoordinateSequence in use)
can reduce the number of Coordinate allocations. See Polygon.vertices
for an example.

This also introduces some additional constructors for use with
CoordinateSequences and adds vertexCount and vertices to the Geometry
trait (to facilitate centralization of implementations).
  • Loading branch information...
mojodna committed Apr 8, 2018
1 parent fe8c0a6 commit 7162c55d1cbee46c6f115cd1a85defddd3fd6506
@@ -16,13 +16,12 @@
package geotrellis.vector
import geotrellis.proj4.CRS
import com.vividsolutions.jts.geom.{CoordinateSequence, TopologyException}
import com.vividsolutions.jts.{geom => jts}
import com.vividsolutions.jts.geom.TopologyException
import GeomFactory._
import geotrellis.vector.GeomFactory._
import spire.syntax.cfor._
import scala.reflect.{ ClassTag, classTag }
import scala.reflect.{ClassTag, classTag}
/** A trait inherited by classes which wrap a jts.Geometry */
trait Geometry extends Serializable {
@@ -69,6 +68,19 @@ trait Geometry extends Serializable {
if(jtsGeom.isEmpty) Extent(0.0, 0.0, 0.0, 0.0)
else jtsGeom.getEnvelopeInternal
/** Get the number of vertices in this geometry */
lazy val vertexCount: Int = jtsGeom.getNumPoints
/** Returns this Geometry's vertices. */
lazy val vertices: Array[Point] = {
val vertices = for (i <- 0 until jtsGeom.getNumGeometries) yield {
Geometry(jtsGeom.getGeometryN(i)).vertices
}
vertices.reduce(_ ++ _)
}
def &(g: Geometry): TwoDimensionsTwoDimensionsIntersectionResult =
intersection(g)
@@ -107,6 +119,14 @@ trait Geometry extends Serializable {
None
}
protected def populatePoints(sequence: CoordinateSequence, arr: Array[Point], offset: Int = 0): Array[Point] = {
cfor(0)(_ < sequence.size, _ + 1) { i =>
arr(i + offset) = Point(sequence.getX(i), sequence.getY(i))
}
arr
}
override
def equals(other: Any): Boolean =
other match {
@@ -18,7 +18,7 @@ package geotrellis.vector
import com.vividsolutions.jts.{geom => jts}
import GeomFactory._
import com.vividsolutions.jts.geom.CoordinateSequence
import spire.syntax.cfor._
object Line {
@@ -35,6 +35,9 @@ object Line {
def apply(points: Point*): Line =
apply(points.toList)
def apply(coords: CoordinateSequence): Line =
Line(factory.createLineString(coords))
def apply(points: Traversable[Point]): Line = {
if (points.size < 2) {
sys.error("Invalid line: Requires 2 or more points.")
@@ -92,18 +95,17 @@ case class Line(jtsGeom: jts.LineString) extends Geometry
def points: Array[Point] = vertices
/** Returns the points which determine this line (i.e. its vertices */
def vertices: Array[Point] = {
val size = jtsGeom.getNumPoints
val arr = Array.ofDim[Point](size)
cfor(0)(_ < arr.size, _ + 1) { i =>
arr(i) = Point(jtsGeom.getPointN(i))
override lazy val vertices: Array[Point] = {
val arr = Array.ofDim[Point](jtsGeom.getNumPoints)
val sequence = jtsGeom.getCoordinateSequence
cfor(0)(_ < arr.length, _ + 1) { i =>
arr(i) = Point(sequence.getX(i), sequence.getY(i))
}
arr
}
/** Returns the number of vertices in this geometry */
lazy val vertexCount: Int = jtsGeom.getNumPoints
/** Returns the length of this Line. */
lazy val length: Double =
jtsGeom.getLength
@@ -16,11 +16,8 @@
package geotrellis.vector
import com.vividsolutions.jts.{geom => jts}
trait MultiGeometry extends Geometry {
/** Computes a area containing these geometries and buffered by size d. */
def buffer(d: Double): TwoDimensionsTwoDimensionsUnionResult =
jtsGeom.buffer(d)
}
@@ -16,12 +16,8 @@
package geotrellis.vector
import GeomFactory._
import com.vividsolutions.jts.{geom=>jts}
import scala.collection.JavaConversions._
import com.vividsolutions.jts.{geom => jts}
import geotrellis.vector.GeomFactory._
import spire.syntax.cfor._
/** Companion object to [[MultiLine]] */
@@ -77,21 +73,6 @@ case class MultiLine(jtsGeom: jts.MultiLineString) extends MultiGeometry
lazy val boundary: OneDimensionBoundaryResult =
jtsGeom.getBoundary
/** Returns this MultiLine's vertices. */
lazy val vertices: Array[Point] = {
val coords = jtsGeom.getCoordinates
val arr = Array.ofDim[Point](coords.size)
cfor(0)(_ < arr.size, _ + 1) { i =>
val coord = coords(i)
arr(i) = Point(coord.x, coord.y)
}
arr
}
/** Get the number of vertices in this geometry */
lazy val vertexCount: Int = jtsGeom.getNumPoints
// -- Intersection
/** Computes a Result that represents a Geometry made up of the points shared
@@ -61,19 +61,6 @@ case class MultiPoint(jtsGeom: jts.MultiPoint) extends MultiGeometry
/** Returns the Points contained in MultiPoint. */
lazy val points: Array[Point] = vertices
lazy val vertices: Array[Point] = {
val coords = jtsGeom.getCoordinates
val arr = Array.ofDim[Point](coords.size)
cfor(0)(_ < arr.size, _ + 1) { i =>
val coord = coords(i)
arr(i) = Point(coord.x, coord.y)
}
arr
}
/** Get the number of vertices in this geometry */
lazy val vertexCount: Int = jtsGeom.getNumPoints
// -- Intersection
/** Computes a Result that represents a Geometry made up of the points shared
@@ -69,20 +69,6 @@ case class MultiPolygon(jtsGeom: jts.MultiPolygon) extends MultiGeometry
lazy val boundary: MultiLineResult =
jtsGeom.getBoundary
/** Returns this MultiPolygon's vertices. */
lazy val vertices: Array[Point] = {
val coords = jtsGeom.getCoordinates
val arr = Array.ofDim[Point](coords.size)
cfor(0)(_ < arr.size, _ + 1) { i =>
val coord = coords(i)
arr(i) = Point(coord.x, coord.y)
}
arr
}
/** Get the number of vertices in this geometry */
lazy val vertexCount: Int = jtsGeom.getNumPoints
// -- Intersection
def intersection(): MultiPolygonMultiPolygonIntersectionResult =
@@ -47,6 +47,8 @@ case class Point(jtsGeom: jts.Point) extends Geometry
val y: Double =
jtsGeom.getY
override lazy val vertices: Array[Point] = Array(this)
private[vector] def toCoordinate() =
new jts.Coordinate(x, y)
@@ -16,13 +16,12 @@
package geotrellis.vector
import com.vividsolutions.jts.geom.TopologyException
import com.vividsolutions.jts.{geom => jts}
import com.vividsolutions.jts.geom.{CoordinateSequence, LinearRing, TopologyException}
import com.vividsolutions.jts.operation.union._
import GeomFactory._
import geotrellis.vector._
import com.vividsolutions.jts.{geom => jts}
import geotrellis.vector.GeomFactory._
import spire.syntax.cfor._
import scala.collection.GenTraversable
import scala.collection.JavaConversions._
object Polygon {
@@ -38,10 +37,16 @@ object Polygon {
def apply(exterior: Line): Polygon =
apply(exterior, Set())
def apply(exterior: CoordinateSequence): Polygon =
Polygon(factory.createPolygon(exterior))
def apply(exterior: Line, holes:Line*): Polygon =
apply(exterior, holes)
def apply(exterior: Line, holes:Traversable[Line]): Polygon = {
def apply(exterior: CoordinateSequence, holes: GenTraversable[CoordinateSequence]): Polygon =
apply(factory.createLinearRing(exterior), holes.map(factory.createLinearRing))
def apply(exterior: Line, holes: GenTraversable[Line]): Polygon = {
if(!exterior.isClosed) {
sys.error(s"Cannot create a polygon with unclosed exterior: $exterior")
}
@@ -50,7 +55,7 @@ object Polygon {
sys.error(s"Cannot create a polygon with exterior with fewer than 4 points: $exterior")
}
val extGeom = factory.createLinearRing(exterior.jtsGeom.getCoordinates)
val extGeom = factory.createLinearRing(exterior.jtsGeom.getCoordinateSequence)
val holeGeoms = (
for (hole <- holes) yield {
@@ -60,13 +65,16 @@ object Polygon {
if (hole.vertices.length < 4) {
sys.error(s"Cannot create a polygon with a hole with fewer than 4 points: $hole")
} else {
factory.createLinearRing(hole.jtsGeom.getCoordinates)
factory.createLinearRing(hole.jtsGeom.getCoordinateSequence)
}
}
}).toArray
Polygon(factory.createPolygon(extGeom, holeGeoms))
apply(extGeom, holeGeoms)
}
def apply(exterior: LinearRing, holes: GenTraversable[LinearRing]): Polygon =
Polygon(factory.createPolygon(exterior, holes.toArray))
}
/** Class representing a polygon */
@@ -117,19 +125,18 @@ case class Polygon(jtsGeom: jts.Polygon) extends Geometry
jtsGeom.getBoundary
/** Returns this Polygon's vertices. */
lazy val vertices: Array[Point] = {
val coords = jtsGeom.getCoordinates
val arr = Array.ofDim[Point](coords.size)
cfor(0)(_ < arr.size, _ + 1) { i =>
val coord = coords(i)
arr(i) = Point(coord.x, coord.y)
}
override lazy val vertices: Array[Point] = {
val arr = Array.ofDim[Point](jtsGeom.getNumPoints)
val sequences = jtsGeom.getExteriorRing.getCoordinateSequence +: (0 until jtsGeom.getNumInteriorRing).map(jtsGeom
.getInteriorRingN(_).getCoordinateSequence)
val offsets = sequences.map(_.size).scanLeft(0)(_ + _).dropRight(1)
sequences.zip(offsets).foreach { case (seq, offset) => populatePoints(seq, arr, offset) }
arr
}
/** Get the number of vertices in this geometry */
lazy val vertexCount: Int = jtsGeom.getNumPoints
/**
* Returns this Polygon's perimeter.
* A Polygon's perimeter is the length of its exterior and interior
@@ -17,11 +17,8 @@
package geotrellis.vector.check.jts
import com.vividsolutions.jts.geom._
import org.scalacheck.Gen._
import org.scalacheck._
import Prop._
import Gen._
import Arbitrary._
object Generators {
val factory = new GeometryFactory()
@@ -61,7 +58,7 @@ object Generators {
lazy val genLinearRing:Gen[LinearRing] =
genPolygon.map { p =>
factory.createLinearRing(p.getExteriorRing.getCoordinates)//.asInstanceOf[LinearRing]
factory.createLinearRing(p.getExteriorRing.getCoordinateSequence)//.asInstanceOf[LinearRing]
}
lazy val genLineString:Gen[LineString] =

0 comments on commit 7162c55

Please sign in to comment.