All Downloads are FREE. Search and download functionalities are using the official Maven repository.

geotrellis.vector.Polygon.scala Maven / Gradle / Ivy

Go to download

GeoTrellis is an open source geographic data processing engine for high performance applications.

There is a newer version: 0.10.3
Show newest version
/*
 * Copyright (c) 2014 Azavea.
 *
 * 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
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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 geotrellis.vector

import com.vividsolutions.jts.geom.TopologyException
import com.vividsolutions.jts.{geom => jts}
import com.vividsolutions.jts.operation.union._
import GeomFactory._
import geotrellis.vector._

import spire.syntax.cfor._
import scala.collection.JavaConversions._

object Polygon {
  implicit def jtsToPolygon(jtsGeom: jts.Polygon): Polygon =
    Polygon(jtsGeom)

  def apply(exterior: Point*)(implicit d: DummyImplicit): Polygon =
    apply(Line(exterior), Set())

  def apply(exterior: Seq[Point]): Polygon =
    apply(Line(exterior), Set())

  def apply(exterior: Line): Polygon =
    apply(exterior, Set())

  def apply(exterior: Line, holes:Line*): Polygon =
    apply(exterior, holes)

  def apply(exterior: Line, holes:Traversable[Line]): Polygon = {
    if(!exterior.isClosed) {
      sys.error(s"Cannot create a polygon with unclosed exterior: $exterior")
    }

    if(exterior.vertices.length < 4) {
      sys.error(s"Cannot create a polygon with exterior with less that 4 points: $exterior")
    }

    val extGeom = factory.createLinearRing(exterior.jtsGeom.getCoordinates)

    val holeGeoms = (
      for (hole <- holes) yield {
        if (!hole.isClosed) {
          sys.error(s"Cannot create a polygon with an unclosed hole: $hole")
        } else {
          if (hole.vertices.length < 4)
            sys.error(s"Cannot create a polygon with a hole with less that 4 points: $hole")
          else
            factory.createLinearRing(hole.jtsGeom.getCoordinates)
        }
      }).toArray

    Polygon(factory.createPolygon(extGeom, holeGeoms))
  }
}

/** Class representing a polygon */
case class Polygon(jtsGeom: jts.Polygon) extends Geometry
                                            with Relatable
                                            with TwoDimensions {

  assert(!jtsGeom.isEmpty, s"Polygon Empty: $jtsGeom")

  /** Returns a unique representation of the geometry based on standard coordinate ordering. */
  def normalized(): Polygon = {
    val geom = jtsGeom.clone.asInstanceOf[jts.Polygon]
    geom.normalize
    Polygon(geom)
  }

  /** Tests whether this Polygon is a rectangle. */
  lazy val isRectangle: Boolean =
    jtsGeom.isRectangle

  /** Returns the area of this Polygon. */
  lazy val area: Double =
    jtsGeom.getArea

  /** Returns the exterior ring of this Polygon. */
  lazy val exterior: Line =
    Line(jtsGeom.getExteriorRing.clone.asInstanceOf[jts.LineString])

  /** Returns the hole rings of this Polygon. */
  lazy val holes: Array[Line] = {
    for (i <- 0 until numberOfHoles) yield
      Line(jtsGeom.getInteriorRingN(i).clone.asInstanceOf[jts.LineString])
  }.toArray

  /** Returns true if this Polygon contains holes */
  lazy val hasHoles: Boolean =
    numberOfHoles > 0

  /** Returns the number of holes in this Polygon */
  lazy val numberOfHoles: Int =
    jtsGeom.getNumInteriorRing

  /**
   * Returns the boundary of this Polygon.
   * The boundary of a Polygon is the set of closed curves corresponding to its
   * exterior and interior boundaries.
   */
  lazy val boundary: PolygonBoundaryResult =
    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)
    }
    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
   * boundaries.
   */
  lazy val perimeter: Double =
    jtsGeom.getLength

  // -- Intersection

  /**
   * Computes a Result that represents a Geometry made up of the points shared
   * by this Polygon and p.
   */
  def &(p: Point): PointOrNoResult =
    intersection(p)

  /**
   * Computes a Result that represents a Geometry made up of the points shared
   * by this Polygon and p.
   */
  def intersection(p: Point): PointOrNoResult =
    jtsGeom.intersection(p.jtsGeom)

  /**
   * Computes a Result that represents a Geometry made up of the points shared
   * by this Polygon and g. If it fails, it reduces the precision to avoid [[TopologyException]].
   */
  def safeIntersection(p: Point): PointOrNoResult =
    try intersection(p)
    catch {
      case _: TopologyException => simplifier.reduce(jtsGeom).intersection(simplifier.reduce(p.jtsGeom))
    }

  /**
   * Computes a Result that represents a Geometry made up of the points shared
   * by this Polygon and mp.
   */
  def &(mp: MultiPoint): MultiPointAtLeastOneDimensionIntersectionResult =
    intersection(mp)

  /**
   * Computes a Result that represents a Geometry made up of the points shared
   * by this Polygon and mp.
   */
  def intersection(mp: MultiPoint): MultiPointAtLeastOneDimensionIntersectionResult =
    jtsGeom.intersection(mp.jtsGeom)

  /**
   * Computes a Result that represents a Geometry made up of the points shared
   * by this Polygon and g. If it fails, it reduces the precision to avoid [[TopologyException]].
   */
  def safeIntersection(mp: MultiPoint): MultiPointAtLeastOneDimensionIntersectionResult =
    try intersection(mp)
    catch {
      case _: TopologyException => simplifier.reduce(jtsGeom).intersection(simplifier.reduce(mp.jtsGeom))
    }

  /**
   * Computes a Result that represents a Geometry made up of the points shared
   * by this Polygon and g.
   */
  def &(g: OneDimension): OneDimensionAtLeastOneDimensionIntersectionResult =
    intersection(g)

  /**
   * Computes a Result that represents a Geometry made up of the points shared
   * by this Polygon and g.
   */
  def intersection(g: OneDimension): OneDimensionAtLeastOneDimensionIntersectionResult =
    jtsGeom.intersection(g.jtsGeom)

  /**
   * Computes a Result that represents a Geometry made up of the points shared
   * by this Polygon and g. If it fails, it reduces the precision to avoid [[TopologyException]].
   */
  def safeIntersection(g: OneDimension): OneDimensionAtLeastOneDimensionIntersectionResult =
    try intersection(g)
    catch {
      case _: TopologyException => simplifier.reduce(jtsGeom).intersection(simplifier.reduce(g.jtsGeom))
    }

  /**
   * Computes a Result that represents a Geometry made up of the points shared
   * by this Polygon and g.
   */
  def &(g: TwoDimensions): TwoDimensionsTwoDimensionsIntersectionResult =
    intersection(g)

  /**
   * Computes a Result that represents a Geometry made up of the points shared
   * by this Polygon and g.
   */
  def intersection(g: TwoDimensions): TwoDimensionsTwoDimensionsIntersectionResult =
    jtsGeom.intersection(g.jtsGeom)

  /**
   * Computes a Result that represents a Geometry made up of the points shared
   * by this Polygon and g. If it fails, it reduces the precision to avoid [[TopologyException]].
   */
  def safeIntersection(g: TwoDimensions): TwoDimensionsTwoDimensionsIntersectionResult =
    try intersection(g)
    catch {
      case _: TopologyException => simplifier.reduce(jtsGeom).intersection(simplifier.reduce(g.jtsGeom))
    }

  // -- Union

  /**
   * Computes a Result that represents a Geometry made up of all the points in
   * this Polygon and g.
   */
  def |(g: AtMostOneDimension): AtMostOneDimensionPolygonUnionResult =
    union(g)

  /**
   * Computes a Result that represents a Geometry made up of all the points in
   * this Polygon and g.
   */
  def union(g: AtMostOneDimension): AtMostOneDimensionPolygonUnionResult =
    jtsGeom.union(g.jtsGeom)

  /**
   * Computes a Result that represents a Geometry made up of all the points in
   * this Polygon and g.
   */
  def |(g: TwoDimensions): TwoDimensionsTwoDimensionsUnionResult =
    union(g)

  /**
   * Computes a Result that represents a Geometry made up of all the points in
   * this Polygon and g. Uses cascaded polygon union if g is a (multi)polygon
   * else falls back to default jts union method.
   */
  def union(g: TwoDimensions): TwoDimensionsTwoDimensionsUnionResult = g match {
    case p:Polygon =>
      new CascadedPolygonUnion(Seq(this, p).map(_.jtsGeom)).union
    case mp:MultiPolygon =>
      new CascadedPolygonUnion((this +: mp.polygons).map(_.jtsGeom).toSeq).union
    case _ =>
      jtsGeom.union(g.jtsGeom)
  }



  // -- Difference

  /**
   * Computes a Result that represents a Geometry made up of all the points in
   * this Polygon that are not in g.
   */
  def -(g: AtMostOneDimension): PolygonAtMostOneDimensionDifferenceResult =
    difference(g)

  /**
   * Computes a Result that represents a Geometry made up of all the points in
   * this Polygon that are not in g.
   */
  def difference(g: AtMostOneDimension): PolygonAtMostOneDimensionDifferenceResult =
    jtsGeom.difference(g.jtsGeom)

  /**
   * Computes a Result that represents a Geometry made up of all the points in
   * this Polygon that are not in g.
   */
  def -(g: TwoDimensions): TwoDimensionsTwoDimensionsDifferenceResult =
    difference(g)

  /**
   * Computes a Result that represents a Geometry made up of all the points in
   * this Polygon that are not in g.
   */
  def difference(g: TwoDimensions): TwoDimensionsTwoDimensionsDifferenceResult =
    jtsGeom.difference(g.jtsGeom)


  // -- SymDifference

  /**
   * Computes a Result that represents a Geometry made up of all the points in
   * this Polygon that are not in g and all the points in g that are not in
   * this Polygon.
   */
  def symDifference(g: AtMostOneDimension): AtMostOneDimensionPolygonSymDifferenceResult =
    jtsGeom.symDifference(g.jtsGeom)

  /**
   * Computes a Result that represents a Geometry made up of all the points in
   * this Polygon that are not in g and all the points in g that are not in
   * this Polygon.
   */
  def symDifference(g: TwoDimensions): TwoDimensionsTwoDimensionsSymDifferenceResult =
    jtsGeom.symDifference(g.jtsGeom)


  // -- Buffer


  /** Computes a buffer area around this Polygon having width d. */
  def buffer(d: Double): Polygon =
    jtsGeom.buffer(d) match {
      case p: jts.Polygon => Polygon(p)
      case x =>
        sys.error(s"Unexpected result for Polygon buffer: ${x.getGeometryType}")
    }

  // -- Predicates


  /**
   * Tests whether this Polygon contains the specified Geometry g.
   * Returns true if the DE-9IM Intersection Matrix for the two geometries is
   * T*****FF*.
   */
  def contains(g: Geometry): Boolean =
    jtsGeom.contains(g.jtsGeom)

  /**
   * Tests whether this Polygon is covered by the specified TwoDimensions g.
   * Returns true if the DE-9IM Intersection Matrix for the two geometries is T*F**F*** or
   * *TF**F*** or **FT*F*** or **F*TF***.
   */
  def coveredBy(g: TwoDimensions): Boolean =
    jtsGeom.coveredBy(g.jtsGeom)

  /**
   * Tests whether this Polygon covers the specified Geometry g.
   * Returns true if the DE-9IM Intersection Matrix for the two geometries is
   * T*****FF* or *T****FF* or ***T**FF* or ****T*FF*.
   */
  def covers(g: Geometry): Boolean =
    jtsGeom.covers(g.jtsGeom)

  /**
   * Tests whether this Polygon crosses the specified MultiPoint mp.
   * Returns true if the DE-9IM Intersection Matrix for the two geometries is
   * T*****T** (A/P).
   */
  def crosses(mp: MultiPoint): Boolean =
    jtsGeom.crosses(mp.jtsGeom)

  /**
   * Tests whether this Polygon crosses the specified OneDimension g.
   * Returns true if the DE-9IM Intersection Matrix for the two geometries is
   * T*****T** (A/L).
   */
  def crosses(g: OneDimension): Boolean =
    jtsGeom.crosses(g.jtsGeom)

  /**
   * Tests whether this Polygon overlaps the specified TwoDimensions g.
   * Returns true if The DE-9IM Intersection Matrix for the two geometries is
   * T*T***T**.
   */
  def overlaps(g: TwoDimensions): Boolean =
    jtsGeom.overlaps(g.jtsGeom)

  /**
   * Tests whether this Polygon touches the specified Geometry g.
   * Returns true if the DE-9IM Intersection Matrix for the two geometries is
   * FT*******, F**T***** or F***T****.
   */
  def touches(g: Geometry): Boolean =
    jtsGeom.touches(g.jtsGeom)

  /**
   * Tests whether this Polygon is within the specified TwoDimensions g.
   * Returns true if the DE-9IM Intersection Matrix for the two geometries is
   * T*F**F***.
   */
  def within(g: TwoDimensions): Boolean =
    jtsGeom.within(g.jtsGeom)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy