<?php
namespace League\Geotools\Polygon;
use League\Geotools\BoundingBox\BoundingBox;
use League\Geotools\BoundingBox\BoundingBoxInterface;
use League\Geotools\Coordinate\Coordinate;
use League\Geotools\Coordinate\CoordinateCollection;
use League\Geotools\Coordinate\CoordinateInterface;
use League\Geotools\Coordinate\Ellipsoid;
class Polygon implements PolygonInterface, \Countable, \IteratorAggregate, \ArrayAccess, \JsonSerializable
{
const TYPE = 'POLYGON';
private $coordinates;
private $boundingBox;
private $hasCoordinate = false;
private $precision = 8;
public function __construct($coordinates = null)
{
if (is_array($coordinates) || null === $coordinates) {
$this->coordinates = new CoordinateCollection;
} elseif ($coordinates instanceof CoordinateCollection) {
$this->coordinates = $coordinates;
$this->hasCoordinate = $coordinates->count() > 0;
} else {
throw new \InvalidArgumentException;
}
$this->boundingBox = new BoundingBox($this);
if (is_array($coordinates)) {
$this->set($coordinates);
}
}
public function getGeometryType()
{
return self::TYPE;
}
public function getEllipsoid()
{
return $this->coordinates->getEllipsoid();
}
public function getCoordinate()
{
return $this->coordinates->offsetGet(0);
}
public function isEmpty()
{
return !$this->hasCoordinate;
}
public function pointInPolygon(CoordinateInterface $coordinate)
{
if (!$this->hasCoordinate) {
return false;
}
if (!$this->boundingBox->pointInBoundingBox($coordinate)) {
return false;
}
if ($this->pointOnVertex($coordinate)) {
return true;
}
if ($this->pointOnBoundary($coordinate)) {
return true;
}
$total = $this->count();
$intersections = 0;
for ($i = 1; $i < $total; $i++) {
$currentVertex = $this->get($i - 1);
$nextVertex = $this->get($i);
if (bccomp(
$coordinate->getLatitude(),
min($currentVertex->getLatitude(), $nextVertex->getLatitude()),
$this->getPrecision()
) === 1 &&
bccomp(
$coordinate->getLatitude(),
max($currentVertex->getLatitude(), $nextVertex->getLatitude()),
$this->getPrecision()
) <= 0 &&
bccomp(
$coordinate->getLongitude(),
max($currentVertex->getLongitude(), $nextVertex->getLongitude()),
$this->getPrecision()
) <= 0 &&
bccomp(
$currentVertex->getLatitude(),
$nextVertex->getLatitude(),
$this->getPrecision()
) !== 0
) {
$xinters =
($coordinate->getLatitude() - $currentVertex->getLatitude()) *
($nextVertex->getLongitude() - $currentVertex->getLongitude()) /
($nextVertex->getLatitude() - $currentVertex->getLatitude()) +
$currentVertex->getLongitude();
if (bccomp(
$currentVertex->getLongitude(),
$nextVertex->getLongitude(),
$this->getPrecision()
) === 0 ||
bccomp(
$coordinate->getLongitude(),
$xinters,
$this->getPrecision()
) <= 0
) {
$intersections++;
}
}
}
if ($intersections % 2 != 0) {
return true;
}
return false;
}
public function pointOnBoundary(CoordinateInterface $coordinate)
{
$total = $this->count();
for ($i = 1; $i <= $total; $i++) {
$currentVertex = $this->get($i - 1);
$nextVertex = $this->get($i);
if (null === $nextVertex) {
$nextVertex = $this->get(0);
}
if (bccomp(
$currentVertex->getLatitude(),
$nextVertex->getLatitude(),
$this->getPrecision()
) === 0 &&
bccomp(
$currentVertex->getLatitude(),
$coordinate->getLatitude(),
$this->getPrecision()
) === 0 &&
bccomp(
$coordinate->getLongitude(),
min($currentVertex->getLongitude(), $nextVertex->getLongitude()),
$this->getPrecision()
) === 1 &&
bccomp(
$coordinate->getLongitude(),
max($currentVertex->getLongitude(), $nextVertex->getLongitude()),
$this->getPrecision()
) === -1
) {
return true;
}
if (bccomp(
$coordinate->getLatitude(),
min($currentVertex->getLatitude(), $nextVertex->getLatitude()),
$this->getPrecision()
) === 1 &&
bccomp(
$coordinate->getLatitude(),
max($currentVertex->getLatitude(), $nextVertex->getLatitude()),
$this->getPrecision()
) <= 0 &&
bccomp(
$coordinate->getLongitude(),
max($currentVertex->getLongitude(), $nextVertex->getLongitude()),
$this->getPrecision()
) <= 0 &&
bccomp(
$currentVertex->getLatitude(),
$nextVertex->getLatitude(),
$this->getPrecision()
) !== 0
) {
$xinters =
($coordinate->getLatitude() - $currentVertex->getLatitude()) *
($nextVertex->getLongitude() - $currentVertex->getLongitude()) /
($nextVertex->getLatitude() - $currentVertex->getLatitude()) +
$currentVertex->getLongitude();
if (bccomp($xinters, $coordinate->getLongitude(), $this->getPrecision()) === 0) {
return true;
}
}
}
return false;
}
public function pointOnVertex(CoordinateInterface $coordinate)
{
foreach ($this->coordinates as $vertexCoordinate) {
if (bccomp(
$vertexCoordinate->getLatitude(),
$coordinate->getLatitude(),
$this->getPrecision()
) === 0 &&
bccomp(
$vertexCoordinate->getLongitude(),
$coordinate->getLongitude(),
$this->getPrecision()
) === 0
) {
return true;
}
}
return false;
}
public function getCoordinates()
{
return $this->coordinates;
}
public function setCoordinates(CoordinateCollection $coordinates)
{
$this->coordinates = $coordinates;
$this->boundingBox->setPolygon($this);
return $this;
}
public function toArray()
{
return $this->coordinates->toArray();
}
public function jsonSerialize()
{
return $this->coordinates->jsonSerialize();
}
public function offsetExists($offset)
{
return $this->coordinates->offsetExists($offset);
}
public function offsetGet($offset)
{
return $this->coordinates->offsetGet($offset);
}
public function offsetSet($offset, $value)
{
$this->coordinates->offsetSet($offset, $value);
$this->boundingBox->setPolygon($this);
}
public function offsetUnset($offset)
{
$retval = $this->coordinates->offsetUnset($offset);
$this->boundingBox->setPolygon($this);
return $retval;
}
public function count()
{
return $this->coordinates->count();
}
public function getIterator()
{
return $this->coordinates->getIterator();
}
public function get($key)
{
return $this->coordinates->get($key);
}
public function set($key, ?CoordinateInterface $coordinate = null)
{
if (is_array($key)) {
$values = $key;
} elseif (null !== $coordinate) {
$values = array($key => $coordinate);
} else {
throw new \InvalidArgumentException;
}
foreach ($values as $key => $value) {
if (!$value instanceof CoordinateInterface) {
$value = new Coordinate($value);
}
$this->coordinates->set($key, $value);
}
$this->hasCoordinate = true;
$this->boundingBox->setPolygon($this);
}
public function add(CoordinateInterface $coordinate)
{
$retval = $this->coordinates->add($coordinate);
$this->hasCoordinate = true;
$this->boundingBox->setPolygon($this);
return $retval;
}
public function remove($key)
{
$retval = $this->coordinates->remove($key);
if (!count($this->coordinates)) {
$this->hasCoordinate = false;
}
$this->boundingBox->setPolygon($this);
return $retval;
}
public function getPrecision()
{
return $this->precision;
}
public function setPrecision($precision)
{
$this->boundingBox->setPrecision($precision);
$this->precision = $precision;
return $this;
}
public function getBoundingBox()
{
return $this->boundingBox;
}
public function setBoundingBox(BoundingBoxInterface $boundingBox)
{
$this->boundingBox = $boundingBox;
return $this;
}
}