Alter Version
This commit is contained in:
628
vendor/Shapefile/Geometry/Geometry.php
vendored
Normal file
628
vendor/Shapefile/Geometry/Geometry.php
vendored
Normal file
@@ -0,0 +1,628 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Shapefile - PHP library to read and write ESRI Shapefiles, compatible with WKT and GeoJSON
|
||||
*
|
||||
* @package Shapefile
|
||||
* @author Gaspare Sganga
|
||||
* @version 3.3.0
|
||||
* @license MIT
|
||||
* @link https://gasparesganga.com/labs/php-shapefile/
|
||||
*/
|
||||
|
||||
namespace Shapefile\Geometry;
|
||||
|
||||
use Shapefile\Shapefile;
|
||||
use Shapefile\ShapefileException;
|
||||
|
||||
/**
|
||||
* Abstract base class for all geometries.
|
||||
* It defines some common public methods and some helper protected functions.
|
||||
*/
|
||||
abstract class Geometry
|
||||
{
|
||||
/**
|
||||
* @var array|null Custom bounding box set with setCustomBoundingBox() method.
|
||||
*/
|
||||
private $custom_bounding_box = null;
|
||||
|
||||
/**
|
||||
* @var array Data of the Geometry.
|
||||
*/
|
||||
private $data = [];
|
||||
|
||||
/**
|
||||
* @var bool Flag representing whether the Geometry is empty.
|
||||
*/
|
||||
private $flag_empty = true;
|
||||
|
||||
/**
|
||||
* @var bool Flag representing whether the Geometry has Z dimension.
|
||||
*/
|
||||
private $flag_z = false;
|
||||
|
||||
/**
|
||||
* @var bool Flag representing whether the Geometry has M dimension.
|
||||
*/
|
||||
private $flag_m = false;
|
||||
|
||||
/**
|
||||
* @var bool Flag representing whether the DBF record is deleted.
|
||||
*/
|
||||
private $flag_deleted = false;
|
||||
|
||||
|
||||
|
||||
/////////////////////////////// ABSTRACT ///////////////////////////////
|
||||
/**
|
||||
* Initialize the Geometry with a structured array.
|
||||
*
|
||||
* @param array $array Array structured according to Geometry type.
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
abstract public function initFromArray($array);
|
||||
|
||||
/**
|
||||
* Initialize the Geometry with WKT.
|
||||
*
|
||||
* @param string $wkt WKT string.
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
abstract public function initFromWKT($wkt);
|
||||
|
||||
/**
|
||||
* Initialize the Geometry with GeoJSON.
|
||||
*
|
||||
* @param string $geojson GeoJSON string.
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
abstract public function initFromGeoJSON($geojson);
|
||||
|
||||
|
||||
/**
|
||||
* Gets the Geometry as a structured array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function getArray();
|
||||
|
||||
/**
|
||||
* Gets the Geometry as WKT.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getWKT();
|
||||
|
||||
/**
|
||||
* Gets the Geometry as GeoJSON.
|
||||
*
|
||||
* @param bool $flag_bbox If true include the bounding box in the GeoJSON output.
|
||||
* @param bool $flag_feature If true output a GeoJSON Feature with all the data.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getGeoJSON($flag_bbox = true, $flag_feature = false);
|
||||
|
||||
/**
|
||||
* Gets Geometry bounding box.
|
||||
* If a custom one is defined, it will be returned instead of a computed one.
|
||||
*
|
||||
* @return array Associative array with the xmin, xmax, ymin, ymax and optional zmin, zmax, mmin, mmax values.
|
||||
*/
|
||||
abstract public function getBoundingBox();
|
||||
|
||||
/**
|
||||
* Gets the Shape base type of the Geometry.
|
||||
* This is not intended for users, but Shapefile requires it for internal mechanisms.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
abstract public function getSHPBasetype();
|
||||
|
||||
|
||||
/**
|
||||
* Gets the WKT base type of the Geometry.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getWKTBasetype();
|
||||
|
||||
/**
|
||||
* Gets the GeoJSON base type of the Geometry.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getGeoJSONBasetype();
|
||||
|
||||
|
||||
|
||||
/////////////////////////////// PUBLIC ///////////////////////////////
|
||||
/**
|
||||
* Gets the state of the Empty flag.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return $this->flag_empty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state of the Z flag.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isZ()
|
||||
{
|
||||
return $this->flag_z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state of the M flag.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isM()
|
||||
{
|
||||
return $this->flag_m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state of the Deleted flag.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDeleted()
|
||||
{
|
||||
return $this->flag_deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state of the Deleted flag.
|
||||
*
|
||||
* @param bool $value
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
public function setFlagDeleted($value)
|
||||
{
|
||||
$this->flag_deleted = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets a custom bounding box for the Geometry.
|
||||
* No check is carried out except a formal compliance of dimensions.
|
||||
*
|
||||
* @param array $bounding_box Associative array with the xmin, xmax, ymin, ymax and optional zmin, zmax, mmin, mmax values.
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
public function setCustomBoundingBox($bounding_box)
|
||||
{
|
||||
$bounding_box = array_intersect_key($bounding_box, array_flip(['xmin', 'xmax', 'ymin', 'ymax', 'zmin', 'zmax', 'mmin', 'mmax']));
|
||||
if (
|
||||
$this->isEmpty()
|
||||
|| !isset($bounding_box['xmin'], $bounding_box['xmax'], $bounding_box['ymin'], $bounding_box['ymax'])
|
||||
|| (($this->isZ() && !isset($bounding_box['zmin'], $bounding_box['zmax'])) || (!$this->isZ() && (isset($bounding_box['zmin']) || isset($bounding_box['zmax']))))
|
||||
|| (($this->isM() && !isset($bounding_box['mmin'], $bounding_box['mmax'])) || (!$this->isM() && (isset($bounding_box['mmin']) || isset($bounding_box['mmax']))))
|
||||
) {
|
||||
throw new ShapefileException(Shapefile::ERR_GEOM_MISMATCHED_BBOX);
|
||||
}
|
||||
$this->custom_bounding_box = $bounding_box;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets custom bounding box for the Geometry.
|
||||
* It will cause getBoundingBox() method to return a normally computed bbox instead of a custom one.
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
public function resetCustomBoundingBox()
|
||||
{
|
||||
$this->custom_bounding_box = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets data value for speficied field name.
|
||||
*
|
||||
* @param string $fieldname Name of the field.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData($fieldname)
|
||||
{
|
||||
if (!isset($this->data[$fieldname])) {
|
||||
throw new ShapefileException(Shapefile::ERR_INPUT_FIELD_NOT_FOUND, $fieldname);
|
||||
}
|
||||
return $this->data[$fieldname];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets data value for speficied field name.
|
||||
*
|
||||
* @param string $fieldname Name of the field.
|
||||
* @param mixed $value Value to assign to the field.
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
public function setData($fieldname, $value)
|
||||
{
|
||||
$this->data[$fieldname] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of defined data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDataArray()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an array of data.
|
||||
*
|
||||
* @param array $data Associative array of values.
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
public function setDataArray($data)
|
||||
{
|
||||
foreach ($data as $fieldname => $value) {
|
||||
$this->data[$fieldname] = $value;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/////////////////////////////// PROTECTED ///////////////////////////////
|
||||
/**
|
||||
* Sets the state of the Empty flag.
|
||||
*
|
||||
* @param bool $value
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
protected function setFlagEmpty($value)
|
||||
{
|
||||
$this->flag_empty = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state of the Z flag.
|
||||
*
|
||||
* @param bool $value
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
protected function setFlagZ($value)
|
||||
{
|
||||
$this->flag_z = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state of the M flag.
|
||||
*
|
||||
* @param bool $value
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
protected function setFlagM($value)
|
||||
{
|
||||
$this->flag_m = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the Geometry has been initialized (it is not empty) and if YES throws an exception.
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
protected function checkInit()
|
||||
{
|
||||
if (!$this->isEmpty()) {
|
||||
throw new ShapefileException(Shapefile::ERR_GEOM_NOT_EMPTY);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the custom bounding box.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
protected function getCustomBoundingBox()
|
||||
{
|
||||
return $this->custom_bounding_box;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sanitize WKT.
|
||||
* It attempts to sanitize user-provided WKT and throws an exception if it appears to be invalid.
|
||||
*
|
||||
* @param string $wkt The WKT to sanitize.
|
||||
*
|
||||
* @return string Sanitized WKT.
|
||||
*/
|
||||
protected function wktSanitize($wkt)
|
||||
{
|
||||
// Normalize whitespaces
|
||||
$wkt = strtoupper(preg_replace('/\s+/', ' ', trim($wkt)));
|
||||
// Normalize commas
|
||||
$wkt = str_replace(array(', ', ' ,'), ',', $wkt);
|
||||
// Check basetype
|
||||
if (substr($wkt, 0, strlen($this->getWKTBasetype())) != strtoupper($this->getWKTBasetype())) {
|
||||
throw new ShapefileException(Shapefile::ERR_INPUT_WKT_NOT_VALID);
|
||||
}
|
||||
return $wkt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if WKT represents an empty Geometry.
|
||||
*
|
||||
* @param string $wkt
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function wktIsEmpty($wkt)
|
||||
{
|
||||
return substr($wkt, -5) == 'EMPTY';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if WKT represents a Geometry that has a Z dimension.
|
||||
*
|
||||
* @param string $wkt The whole sanitized WKT.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function wktIsZ($wkt)
|
||||
{
|
||||
return strpos(trim(substr($wkt, strlen($this->getWKTBasetype()), 3)), 'Z') !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if WKT represents a Geometry that has a M dimension.
|
||||
*
|
||||
* @param string $wkt The whole sanitized WKT.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function wktIsM($wkt)
|
||||
{
|
||||
return strpos(trim(substr($wkt, strlen($this->getWKTBasetype()), 3)), 'M') !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts data from WKT.
|
||||
*
|
||||
* @param string $wkt The whole sanitized WKT.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function wktExtractData($wkt)
|
||||
{
|
||||
if ($this->wktIsEmpty($wkt)) {
|
||||
return null;
|
||||
}
|
||||
$begin = strpos($wkt, '(');
|
||||
if ($begin === false) {
|
||||
throw new ShapefileException(ERR_INPUT_WKT_NOT_VALID);
|
||||
}
|
||||
$end = strrpos($wkt, ')');
|
||||
if ($end === false) {
|
||||
throw new ShapefileException(ERR_INPUT_WKT_NOT_VALID);
|
||||
}
|
||||
return trim(substr($wkt, $begin + 1, $end - $begin - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a group of WKT coordinates into an associative array.
|
||||
* Refer to parseCoordinatesArray() method for output details.
|
||||
*
|
||||
* @param string $coordinates_string The WKT coordinates group.
|
||||
* @param bool $force_z Flag to enforce the presence of Z dimension.
|
||||
* @param bool $force_m Flag to enforce the presence of M dimension.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function wktParseCoordinates($coordinates_string, $force_z, $force_m)
|
||||
{
|
||||
return $this->parseCoordinatesArray(explode(' ', trim($coordinates_string)), $force_z, $force_m, Shapefile::ERR_INPUT_WKT_NOT_VALID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an initialized WKT according to the Geometry properties.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function wktInitializeOutput()
|
||||
{
|
||||
$ret = $this->getWKTBasetype();
|
||||
if ($this->isEmpty()) {
|
||||
$ret .= ' EMPTY';
|
||||
} else {
|
||||
$ret .= ($this->isZ() ? 'Z' : '') . ($this->isM() ? 'M' : '');
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return sanitized GeoJSON, keeping just the geometry part.
|
||||
* It attempts to sanitize user-provided GeoJSON and throws an exception if it appears to be invalid.
|
||||
*
|
||||
* @param string $geojson The GeoJSON to sanitize.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function geojsonSanitize($geojson)
|
||||
{
|
||||
$geojson = json_decode($geojson, true);
|
||||
|
||||
// If it is null it means "an empty Geometry"
|
||||
if ($geojson === null) {
|
||||
return null;
|
||||
}
|
||||
// If it is a Feature just keep the Geometry part
|
||||
if (isset($geojson['geometry'])) {
|
||||
$geojson = $geojson['geometry'];
|
||||
}
|
||||
// Check if "type" and "coordinates" are defined
|
||||
if (!isset($geojson['type'], $geojson['coordinates'])) {
|
||||
throw new ShapefileException(Shapefile::ERR_INPUT_GEOJSON_NOT_VALID);
|
||||
}
|
||||
// Check if "type" is consistent with current Geometry
|
||||
if (substr(strtoupper(trim($geojson['type'])), 0, strlen($this->getGeoJSONBasetype())) != strtoupper($this->getGeoJSONBasetype())) {
|
||||
throw new ShapefileException(Shapefile::ERR_INPUT_GEOJSON_NOT_VALID);
|
||||
}
|
||||
|
||||
return $geojson;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if GeoJSON type represents a Geometry that has a M dimension.
|
||||
*
|
||||
* @param string $type The type speficied in the GeoJSON.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function geojsonIsM($type)
|
||||
{
|
||||
return substr($type, -1) == 'M';
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an array of GeoJSON coordinates into an associative array.
|
||||
* Refer to parseCoordinatesArray() method for output details.
|
||||
*
|
||||
* @param string $coordinates_array GeoJSON coordinates array.
|
||||
* @param bool $force_m Flag to enforce the presence of M dimension.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function geojsonParseCoordinates($coordinates_array, $force_m)
|
||||
{
|
||||
return $this->parseCoordinatesArray($coordinates_array, false, $force_m, Shapefile::ERR_INPUT_GEOJSON_NOT_VALID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds valid GeoJSON starting from raw coordinates.
|
||||
*
|
||||
* @param array $coordinates GeoJSON coordinates array.
|
||||
* @param bool $flag_bbox If true include the bounding box in the GeoJSON output.
|
||||
* @param bool $flag_feature If true output a GeoJSON Feature with all the data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function geojsonPackOutput($coordinates, $flag_bbox, $flag_feature)
|
||||
{
|
||||
$ret = [];
|
||||
// Type
|
||||
$ret['type'] = $this->getGeoJSONBasetype() . ($this->isM() ? 'M' : '');
|
||||
// Bounding box
|
||||
if ($flag_bbox) {
|
||||
$ret['bbox'] = [];
|
||||
$bbox = $this->getBoundingBox();
|
||||
$ret['bbox'][] = $bbox['xmin'];
|
||||
$ret['bbox'][] = $bbox['ymin'];
|
||||
if ($this->isZ()) {
|
||||
$ret['bbox'][] = $bbox['zmin'];
|
||||
}
|
||||
if ($this->isM()) {
|
||||
$ret['bbox'][] = $bbox['mmin'];
|
||||
}
|
||||
$ret['bbox'][] = $bbox['xmax'];
|
||||
$ret['bbox'][] = $bbox['ymax'];
|
||||
if ($this->isZ()) {
|
||||
$ret['bbox'][] = $bbox['zmax'];
|
||||
}
|
||||
if ($this->isM()) {
|
||||
$ret['bbox'][] = $bbox['mmax'];
|
||||
}
|
||||
}
|
||||
// Coordinates
|
||||
$ret['coordinates'] = $coordinates;
|
||||
// Feature
|
||||
if ($flag_feature) {
|
||||
$ret = [
|
||||
'type' => 'Feature',
|
||||
'geometry' => $ret,
|
||||
'properties' => $this->data,
|
||||
];
|
||||
}
|
||||
|
||||
return json_encode($ret);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////// PRIVATE ///////////////////////////////
|
||||
/**
|
||||
* Parses an indexed array of coordinates and returns an associative one in the form of:
|
||||
* [
|
||||
* "x" => float
|
||||
* "y" => float
|
||||
* "z" => float|null
|
||||
* "m" => float|null
|
||||
* ]
|
||||
*
|
||||
* @param float[] $coordinates The indexed array of coordinates to parse.
|
||||
* @param bool $force_z Flag to enforce the presence of Z dimension.
|
||||
* @param bool $force_m Flag to enforce the presence of M dimension.
|
||||
* @param int $err_code Error code to throw an exception in case of invalid input.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function parseCoordinatesArray($coordinates, $force_z, $force_m, $err_code)
|
||||
{
|
||||
$count = count($coordinates);
|
||||
if (
|
||||
$count < 2 ||
|
||||
(($force_z || $force_m) && $count < 3) ||
|
||||
($force_z && $force_m && $count < 4) ||
|
||||
$count > 4
|
||||
) {
|
||||
throw new ShapefileException($err_code);
|
||||
}
|
||||
|
||||
$ret = [
|
||||
'x' => $coordinates[0],
|
||||
'y' => $coordinates[1],
|
||||
'z' => null,
|
||||
'm' => null,
|
||||
];
|
||||
if ($count == 3) {
|
||||
if ($force_m) {
|
||||
$ret['m'] = $coordinates[2];
|
||||
} else {
|
||||
$ret['z'] = $coordinates[2];
|
||||
}
|
||||
}
|
||||
if ($count == 4) {
|
||||
$ret['z'] = $coordinates[2];
|
||||
$ret['m'] = $coordinates[3];
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
182
vendor/Shapefile/Geometry/GeometryCollection.php
vendored
Normal file
182
vendor/Shapefile/Geometry/GeometryCollection.php
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Shapefile - PHP library to read and write ESRI Shapefiles, compatible with WKT and GeoJSON
|
||||
*
|
||||
* @package Shapefile
|
||||
* @author Gaspare Sganga
|
||||
* @version 3.3.0
|
||||
* @license MIT
|
||||
* @link https://gasparesganga.com/labs/php-shapefile/
|
||||
*/
|
||||
|
||||
namespace Shapefile\Geometry;
|
||||
|
||||
use Shapefile\Shapefile;
|
||||
use Shapefile\ShapefileException;
|
||||
|
||||
/**
|
||||
* Abstract base class for all Geometry Collections.
|
||||
* It defines some common public methods and some helper protected functions.
|
||||
*/
|
||||
abstract class GeometryCollection extends Geometry
|
||||
{
|
||||
/**
|
||||
* @var \Shapefile\Geometry\Geometry[] The actual geometries in the collection.
|
||||
* They are enforced to be all of the same type by addGeometry() method.
|
||||
*/
|
||||
protected $geometries = [];
|
||||
|
||||
|
||||
|
||||
/////////////////////////////// ABSTRACT ///////////////////////////////
|
||||
/**
|
||||
* Gets the class name of the base geometries in the collection.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getCollectionClass();
|
||||
|
||||
|
||||
|
||||
/////////////////////////////// PUBLIC ///////////////////////////////
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \Shapefile\Geometry\Geometry[] $geometries Optional array of geometries to initialize the collection.
|
||||
*/
|
||||
public function __construct(array $geometries = null)
|
||||
{
|
||||
if ($geometries !== null) {
|
||||
foreach ($geometries as $Geometry) {
|
||||
$this->addGeometry($Geometry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function getBoundingBox()
|
||||
{
|
||||
if ($this->isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
$ret = $this->getCustomBoundingBox();
|
||||
if (!$ret) {
|
||||
$is_z = $this->isZ();
|
||||
$is_m = $this->isM();
|
||||
foreach ($this->geometries as $Geometry) {
|
||||
$bbox = $Geometry->getBoundingBox();
|
||||
if (!$ret) {
|
||||
$ret = $bbox;
|
||||
} elseif ($bbox) {
|
||||
if ($bbox['xmin'] < $ret['xmin']) {
|
||||
$ret['xmin'] = $bbox['xmin'];
|
||||
}
|
||||
if ($bbox['xmax'] > $ret['xmax']) {
|
||||
$ret['xmax'] = $bbox['xmax'];
|
||||
}
|
||||
if ($bbox['ymin'] < $ret['ymin']) {
|
||||
$ret['ymin'] = $bbox['ymin'];
|
||||
}
|
||||
if ($bbox['ymax'] > $ret['ymax']) {
|
||||
$ret['ymax'] = $bbox['ymax'];
|
||||
}
|
||||
if ($is_z) {
|
||||
if ($bbox['zmin'] < $ret['zmin']) {
|
||||
$ret['zmin'] = $bbox['zmin'];
|
||||
}
|
||||
if ($bbox['zmax'] > $ret['zmax']) {
|
||||
$ret['zmax'] = $bbox['zmax'];
|
||||
}
|
||||
}
|
||||
if ($is_m) {
|
||||
if ($ret['mmin'] === false || $bbox['mmin'] < $ret['mmin']) {
|
||||
$ret['mmin'] = $bbox['mmin'];
|
||||
}
|
||||
if ($ret['mmax'] === false || $bbox['mmax'] > $ret['mmax']) {
|
||||
$ret['mmax'] = $bbox['mmax'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/////////////////////////////// PROTECTED ///////////////////////////////
|
||||
/**
|
||||
* Adds a Geometry to the collection.
|
||||
* It enforces all geometries to be of the same type.
|
||||
*
|
||||
* @param \Shapefile\Geometry\Geometry $Geometry
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
protected function addGeometry(Geometry $Geometry)
|
||||
{
|
||||
if (!is_a($Geometry, $this->getCollectionClass())) {
|
||||
throw new ShapefileException(Shapefile::ERR_INPUT_GEOMETRY_TYPE_NOT_VALID, $this->getCollectionClass());
|
||||
}
|
||||
if (!$Geometry->isEmpty()) {
|
||||
if ($this->isEmpty()) {
|
||||
$this->setFlagEmpty(false);
|
||||
$this->setFlagZ($Geometry->isZ());
|
||||
$this->setFlagM($Geometry->isM());
|
||||
} else {
|
||||
if ($this->isZ() !== $Geometry->isZ() || $this->isM() !== $Geometry->isM()) {
|
||||
throw new ShapefileException(Shapefile::ERR_GEOM_MISMATCHED_DIMENSIONS);
|
||||
}
|
||||
}
|
||||
$this->geometries[] = $Geometry;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a Geometry at specified index from the collection.
|
||||
*
|
||||
* @param int $index The index of the Geometry.
|
||||
*
|
||||
* @return \Shapefile\Geometry\Geometry
|
||||
*/
|
||||
protected function getGeometry($index)
|
||||
{
|
||||
if (!isset($this->geometries[$index])) {
|
||||
throw new ShapefileException(Shapefile::ERR_INPUT_GEOMETRY_INDEX_NOT_VALID, $index);
|
||||
}
|
||||
return $this->geometries[$index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the geometries in the collection.
|
||||
*
|
||||
* @return \Shapefile\Geometry\Geometry[]
|
||||
*/
|
||||
protected function getGeometries()
|
||||
{
|
||||
return $this->geometries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of geometries in the collection.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function getNumGeometries()
|
||||
{
|
||||
return count($this->geometries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverses the order of geometries in the collection.
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
protected function reverseGeometries()
|
||||
{
|
||||
$this->geometries = array_reverse($this->geometries);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
218
vendor/Shapefile/Geometry/Linestring.php
vendored
Normal file
218
vendor/Shapefile/Geometry/Linestring.php
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Shapefile - PHP library to read and write ESRI Shapefiles, compatible with WKT and GeoJSON
|
||||
*
|
||||
* @package Shapefile
|
||||
* @author Gaspare Sganga
|
||||
* @version 3.3.0
|
||||
* @license MIT
|
||||
* @link https://gasparesganga.com/labs/php-shapefile/
|
||||
*/
|
||||
|
||||
namespace Shapefile\Geometry;
|
||||
|
||||
use Shapefile\Shapefile;
|
||||
use Shapefile\ShapefileException;
|
||||
|
||||
/**
|
||||
* Linestring Geometry.
|
||||
*
|
||||
* - Array: [
|
||||
* [numpoints] => int
|
||||
* [points] => [
|
||||
* [
|
||||
* [x] => float
|
||||
* [y] => float
|
||||
* [z] => float
|
||||
* [m] => float/bool
|
||||
* ]
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
* - WKT:
|
||||
* LINESTRING [Z][M] (x y z m, x y z m)
|
||||
*
|
||||
* - GeoJSON:
|
||||
* {
|
||||
* "type": "LineString" / "LineStringM"
|
||||
* "coordinates": [
|
||||
* [x, y, z] / [x, y, m] / [x, y, z, m]
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
class Linestring extends MultiPoint
|
||||
{
|
||||
/**
|
||||
* WKT and GeoJSON basetypes, collection class type
|
||||
*/
|
||||
const WKT_BASETYPE = 'LINESTRING';
|
||||
const GEOJSON_BASETYPE = 'LineString';
|
||||
const COLLECTION_CLASS = 'Point';
|
||||
|
||||
|
||||
/////////////////////////////// PUBLIC ///////////////////////////////
|
||||
/**
|
||||
* Checks whether the linestring is a closed ring or not.
|
||||
* A closed ring has at least 4 vertices and the first and last ones must be the same.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClosedRing()
|
||||
{
|
||||
return $this->getNumPoints() >= 4 && $this->getPoint(0) == $this->getPoint($this->getNumPoints() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the linestring to be a closed ring.
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
public function forceClosedRing()
|
||||
{
|
||||
if (!$this->checkRingNumPoints()) {
|
||||
throw new ShapefileException(Shapefile::ERR_GEOM_RING_NOT_ENOUGH_VERTICES);
|
||||
}
|
||||
if (!$this->isClosedRing()) {
|
||||
$this->addPoint($this->getPoint(0));
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether a ring is clockwise or not (it works with open rings too).
|
||||
*
|
||||
* Throws an exception if ring area is too small and cannot determine its orientation.
|
||||
* Returns Shapefile::UNDEFINED or throw an exception if there are not enough points.
|
||||
*
|
||||
* @param bool $flag_throw_exception Optional flag to throw an exception if there are not enough points.
|
||||
*
|
||||
* @return bool|Shapefile::UNDEFINED
|
||||
*/
|
||||
public function isClockwise($flag_throw_exception = false)
|
||||
{
|
||||
if ($this->isEmpty()) {
|
||||
return Shapefile::UNDEFINED;
|
||||
}
|
||||
|
||||
if (!$this->checkRingNumPoints()) {
|
||||
if ($flag_throw_exception) {
|
||||
throw new ShapefileException(Shapefile::ERR_GEOM_RING_NOT_ENOUGH_VERTICES);
|
||||
}
|
||||
return Shapefile::UNDEFINED;
|
||||
}
|
||||
|
||||
$area = $this->computeGaussArea($this->getArray()['points']);
|
||||
if (!$area) {
|
||||
throw new ShapefileException(Shapefile::ERR_GEOM_RING_AREA_TOO_SMALL);
|
||||
}
|
||||
|
||||
// Negative area means clockwise direction
|
||||
return $area < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the ring to be in clockwise direction (it works with open rings too).
|
||||
* Throws an exception if direction is undefined.
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
public function forceClockwise()
|
||||
{
|
||||
if ($this->isClockwise(true) === false) {
|
||||
$this->reverseGeometries();
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the ring to be in counterclockwise direction (it works with open rings too).
|
||||
* Throws an exception if direction is undefined.
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
public function forceCounterClockwise()
|
||||
{
|
||||
if ($this->isClockwise(true) === true) {
|
||||
$this->reverseGeometries();
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function getSHPBasetype()
|
||||
{
|
||||
return Shapefile::SHAPE_TYPE_POLYLINE;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////// PROTECTED ///////////////////////////////
|
||||
protected function getWKTBasetype()
|
||||
{
|
||||
return static::WKT_BASETYPE;
|
||||
}
|
||||
|
||||
protected function getGeoJSONBasetype()
|
||||
{
|
||||
return static::GEOJSON_BASETYPE;
|
||||
}
|
||||
|
||||
protected function getCollectionClass()
|
||||
{
|
||||
return __NAMESPACE__ . '\\' . static::COLLECTION_CLASS;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////// PRIVATE ///////////////////////////////
|
||||
/**
|
||||
* Checks if the linestring has enough points to be a ring.
|
||||
*/
|
||||
private function checkRingNumPoints()
|
||||
{
|
||||
return $this->getNumPoints() >= 3;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Computes ring area using a Gauss-like formula.
|
||||
* The target is to determine whether it is positive or negative, not the exact area.
|
||||
*
|
||||
* An optional $exp parameter is used to deal with very small areas.
|
||||
*
|
||||
* @param array $points Array of points. Each element must have "x" and "y" members.
|
||||
* @param int $exp Optional exponent to deal with small areas (coefficient = 10^exponent).
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
private function computeGaussArea($points, $exp = 0)
|
||||
{
|
||||
// If a coefficient of 10^9 is not enough, give up!
|
||||
if ($exp > 9) {
|
||||
return 0;
|
||||
}
|
||||
$coef = pow(10, $exp);
|
||||
|
||||
// At least 3 points (in case of an open ring) are needed to compute the area
|
||||
$num_points = count($points);
|
||||
if ($num_points < 3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Use Gauss's area formula (no need to be strict here, hence no 1/2 coefficient and no check for closed rings)
|
||||
$num_points--;
|
||||
$tot = 0;
|
||||
for ($i = 0; $i < $num_points; ++$i) {
|
||||
$tot += ($coef * $points[$i]['x'] * $points[$i + 1]['y']) - ($coef * $points[$i]['y'] * $points[$i + 1]['x']);
|
||||
}
|
||||
$tot += ($coef * $points[$num_points]['x'] * $points[0]['y']) - ($coef * $points[$num_points]['y'] * $points[0]['x']);
|
||||
|
||||
// If area is too small, increase coefficient exponent and retry
|
||||
if ($tot == 0) {
|
||||
return $this->computeGaussArea($points, $exp + 3);
|
||||
}
|
||||
|
||||
return $tot;
|
||||
}
|
||||
}
|
||||
236
vendor/Shapefile/Geometry/MultiLinestring.php
vendored
Normal file
236
vendor/Shapefile/Geometry/MultiLinestring.php
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Shapefile - PHP library to read and write ESRI Shapefiles, compatible with WKT and GeoJSON
|
||||
*
|
||||
* @package Shapefile
|
||||
* @author Gaspare Sganga
|
||||
* @version 3.3.0
|
||||
* @license MIT
|
||||
* @link https://gasparesganga.com/labs/php-shapefile/
|
||||
*/
|
||||
|
||||
namespace Shapefile\Geometry;
|
||||
|
||||
use Shapefile\Shapefile;
|
||||
use Shapefile\ShapefileException;
|
||||
|
||||
/**
|
||||
* MultiLinestring Geometry.
|
||||
*
|
||||
* - Array: [
|
||||
* [numparts] => int
|
||||
* [parts] => [
|
||||
* [
|
||||
* [numpoints] => int
|
||||
* [points] => [
|
||||
* [
|
||||
* [x] => float
|
||||
* [y] => float
|
||||
* [z] => float
|
||||
* [m] => float/bool
|
||||
* ]
|
||||
* ]
|
||||
* ]
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
* - WKT:
|
||||
* MULTILINESTRING [Z][M] ((x y z m, x y z m, x y z m), (x y z m, x y z m))
|
||||
*
|
||||
* - GeoJSON:
|
||||
* {
|
||||
* "type": "MultiLineString" / "MultiLineStringM"
|
||||
* "coordinates": [
|
||||
* [
|
||||
* [x, y, z] / [x, y, m] / [x, y, z, m]
|
||||
* ]
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
class MultiLinestring extends GeometryCollection
|
||||
{
|
||||
/**
|
||||
* WKT and GeoJSON basetypes, collection class type
|
||||
*/
|
||||
const WKT_BASETYPE = 'MULTILINESTRING';
|
||||
const GEOJSON_BASETYPE = 'MultiLineString';
|
||||
const COLLECTION_CLASS = 'Linestring';
|
||||
|
||||
|
||||
/////////////////////////////// PUBLIC ///////////////////////////////
|
||||
public function initFromArray($array)
|
||||
{
|
||||
$this->checkInit();
|
||||
if (!isset($array['parts']) || !is_array($array['parts'])) {
|
||||
throw new ShapefileException(Shapefile::ERR_INPUT_ARRAY_NOT_VALID);
|
||||
}
|
||||
foreach ($array['parts'] as $part) {
|
||||
if (!isset($part['points']) || !is_array($part['points'])) {
|
||||
throw new ShapefileException(Shapefile::ERR_INPUT_ARRAY_NOT_VALID);
|
||||
}
|
||||
$Linestring = new Linestring();
|
||||
foreach ($part['points'] as $coordinates) {
|
||||
$Point = new Point();
|
||||
$Point->initFromArray($coordinates);
|
||||
$Linestring->addPoint($Point);
|
||||
}
|
||||
$this->addLinestring($Linestring);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function initFromWKT($wkt)
|
||||
{
|
||||
$this->checkInit();
|
||||
$wkt = $this->wktSanitize($wkt);
|
||||
if (!$this->wktIsEmpty($wkt)) {
|
||||
$force_z = $this->wktIsZ($wkt);
|
||||
$force_m = $this->wktIsM($wkt);
|
||||
foreach (explode('),(', substr($this->wktExtractData($wkt), 1, -1)) as $part) {
|
||||
$Linestring = new Linestring();
|
||||
foreach (explode(',', $part) as $wkt_coordinates) {
|
||||
$coordinates = $this->wktParseCoordinates($wkt_coordinates, $force_z, $force_m);
|
||||
$Point = new Point($coordinates['x'], $coordinates['y'], $coordinates['z'], $coordinates['m']);
|
||||
$Linestring->addPoint($Point);
|
||||
}
|
||||
$this->addLinestring($Linestring);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function initFromGeoJSON($geojson)
|
||||
{
|
||||
$this->checkInit();
|
||||
$geojson = $this->geojsonSanitize($geojson);
|
||||
if ($geojson !== null) {
|
||||
$force_m = $this->geojsonIsM($geojson['type']);
|
||||
foreach ($geojson['coordinates'] as $part) {
|
||||
$Linestring = new Linestring();
|
||||
foreach ($part as $geojson_coordinates) {
|
||||
$coordinates = $this->geojsonParseCoordinates($geojson_coordinates, $force_m);
|
||||
$Point = new Point($coordinates['x'], $coordinates['y'], $coordinates['z'], $coordinates['m']);
|
||||
$Linestring->addPoint($Point);
|
||||
}
|
||||
$this->addLinestring($Linestring);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function getArray()
|
||||
{
|
||||
$parts = [];
|
||||
foreach ($this->getLinestrings() as $Linestring) {
|
||||
$parts[] = $Linestring->getArray();
|
||||
}
|
||||
return [
|
||||
'numparts' => $this->getNumGeometries(),
|
||||
'parts' => $parts,
|
||||
];
|
||||
}
|
||||
|
||||
public function getWKT()
|
||||
{
|
||||
$ret = $this->wktInitializeOutput();
|
||||
if (!$this->isEmpty()) {
|
||||
$parts = [];
|
||||
foreach ($this->getLinestrings() as $Linestring) {
|
||||
$points = [];
|
||||
foreach ($Linestring->getPoints() as $Point) {
|
||||
$points[] = implode(' ', $Point->getRawArray());
|
||||
}
|
||||
$parts[] = '(' . implode(', ', $points) . ')';
|
||||
}
|
||||
$ret .= '(' . implode(', ', $parts) . ')';
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function getGeoJSON($flag_bbox = true, $flag_feature = false)
|
||||
{
|
||||
if ($this->isEmpty()) {
|
||||
return 'null';
|
||||
}
|
||||
$coordinates = [];
|
||||
foreach ($this->getLinestrings() as $Linestring) {
|
||||
$parts = [];
|
||||
foreach ($Linestring->getPoints() as $Point) {
|
||||
$parts[] = $Point->getRawArray();
|
||||
}
|
||||
$coordinates[] = $parts;
|
||||
}
|
||||
return $this->geojsonPackOutput($coordinates, $flag_bbox, $flag_feature);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a linestring to the collection.
|
||||
*
|
||||
* @param \Shapefile\Geometry\Linestring $Linestring
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
public function addLinestring(Linestring $Linestring)
|
||||
{
|
||||
$this->addGeometry($Linestring);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a linestring at specified index from the collection.
|
||||
*
|
||||
* @param int $index The index of the linestring.
|
||||
*
|
||||
* @return \Shapefile\Geometry\Linestring
|
||||
*/
|
||||
public function getLinestring($index)
|
||||
{
|
||||
return $this->getGeometry($index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the linestrings in the collection.
|
||||
*
|
||||
* @return \Shapefile\Geometry\Linestring[]
|
||||
*/
|
||||
public function getLinestrings()
|
||||
{
|
||||
return $this->getGeometries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of linestrings in the collection.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getNumLinestrings()
|
||||
{
|
||||
return $this->getNumGeometries();
|
||||
}
|
||||
|
||||
|
||||
public function getSHPBasetype()
|
||||
{
|
||||
return Shapefile::SHAPE_TYPE_POLYLINE;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////// PROTECTED ///////////////////////////////
|
||||
protected function getWKTBasetype()
|
||||
{
|
||||
return static::WKT_BASETYPE;
|
||||
}
|
||||
|
||||
protected function getGeoJSONBasetype()
|
||||
{
|
||||
return static::GEOJSON_BASETYPE;
|
||||
}
|
||||
|
||||
protected function getCollectionClass()
|
||||
{
|
||||
return __NAMESPACE__ . '\\' . static::COLLECTION_CLASS;
|
||||
}
|
||||
}
|
||||
207
vendor/Shapefile/Geometry/MultiPoint.php
vendored
Normal file
207
vendor/Shapefile/Geometry/MultiPoint.php
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Shapefile - PHP library to read and write ESRI Shapefiles, compatible with WKT and GeoJSON
|
||||
*
|
||||
* @package Shapefile
|
||||
* @author Gaspare Sganga
|
||||
* @version 3.3.0
|
||||
* @license MIT
|
||||
* @link https://gasparesganga.com/labs/php-shapefile/
|
||||
*/
|
||||
|
||||
namespace Shapefile\Geometry;
|
||||
|
||||
use Shapefile\Shapefile;
|
||||
use Shapefile\ShapefileException;
|
||||
|
||||
/**
|
||||
* MultiPoint Geometry.
|
||||
*
|
||||
* - Array: [
|
||||
* [numpoints] => int
|
||||
* [points] => [
|
||||
* [
|
||||
* [x] => float
|
||||
* [y] => float
|
||||
* [z] => float
|
||||
* [m] => float/bool
|
||||
* ]
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
* - WKT:
|
||||
* MULTIPOINT [Z][M] (x y z m, x y z m)
|
||||
* N.B.: Points coordinates may be enclosed in additional brackets: MULTIPOINT ((x y z m), (x y z m))
|
||||
*
|
||||
* - GeoJSON:
|
||||
* {
|
||||
* "type": "MultiPoint" / "MultiPointM"
|
||||
* "coordinates": [
|
||||
* [x, y, z] / [x, y, m] / [x, y, z, m]
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
class MultiPoint extends GeometryCollection
|
||||
{
|
||||
/**
|
||||
* WKT and GeoJSON basetypes, collection class type
|
||||
*/
|
||||
const WKT_BASETYPE = 'MULTIPOINT';
|
||||
const GEOJSON_BASETYPE = 'MultiPoint';
|
||||
const COLLECTION_CLASS = 'Point';
|
||||
|
||||
|
||||
/////////////////////////////// PUBLIC ///////////////////////////////
|
||||
public function initFromArray($array)
|
||||
{
|
||||
$this->checkInit();
|
||||
if (!isset($array['points']) || !is_array($array['points'])) {
|
||||
throw new ShapefileException(Shapefile::ERR_INPUT_ARRAY_NOT_VALID);
|
||||
}
|
||||
foreach ($array['points'] as $coordinates) {
|
||||
$Point = new Point();
|
||||
$Point->initFromArray($coordinates);
|
||||
$this->addPoint($Point);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function initFromWKT($wkt)
|
||||
{
|
||||
$this->checkInit();
|
||||
$wkt = $this->wktSanitize($wkt);
|
||||
if (!$this->wktIsEmpty($wkt)) {
|
||||
$force_z = $this->wktIsZ($wkt);
|
||||
$force_m = $this->wktIsM($wkt);
|
||||
foreach (explode(',', str_replace(array('(', ')'), '', $this->wktExtractData($wkt))) as $wkt_coordinates) {
|
||||
$coordinates = $this->wktParseCoordinates($wkt_coordinates, $force_z, $force_m);
|
||||
$Point = new Point($coordinates['x'], $coordinates['y'], $coordinates['z'], $coordinates['m']);
|
||||
$this->addPoint($Point);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function initFromGeoJSON($geojson)
|
||||
{
|
||||
$this->checkInit();
|
||||
$geojson = $this->geojsonSanitize($geojson);
|
||||
if ($geojson !== null) {
|
||||
$force_m = $this->geojsonIsM($geojson['type']);
|
||||
foreach ($geojson['coordinates'] as $geojson_coordinates) {
|
||||
$coordinates = $this->geojsonParseCoordinates($geojson_coordinates, $force_m);
|
||||
$Point = new Point($coordinates['x'], $coordinates['y'], $coordinates['z'], $coordinates['m']);
|
||||
$this->addPoint($Point);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function getArray()
|
||||
{
|
||||
$points = [];
|
||||
foreach ($this->getPoints() as $Point) {
|
||||
$points[] = $Point->getArray();
|
||||
}
|
||||
return [
|
||||
'numpoints' => $this->getNumGeometries(),
|
||||
'points' => $points,
|
||||
];
|
||||
}
|
||||
|
||||
public function getWKT()
|
||||
{
|
||||
$ret = $this->wktInitializeOutput();
|
||||
if (!$this->isEmpty()) {
|
||||
$points = [];
|
||||
foreach ($this->getPoints() as $Point) {
|
||||
$points[] = implode(' ', $Point->getRawArray());
|
||||
}
|
||||
$ret .= '(' . implode(', ', $points) . ')';
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function getGeoJSON($flag_bbox = true, $flag_feature = false)
|
||||
{
|
||||
if ($this->isEmpty()) {
|
||||
return 'null';
|
||||
}
|
||||
$coordinates = [];
|
||||
foreach ($this->getPoints() as $Point) {
|
||||
$coordinates[] = $Point->getRawArray();
|
||||
}
|
||||
return $this->geojsonPackOutput($coordinates, $flag_bbox, $flag_feature);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a point to the collection.
|
||||
*
|
||||
* @param \Shapefile\Geometry\Point $Point
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
public function addPoint(Point $Point)
|
||||
{
|
||||
$this->addGeometry($Point);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a point at specified index from the collection.
|
||||
*
|
||||
* @param int $index The index of the point.
|
||||
*
|
||||
* @return \Shapefile\Geometry\Point
|
||||
*/
|
||||
public function getPoint($index)
|
||||
{
|
||||
return $this->getGeometry($index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the points in the collection.
|
||||
*
|
||||
* @return \Shapefile\Geometry\Point[]
|
||||
*/
|
||||
public function getPoints()
|
||||
{
|
||||
return $this->getGeometries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of points in the collection.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getNumPoints()
|
||||
{
|
||||
return $this->getNumGeometries();
|
||||
}
|
||||
|
||||
|
||||
public function getSHPBasetype()
|
||||
{
|
||||
return Shapefile::SHAPE_TYPE_MULTIPOINT;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////// PROTECTED ///////////////////////////////
|
||||
protected function getWKTBasetype()
|
||||
{
|
||||
return static::WKT_BASETYPE;
|
||||
}
|
||||
|
||||
protected function getGeoJSONBasetype()
|
||||
{
|
||||
return static::GEOJSON_BASETYPE;
|
||||
}
|
||||
|
||||
protected function getCollectionClass()
|
||||
{
|
||||
return __NAMESPACE__ . '\\' . static::COLLECTION_CLASS;
|
||||
}
|
||||
}
|
||||
435
vendor/Shapefile/Geometry/MultiPolygon.php
vendored
Normal file
435
vendor/Shapefile/Geometry/MultiPolygon.php
vendored
Normal file
@@ -0,0 +1,435 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Shapefile - PHP library to read and write ESRI Shapefiles, compatible with WKT and GeoJSON
|
||||
*
|
||||
* @package Shapefile
|
||||
* @author Gaspare Sganga
|
||||
* @version 3.3.0
|
||||
* @license MIT
|
||||
* @link https://gasparesganga.com/labs/php-shapefile/
|
||||
*/
|
||||
|
||||
namespace Shapefile\Geometry;
|
||||
|
||||
use Shapefile\Shapefile;
|
||||
use Shapefile\ShapefileException;
|
||||
|
||||
/**
|
||||
* MultiPolygon Geometry.
|
||||
*
|
||||
* - Array: [
|
||||
* [numparts] => int
|
||||
* [parts] => [
|
||||
* [
|
||||
* [numrings] => int
|
||||
* [rings] => [
|
||||
* [
|
||||
* [numpoints] => int
|
||||
* [points] => [
|
||||
* [
|
||||
* [x] => float
|
||||
* [y] => float
|
||||
* [z] => float
|
||||
* [m] => float/bool
|
||||
* ]
|
||||
* ]
|
||||
* ]
|
||||
* ]
|
||||
* ]
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
* - WKT:
|
||||
* MULTIPOLYGON [Z][M] (((x y z m, x y z m, x y z m, x y z m), (x y z m, x y z m, x y z m)), ((x y z m, x y z m, x y z m, x y z m), (x y z m, x y z m, x y z m)))
|
||||
*
|
||||
* - GeoJSON:
|
||||
* {
|
||||
* "type": "MultiPolygon" / "MultiPolygonM"
|
||||
* "coordinates": [
|
||||
* [
|
||||
* [
|
||||
* [x, y, z] / [x, y, m] / [x, y, z, m]
|
||||
* ]
|
||||
* ]
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
class MultiPolygon extends GeometryCollection
|
||||
{
|
||||
/**
|
||||
* WKT and GeoJSON basetypes, collection class type
|
||||
*/
|
||||
const WKT_BASETYPE = 'MULTIPOLYGON';
|
||||
const GEOJSON_BASETYPE = 'MultiPolygon';
|
||||
const COLLECTION_CLASS = 'Polygon';
|
||||
|
||||
|
||||
/**
|
||||
* @var int Action to perform on polygons rings.
|
||||
*/
|
||||
private $closed_rings;
|
||||
|
||||
/**
|
||||
* @var int Orientation to force for polygons rings.
|
||||
*/
|
||||
private $force_orientation;
|
||||
|
||||
|
||||
|
||||
/////////////////////////////// PUBLIC ///////////////////////////////
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \Shapefile\Geometry\Polygon[] $polygons Optional array of polygons to initialize the multipolygon.
|
||||
* @param int $closed_rings Optional action to perform on polygons rings. Possible values:
|
||||
* - Shapefile::ACTION_IGNORE
|
||||
* - Shapefile::ACTION_CHECK
|
||||
* - Shapefile::ACTION_FORCE
|
||||
* @param int $force_orientation Optional orientation to force for polygons rings. Possible values:
|
||||
* - Shapefile::ORIENTATION_CLOCKWISE
|
||||
* - Shapefile::ORIENTATION_COUNTERCLOCKWISE
|
||||
* - Shapefile::ORIENTATION_UNCHANGED
|
||||
*/
|
||||
public function __construct(array $polygons = null, $closed_rings = Shapefile::ACTION_CHECK, $force_orientation = Shapefile::ORIENTATION_COUNTERCLOCKWISE)
|
||||
{
|
||||
$this->closed_rings = $closed_rings;
|
||||
$this->force_orientation = $force_orientation;
|
||||
parent::__construct($polygons);
|
||||
}
|
||||
|
||||
|
||||
public function initFromArray($array)
|
||||
{
|
||||
$this->checkInit();
|
||||
if (!isset($array['parts']) || !is_array($array['parts'])) {
|
||||
throw new ShapefileException(Shapefile::ERR_INPUT_ARRAY_NOT_VALID);
|
||||
}
|
||||
foreach ($array['parts'] as $part) {
|
||||
if (!isset($part['rings']) || !is_array($part['rings'])) {
|
||||
throw new ShapefileException(Shapefile::ERR_INPUT_ARRAY_NOT_VALID);
|
||||
}
|
||||
$Polygon = new Polygon(null, $this->closed_rings, $this->force_orientation);
|
||||
foreach ($part['rings'] as $part) {
|
||||
if (!isset($part['points']) || !is_array($part['points'])) {
|
||||
throw new ShapefileException(Shapefile::ERR_INPUT_ARRAY_NOT_VALID);
|
||||
}
|
||||
$Linestring = new Linestring();
|
||||
foreach ($part['points'] as $coordinates) {
|
||||
$Point = new Point();
|
||||
$Point->initFromArray($coordinates);
|
||||
$Linestring->addPoint($Point);
|
||||
}
|
||||
$Polygon->addLinestring($Linestring);
|
||||
}
|
||||
$this->addGeometry($Polygon, false);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function initFromWKT($wkt)
|
||||
{
|
||||
$this->checkInit();
|
||||
$wkt = $this->wktSanitize($wkt);
|
||||
if (!$this->wktIsEmpty($wkt)) {
|
||||
$force_z = $this->wktIsZ($wkt);
|
||||
$force_m = $this->wktIsM($wkt);
|
||||
foreach (explode(')),((', substr($this->wktExtractData($wkt), 2, -2)) as $part) {
|
||||
$Polygon = new Polygon(null, $this->closed_rings, $this->force_orientation);
|
||||
foreach (explode('),(', $part) as $ring) {
|
||||
$Linestring = new Linestring();
|
||||
foreach (explode(',', $ring) as $wkt_coordinates) {
|
||||
$coordinates = $this->wktParseCoordinates($wkt_coordinates, $force_z, $force_m);
|
||||
$Point = new Point($coordinates['x'], $coordinates['y'], $coordinates['z'], $coordinates['m']);
|
||||
$Linestring->addPoint($Point);
|
||||
}
|
||||
$Polygon->addLinestring($Linestring);
|
||||
}
|
||||
$this->addGeometry($Polygon, false);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function initFromGeoJSON($geojson)
|
||||
{
|
||||
$this->checkInit();
|
||||
$geojson = $this->geojsonSanitize($geojson);
|
||||
if ($geojson !== null) {
|
||||
$force_m = $this->geojsonIsM($geojson['type']);
|
||||
foreach ($geojson['coordinates'] as $part) {
|
||||
$Polygon = new Polygon(null, $this->closed_rings, $this->force_orientation);
|
||||
foreach ($part as $ring) {
|
||||
$Linestring = new Linestring();
|
||||
foreach ($ring as $geojson_coordinates) {
|
||||
$coordinates = $this->geojsonParseCoordinates($geojson_coordinates, $force_m);
|
||||
$Point = new Point($coordinates['x'], $coordinates['y'], $coordinates['z'], $coordinates['m']);
|
||||
$Linestring->addPoint($Point);
|
||||
}
|
||||
$Polygon->addLinestring($Linestring);
|
||||
}
|
||||
$this->addGeometry($Polygon, false);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function getArray()
|
||||
{
|
||||
$parts = [];
|
||||
foreach ($this->getPolygons() as $Polygon) {
|
||||
$parts[] = $Polygon->getArray();
|
||||
}
|
||||
return [
|
||||
'numparts' => $this->getNumGeometries(),
|
||||
'parts' => $parts,
|
||||
];
|
||||
}
|
||||
|
||||
public function getWKT()
|
||||
{
|
||||
$ret = $this->wktInitializeOutput();
|
||||
if (!$this->isEmpty()) {
|
||||
$parts = [];
|
||||
foreach ($this->getPolygons() as $Polygon) {
|
||||
$rings = [];
|
||||
foreach ($Polygon->getLinestrings() as $Linestring) {
|
||||
$points = [];
|
||||
foreach ($Linestring->getPoints() as $Point) {
|
||||
$points[] = implode(' ', $Point->getRawArray());
|
||||
}
|
||||
$rings[] = '(' . implode(', ', $points) . ')';
|
||||
}
|
||||
$parts[] = '(' . implode(', ', $rings) . ')';
|
||||
}
|
||||
$ret .= '(' . implode(', ', $parts) . ')';
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function getGeoJSON($flag_bbox = true, $flag_feature = false)
|
||||
{
|
||||
if ($this->isEmpty()) {
|
||||
return 'null';
|
||||
}
|
||||
$coordinates = [];
|
||||
foreach ($this->getPolygons() as $Polygon) {
|
||||
$parts = [];
|
||||
foreach ($Polygon->getLinestrings() as $Linestring) {
|
||||
$rings = [];
|
||||
foreach ($Linestring->getPoints() as $Point) {
|
||||
$rings[] = $Point->getRawArray();
|
||||
}
|
||||
$parts[] = $rings;
|
||||
}
|
||||
$coordinates[] = $parts;
|
||||
}
|
||||
return $this->geojsonPackOutput($coordinates, $flag_bbox, $flag_feature);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a polygon to the collection.
|
||||
*
|
||||
* @param \Shapefile\Geometry\Polygon $Polygon
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
public function addPolygon(Polygon $Polygon)
|
||||
{
|
||||
$this->addGeometry($Polygon, true);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a polygon at specified index from the collection.
|
||||
*
|
||||
* @param int $index The index of the polygon.
|
||||
*
|
||||
* @return \Shapefile\Geometry\Polygon
|
||||
*/
|
||||
public function getPolygon($index)
|
||||
{
|
||||
return $this->getGeometry($index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the polygons in the collection.
|
||||
*
|
||||
* @return \Shapefile\Geometry\Polygon[]
|
||||
*/
|
||||
public function getPolygons()
|
||||
{
|
||||
return $this->getGeometries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of polygons in the collection.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getNumPolygons()
|
||||
{
|
||||
return $this->getNumGeometries();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Forces multipolygon rings to be closed.
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
public function forceClosedRings()
|
||||
{
|
||||
foreach ($this->getPolygons() as $Polygon) {
|
||||
$Polygon->forceClosedRings();
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether all multipolygon outer rings have a clockwise orientation and all the inner rings have a counterclockwise one.
|
||||
* Note that a false return value does not guarantee multipolygon is strictly counterclockwise. Use MultiPolygon::forceCounterClockwise() to enforce that!
|
||||
*
|
||||
* Returns Shapefile::UNDEFINED if geometry is empty.
|
||||
*
|
||||
* @return bool|Shapefile::UNDEFINED
|
||||
*/
|
||||
public function isClockwise()
|
||||
{
|
||||
if ($this->isEmpty()) {
|
||||
return Shapefile::UNDEFINED;
|
||||
}
|
||||
|
||||
foreach ($this->getPolygons() as $Polygon) {
|
||||
if ($Polygon->getOuterRing()->isClockwise(true) === false) {
|
||||
return false;
|
||||
}
|
||||
foreach ($Polygon->getInnerRings() as $Linestring) {
|
||||
if ($Linestring->isClockwise(true) === true) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether all multipolygon outer rings have a counterclockwise orientation and all the inner rings have a clockwise one.
|
||||
* Note that a false return value does not guarantee multipolygon is strictly clockwise. Use MultiPolygon::forceClockwise() to enforce that!
|
||||
*
|
||||
* Returns Shapefile::UNDEFINED if geometry is empty.
|
||||
*
|
||||
* @return bool|Shapefile::UNDEFINED
|
||||
*/
|
||||
public function isCounterClockwise()
|
||||
{
|
||||
if ($this->isEmpty()) {
|
||||
return Shapefile::UNDEFINED;
|
||||
}
|
||||
|
||||
foreach ($this->getPolygons() as $Polygon) {
|
||||
if ($Polygon->getOuterRing()->isClockwise(true) === true) {
|
||||
return false;
|
||||
}
|
||||
foreach ($Polygon->getInnerRings() as $Linestring) {
|
||||
if ($Linestring->isClockwise(true) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces all multipolygon outer rings to have a clockwise orientation and all the inner rings to have a counterclockwise one.
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
public function forceClockwise()
|
||||
{
|
||||
foreach ($this->getPolygons() as $Polygon) {
|
||||
$Polygon->getOuterRing()->forceClockwise();
|
||||
foreach ($Polygon->getInnerRings() as $Linestring) {
|
||||
$Linestring->forceCounterClockwise();
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces all multipolygon outer rings to have a counterclockwise orientation and all the inner rings to have a clockwise one.
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
public function forceCounterClockwise()
|
||||
{
|
||||
foreach ($this->getPolygons() as $Polygon) {
|
||||
$Polygon->getOuterRing()->forceCounterClockwise();
|
||||
foreach ($Polygon->getInnerRings() as $Linestring) {
|
||||
$Linestring->forceClockwise();
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function getSHPBasetype()
|
||||
{
|
||||
return Shapefile::SHAPE_TYPE_POLYGON;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////// PROTECTED ///////////////////////////////
|
||||
/**
|
||||
* Enforces class-wide action and orientation for polygons rings.
|
||||
*
|
||||
* @param \Shapefile\Geometry\Geometry $Polygon
|
||||
* @param bool $flag_rings_and_orientation Optionally enforce class action and orientation for rings.
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
protected function addGeometry(Geometry $Polygon, $flag_rings_and_orientation = true)
|
||||
{
|
||||
parent::addGeometry($Polygon);
|
||||
|
||||
if ($flag_rings_and_orientation && ($this->closed_rings != Shapefile::ACTION_IGNORE || $this->force_orientation != Shapefile::ORIENTATION_UNCHANGED)) {
|
||||
foreach ($Polygon->getRings() as $i => $Linestring) {
|
||||
// Closed rings
|
||||
if ($this->closed_rings == Shapefile::ACTION_FORCE) {
|
||||
$Linestring->forceClosedRing();
|
||||
} elseif ($this->closed_rings == Shapefile::ACTION_CHECK && !$Linestring->isClosedRing()) {
|
||||
throw new ShapefileException(Shapefile::ERR_GEOM_POLYGON_OPEN_RING);
|
||||
}
|
||||
// Orientation
|
||||
if ($this->force_orientation == Shapefile::ORIENTATION_CLOCKWISE) {
|
||||
$Linestring->{($i == 0) ? 'forceClockwise' : 'forceCounterClockwise'}();
|
||||
} elseif ($this->force_orientation == Shapefile::ORIENTATION_COUNTERCLOCKWISE) {
|
||||
$Linestring->{($i == 0) ? 'forceCounterClockwise' : 'forceClockwise'}();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
protected function getWKTBasetype()
|
||||
{
|
||||
return static::WKT_BASETYPE;
|
||||
}
|
||||
|
||||
protected function getGeoJSONBasetype()
|
||||
{
|
||||
return static::GEOJSON_BASETYPE;
|
||||
}
|
||||
|
||||
protected function getCollectionClass()
|
||||
{
|
||||
return __NAMESPACE__ . '\\' . static::COLLECTION_CLASS;
|
||||
}
|
||||
}
|
||||
312
vendor/Shapefile/Geometry/Point.php
vendored
Normal file
312
vendor/Shapefile/Geometry/Point.php
vendored
Normal file
@@ -0,0 +1,312 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Shapefile - PHP library to read and write ESRI Shapefiles, compatible with WKT and GeoJSON
|
||||
*
|
||||
* @package Shapefile
|
||||
* @author Gaspare Sganga
|
||||
* @version 3.3.0
|
||||
* @license MIT
|
||||
* @link https://gasparesganga.com/labs/php-shapefile/
|
||||
*/
|
||||
|
||||
namespace Shapefile\Geometry;
|
||||
|
||||
use Shapefile\Shapefile;
|
||||
use Shapefile\ShapefileException;
|
||||
|
||||
/**
|
||||
* Point Geometry.
|
||||
*
|
||||
* - Array: [
|
||||
* [x] => float
|
||||
* [y] => float
|
||||
* [z] => float
|
||||
* [m] => float/bool
|
||||
* ]
|
||||
*
|
||||
* - WKT:
|
||||
* POINT [Z][M] (x y z m)
|
||||
*
|
||||
*
|
||||
* - GeoJSON:
|
||||
* {
|
||||
* "type": "Point" / "PointM"
|
||||
* "coordinates": [x, y, z] / [x, y, m] / [x, y, z, m]
|
||||
* }
|
||||
*/
|
||||
class Point extends Geometry
|
||||
{
|
||||
/**
|
||||
* WKT and GeoJSON basetypes
|
||||
*/
|
||||
const WKT_BASETYPE = 'POINT';
|
||||
const GEOJSON_BASETYPE = 'Point';
|
||||
|
||||
/**
|
||||
* @var float|null X coordinate
|
||||
*/
|
||||
private $x = null;
|
||||
|
||||
/**
|
||||
* @var float|null Y coordinate
|
||||
*/
|
||||
private $y = null;
|
||||
|
||||
/**
|
||||
* @var floa|null Z coordinate
|
||||
*/
|
||||
private $z = null;
|
||||
|
||||
/**
|
||||
* @var float|bool|null M coordinate
|
||||
*/
|
||||
private $m = null;
|
||||
|
||||
|
||||
/////////////////////////////// PUBLIC ///////////////////////////////
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param float $x X coordinate
|
||||
* @param float $y Y coordinate
|
||||
* @param float $z Z coordinate
|
||||
* @param float|bool $m M coordinate
|
||||
*/
|
||||
public function __construct($x = null, $y = null, $z = null, $m = null)
|
||||
{
|
||||
$this->init($x, $y, $z, $m);
|
||||
}
|
||||
|
||||
public function initFromArray($array)
|
||||
{
|
||||
$this->checkInit();
|
||||
$this->init(
|
||||
isset($array['x']) ? $array['x'] : null,
|
||||
isset($array['y']) ? $array['y'] : null,
|
||||
isset($array['z']) ? $array['z'] : null,
|
||||
isset($array['m']) ? $array['m'] : null
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function initFromWKT($wkt)
|
||||
{
|
||||
$this->checkInit();
|
||||
$wkt = $this->wktSanitize($wkt);
|
||||
if (!$this->wktIsEmpty($wkt)) {
|
||||
$coordinates = $this->wktParseCoordinates(
|
||||
$this->wktExtractData($wkt),
|
||||
$this->wktIsZ($wkt),
|
||||
$this->wktIsM($wkt)
|
||||
);
|
||||
$this->init($coordinates['x'], $coordinates['y'], $coordinates['z'], $coordinates['m']);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function initFromGeoJSON($geojson)
|
||||
{
|
||||
$this->checkInit();
|
||||
$geojson = $this->geojsonSanitize($geojson);
|
||||
if ($geojson !== null) {
|
||||
$coordinates = $this->geojsonParseCoordinates($geojson['coordinates'], $this->geojsonIsM($geojson['type']));
|
||||
$this->init($coordinates['x'], $coordinates['y'], $coordinates['z'], $coordinates['m']);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function getArray()
|
||||
{
|
||||
if ($this->isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$ret = [
|
||||
'x' => $this->x,
|
||||
'y' => $this->y,
|
||||
];
|
||||
if ($this->isZ()) {
|
||||
$ret['z'] = $this->z;
|
||||
}
|
||||
if ($this->isM()) {
|
||||
$ret['m'] = $this->m;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function getWKT()
|
||||
{
|
||||
$ret = $this->wktInitializeOutput();
|
||||
if (!$this->isEmpty()) {
|
||||
$ret .= '(' . implode(' ', $this->getRawArray()) . ')';
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function getGeoJSON($flag_bbox = false, $flag_feature = false)
|
||||
{
|
||||
if ($this->isEmpty()) {
|
||||
return 'null';
|
||||
}
|
||||
return $this->geojsonPackOutput($this->getRawArray(), $flag_bbox, $flag_feature);
|
||||
}
|
||||
|
||||
public function getBoundingBox()
|
||||
{
|
||||
if ($this->isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
$ret = $this->getCustomBoundingBox();
|
||||
if (!$ret) {
|
||||
$ret = [
|
||||
'xmin' => $this->x,
|
||||
'xmax' => $this->x,
|
||||
'ymin' => $this->y,
|
||||
'ymax' => $this->y,
|
||||
];
|
||||
if ($this->isZ()) {
|
||||
$ret['zmin'] = $this->z;
|
||||
$ret['zmax'] = $this->z;
|
||||
}
|
||||
if ($this->isM()) {
|
||||
$ret['mmin'] = $this->m;
|
||||
$ret['mmax'] = $this->m;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
public function getSHPBasetype()
|
||||
{
|
||||
return Shapefile::SHAPE_TYPE_POINT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets X coordinate
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getX()
|
||||
{
|
||||
return $this->x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets Y coordinate
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getY()
|
||||
{
|
||||
return $this->y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets Z coordinate
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getZ()
|
||||
{
|
||||
return $this->z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets M coordinate
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getM()
|
||||
{
|
||||
return $this->m;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Gets an indexed array of coordinates.
|
||||
* This is not actually for public use, rather it is used by other classes in the library.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRawArray()
|
||||
{
|
||||
$ret = [];
|
||||
if (!$this->isEmpty()) {
|
||||
$ret[] = $this->x;
|
||||
$ret[] = $this->y;
|
||||
if ($this->isZ()) {
|
||||
$ret[] = $this->z;
|
||||
}
|
||||
if ($this->isM()) {
|
||||
$ret[] = $this->m === false ? 0 : $this->m ;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
/****************************** PROTECTED ******************************/
|
||||
protected function getWKTBasetype()
|
||||
{
|
||||
return static::WKT_BASETYPE;
|
||||
}
|
||||
|
||||
protected function getGeoJSONBasetype()
|
||||
{
|
||||
return static::GEOJSON_BASETYPE;
|
||||
}
|
||||
|
||||
|
||||
/****************************** PRIVATE ******************************/
|
||||
/**
|
||||
* Initializes Geometry with coordinates.
|
||||
*
|
||||
* @param float $x X coordinate
|
||||
* @param float $y Y coordinate
|
||||
* @param float $z Z coordinate
|
||||
* @param float|bool $m M coordinate
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
private function init($x = null, $y = null, $z = null, $m = null)
|
||||
{
|
||||
if ($x === null xor $y === null) {
|
||||
throw new ShapefileException(Shapefile::ERR_GEOM_POINT_NOT_VALID);
|
||||
}
|
||||
if ($x !== null && $y !== null) {
|
||||
$this->x = $this->validateCoordValue($x);
|
||||
$this->y = $this->validateCoordValue($y);
|
||||
$this->setFlagEmpty(false);
|
||||
if ($z !== null) {
|
||||
$this->z = $this->validateCoordValue($z);
|
||||
$this->setFlagZ(true);
|
||||
}
|
||||
if ($m !== null) {
|
||||
$this->m = ($m === false) ? $m : $this->validateCoordValue($m);
|
||||
$this->setFlagM(true);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a coordinate value.
|
||||
*
|
||||
* @param float $value Coordinate value
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
private function validateCoordValue($value)
|
||||
{
|
||||
if (!is_numeric($value)) {
|
||||
throw new ShapefileException(Shapefile::ERR_GEOM_COORD_VALUE_NOT_VALID, $value);
|
||||
}
|
||||
return floatval($value);
|
||||
}
|
||||
}
|
||||
341
vendor/Shapefile/Geometry/Polygon.php
vendored
Normal file
341
vendor/Shapefile/Geometry/Polygon.php
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Shapefile - PHP library to read and write ESRI Shapefiles, compatible with WKT and GeoJSON
|
||||
*
|
||||
* @package Shapefile
|
||||
* @author Gaspare Sganga
|
||||
* @version 3.3.0
|
||||
* @license MIT
|
||||
* @link https://gasparesganga.com/labs/php-shapefile/
|
||||
*/
|
||||
|
||||
namespace Shapefile\Geometry;
|
||||
|
||||
use Shapefile\Shapefile;
|
||||
use Shapefile\ShapefileException;
|
||||
|
||||
/**
|
||||
* Polygon Geometry.
|
||||
*
|
||||
* - Array: [
|
||||
* [numrings] => int
|
||||
* [rings] => [
|
||||
* [
|
||||
* [numpoints] => int
|
||||
* [points] => [
|
||||
* [
|
||||
* [x] => float
|
||||
* [y] => float
|
||||
* [z] => float
|
||||
* [m] => float/bool
|
||||
* ]
|
||||
* ]
|
||||
* ]
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
* - WKT:
|
||||
* POLYGON [Z][M] ((x y z m, x y z m, x y z m, x y z m), (x y z m, x y z m, x y z m))
|
||||
*
|
||||
* - GeoJSON:
|
||||
* {
|
||||
* "type": "Polygon" / "PolygonM"
|
||||
* "coordinates": [
|
||||
* [
|
||||
* [x, y, z] / [x, y, m] / [x, y, z, m]
|
||||
* ]
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
class Polygon extends MultiLinestring
|
||||
{
|
||||
/**
|
||||
* WKT and GeoJSON basetypes, collection class type
|
||||
*/
|
||||
const WKT_BASETYPE = 'POLYGON';
|
||||
const GEOJSON_BASETYPE = 'Polygon';
|
||||
const COLLECTION_CLASS = 'Linestring';
|
||||
|
||||
|
||||
/**
|
||||
* @var int Action to perform on polygon rings.
|
||||
*/
|
||||
private $closed_rings;
|
||||
|
||||
/**
|
||||
* @var int Orientation to force for polygon rings.
|
||||
*/
|
||||
private $force_orientation;
|
||||
|
||||
|
||||
|
||||
/////////////////////////////// PUBLIC ///////////////////////////////
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \Shapefile\Geometry\Linestring[] $linestrings Optional array of linestrings to initialize the polygon.
|
||||
* @param int $closed_rings Optional action to perform on polygon rings. Possible values:
|
||||
* - Shapefile::ACTION_IGNORE
|
||||
* - Shapefile::ACTION_CHECK
|
||||
* - Shapefile::ACTION_FORCE
|
||||
* @param int $force_orientation Optional orientation to force for polygon rings. Possible values:
|
||||
* - Shapefile::ORIENTATION_CLOCKWISE
|
||||
* - Shapefile::ORIENTATION_COUNTERCLOCKWISE
|
||||
* - Shapefile::ORIENTATION_UNCHANGED
|
||||
*/
|
||||
public function __construct(array $linestrings = null, $closed_rings = Shapefile::ACTION_CHECK, $force_orientation = Shapefile::ORIENTATION_COUNTERCLOCKWISE)
|
||||
{
|
||||
$this->closed_rings = $closed_rings;
|
||||
$this->force_orientation = $force_orientation;
|
||||
parent::__construct($linestrings);
|
||||
}
|
||||
|
||||
|
||||
public function initFromArray($array)
|
||||
{
|
||||
$this->checkInit();
|
||||
if (!isset($array['rings']) || !is_array($array['rings'])) {
|
||||
throw new ShapefileException(Shapefile::ERR_INPUT_ARRAY_NOT_VALID);
|
||||
}
|
||||
foreach ($array['rings'] as $part) {
|
||||
if (!isset($part['points']) || !is_array($part['points'])) {
|
||||
throw new ShapefileException(Shapefile::ERR_INPUT_ARRAY_NOT_VALID);
|
||||
}
|
||||
$Linestring = new Linestring();
|
||||
foreach ($part['points'] as $coordinates) {
|
||||
$Point = new Point();
|
||||
$Point->initFromArray($coordinates);
|
||||
$Linestring->addPoint($Point);
|
||||
}
|
||||
$this->addRing($Linestring);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function getArray()
|
||||
{
|
||||
$rings = [];
|
||||
foreach ($this->getLinestrings() as $Linestring) {
|
||||
$rings[] = $Linestring->getArray();
|
||||
}
|
||||
return [
|
||||
'numrings' => $this->getNumGeometries(),
|
||||
'rings' => $rings,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a ring to the collection.
|
||||
*
|
||||
* @param \Shapefile\Geometry\Linestring $Linestring
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
public function addRing(Linestring $Linestring)
|
||||
{
|
||||
$this->addGeometry($Linestring);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a ring at specified index from the collection.
|
||||
*
|
||||
* @param int $index The index of the ring.
|
||||
*
|
||||
* @return \Shapefile\Geometry\Linestring
|
||||
*/
|
||||
public function getRing($index)
|
||||
{
|
||||
return $this->getGeometry($index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the rings in the collection.
|
||||
*
|
||||
* @return \Shapefile\Geometry\Linestring[]
|
||||
*/
|
||||
public function getRings()
|
||||
{
|
||||
return $this->getGeometries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of rings in the collection.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getNumRings()
|
||||
{
|
||||
return $this->getNumGeometries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the polygon outer ring.
|
||||
*
|
||||
* @return \Shapefile\Geometry\Linestring
|
||||
*/
|
||||
public function getOuterRing()
|
||||
{
|
||||
return $this->isEmpty() ? null : $this->getRing(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets polygon inners rings.
|
||||
*
|
||||
* @return \Shapefile\Geometry\Linestring[]
|
||||
*/
|
||||
public function getInnerRings()
|
||||
{
|
||||
return array_slice($this->getRings(), 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Forces polygon rings to be closed.
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
public function forceClosedRings()
|
||||
{
|
||||
foreach ($this->getRings() as $Linestring) {
|
||||
$Linestring->forceClosedRing();
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether polygon outer ring has a clockwise orientation and all the inner rings have a counterclockwise one.
|
||||
* Note that a false return value does not guarantee polygon is strictly counterclockwise. Use Polygon::forceCounterClockwise() to enforce that!
|
||||
*
|
||||
* Returns Shapefile::UNDEFINED if geometry is empty.
|
||||
*
|
||||
* @return bool|Shapefile::UNDEFINED
|
||||
*/
|
||||
public function isClockwise()
|
||||
{
|
||||
if ($this->isEmpty()) {
|
||||
return Shapefile::UNDEFINED;
|
||||
}
|
||||
if ($this->getOuterRing()->isClockwise(true) === false) {
|
||||
return false;
|
||||
}
|
||||
foreach ($this->getInnerRings() as $Linestring) {
|
||||
if ($Linestring->isClockwise(true) === true) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether polygon outer ring has a counterclockwise orientation and all the inner rings have a clockwise one.
|
||||
* Note that a false return value does not guarantee polygon is strictly clockwise. Use Polygon::forceClockwise() to enforce that!
|
||||
*
|
||||
* Returns Shapefile::UNDEFINED if geometry is empty.
|
||||
*
|
||||
* @return bool|Shapefile::UNDEFINED
|
||||
*/
|
||||
public function isCounterClockwise()
|
||||
{
|
||||
if ($this->isEmpty()) {
|
||||
return Shapefile::UNDEFINED;
|
||||
}
|
||||
if ($this->getOuterRing()->isClockwise(true) === true) {
|
||||
return false;
|
||||
}
|
||||
foreach ($this->getInnerRings() as $Linestring) {
|
||||
if ($Linestring->isClockwise(true) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces polygon outer ring to have a clockwise orientation and all the inner rings to have a counterclockwise one.
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
public function forceClockwise()
|
||||
{
|
||||
if (!$this->isEmpty()) {
|
||||
$this->getOuterRing()->forceClockwise();
|
||||
foreach ($this->getInnerRings() as $Linestring) {
|
||||
$Linestring->forceCounterClockwise();
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces polygon outer ring to have a counterclockwise orientation and all the inner rings to have a clockwise one.
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
public function forceCounterClockwise()
|
||||
{
|
||||
if (!$this->isEmpty()) {
|
||||
$this->getOuterRing()->forceCounterClockwise();
|
||||
foreach ($this->getInnerRings() as $Linestring) {
|
||||
$Linestring->forceClockwise();
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function getSHPBasetype()
|
||||
{
|
||||
return Shapefile::SHAPE_TYPE_POLYGON;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////// PROTECTED ///////////////////////////////
|
||||
/**
|
||||
* Performs selected action and eventually forces orientation for polygon rings.
|
||||
*
|
||||
* @param \Shapefile\Geometry\Geometry $Linestring
|
||||
*
|
||||
* @return self Returns $this to provide a fluent interface.
|
||||
*/
|
||||
protected function addGeometry(Geometry $Linestring)
|
||||
{
|
||||
parent::addGeometry($Linestring);
|
||||
|
||||
// Closed rings
|
||||
if ($this->closed_rings == Shapefile::ACTION_FORCE) {
|
||||
$Linestring->forceClosedRing();
|
||||
} elseif ($this->closed_rings == Shapefile::ACTION_CHECK && !$Linestring->isClosedRing()) {
|
||||
throw new ShapefileException(Shapefile::ERR_GEOM_POLYGON_OPEN_RING);
|
||||
}
|
||||
|
||||
// Orientation
|
||||
if ($this->force_orientation == Shapefile::ORIENTATION_CLOCKWISE) {
|
||||
$Linestring->{($this->getNumGeometries() == 1) ? 'forceClockwise' : 'forceCounterClockwise'}();
|
||||
} elseif ($this->force_orientation == Shapefile::ORIENTATION_COUNTERCLOCKWISE) {
|
||||
$Linestring->{($this->getNumGeometries() == 1) ? 'forceCounterClockwise' : 'forceClockwise'}();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
protected function getWKTBasetype()
|
||||
{
|
||||
return static::WKT_BASETYPE;
|
||||
}
|
||||
|
||||
protected function getGeoJSONBasetype()
|
||||
{
|
||||
return static::GEOJSON_BASETYPE;
|
||||
}
|
||||
|
||||
protected function getCollectionClass()
|
||||
{
|
||||
return __NAMESPACE__ . '\\' . static::COLLECTION_CLASS;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user