From 920edeca94b997322582a5612c6c911fceba961f Mon Sep 17 00:00:00 2001 From: Patrick Hayes Date: Wed, 11 Apr 2012 20:34:40 -0700 Subject: [PATCH] Adding support for empty geometries --- lib/adapters/GPX.class.php | 1 + lib/adapters/WKB.class.php | 20 ++++++++++++++++++-- lib/adapters/WKT.class.php | 29 ++++++++++++++++++++++++++++- lib/geometry/Collection.class.php | 23 +++++++++++++++++------ lib/geometry/Geometry.class.php | 7 +------ lib/geometry/LineString.class.php | 6 +++--- lib/geometry/Point.class.php | 4 ++++ 7 files changed, 72 insertions(+), 18 deletions(-) diff --git a/lib/adapters/GPX.class.php b/lib/adapters/GPX.class.php index 8aa7cb5d..9abf1b50 100644 --- a/lib/adapters/GPX.class.php +++ b/lib/adapters/GPX.class.php @@ -32,6 +32,7 @@ public function read($gpx) { * @return string The GPX string representation of the input geometries */ public function write(Geometry $geometry) { + if ($geometry->isEmpty()) return NULL; return ''.$this->geometryToGPX($geometry).''; } diff --git a/lib/adapters/WKB.class.php b/lib/adapters/WKB.class.php index b084f17a..e21ec421 100644 --- a/lib/adapters/WKB.class.php +++ b/lib/adapters/WKB.class.php @@ -14,6 +14,10 @@ class WKB extends GeoAdapter { + private $dimension = 2; + private $z = FALSE; + private $m = FALSE; + /** * Read WKB into geometry objects * @@ -44,6 +48,15 @@ function getGeometry(&$mem) { throw new Exception('Only NDR (little endian) SKB format is supported at the moment'); } + if ($base_info['z']) { + $this->dimension++; + $this->z = TRUE; + } + if ($base_info['m']) { + $this->dimension++; + $this->m = TRUE; + } + // If there is SRID information, ignore it - use EWKB Adapter to get SRID support if ($base_info['s']) { fread($mem, 4); @@ -68,7 +81,7 @@ function getGeometry(&$mem) { } function getPoint(&$mem) { - $point_coords = unpack("d*", fread($mem,16)); + $point_coords = unpack("d*", fread($mem,$this->dimension*8)); return new Point($point_coords[1],$point_coords[2]); } @@ -76,8 +89,11 @@ function getLinstring(&$mem) { // Get the number of points expected in this string out of the first 4 bytes $line_length = unpack('L',fread($mem,4)); + // Return an empty linestring if there is no line-length + if (!$line_length[1]) return new LineString(); + // Read the nubmer of points x2 (each point is two coords) into decimal-floats - $line_coords = unpack('d'.$line_length[1]*2, fread($mem,$line_length[1]*16)); + $line_coords = unpack('d*', fread($mem,$line_length[1]*$this->dimension*8)); // We have our coords, build up the linestring $components = array(); diff --git a/lib/adapters/WKT.class.php b/lib/adapters/WKT.class.php index b7d0a0ec..6c68fced 100644 --- a/lib/adapters/WKT.class.php +++ b/lib/adapters/WKT.class.php @@ -69,6 +69,10 @@ private function parsePoint($data_string) { private function parseLineString($data_string) { $data_string = $this->trimParens($data_string); + + // If it's marked as empty, then return an empty line + if ($data_string == 'EMPTY') return new LineString(); + $parts = explode(',',$data_string); $points = array(); foreach ($parts as $part) { @@ -79,6 +83,10 @@ private function parseLineString($data_string) { private function parsePolygon($data_string) { $data_string = $this->trimParens($data_string); + + // If it's marked as empty, then return an empty polygon + if ($data_string == 'EMPTY') return new Polygon(); + $parts = explode('),(',$data_string); $lines = array(); foreach ($parts as $part) { @@ -91,6 +99,10 @@ private function parsePolygon($data_string) { private function parseMultiPoint($data_string) { $data_string = $this->trimParens($data_string); + + // If it's marked as empty, then return an empty MutiPoint + if ($data_string == 'EMPTY') return new MultiPoint(); + $parts = explode(',',$data_string); $points = array(); foreach ($parts as $part) { @@ -101,6 +113,10 @@ private function parseMultiPoint($data_string) { private function parseMultiLineString($data_string) { $data_string = $this->trimParens($data_string); + + // If it's marked as empty, then return an empty multi-linestring + if ($data_string == 'EMPTY') return new MultiLineString(); + $parts = explode('),(',$data_string); $lines = array(); foreach ($parts as $part) { @@ -114,6 +130,10 @@ private function parseMultiLineString($data_string) { private function parseMultiPolygon($data_string) { $data_string = $this->trimParens($data_string); + + // If it's marked as empty, then return an empty multi-polygon + if ($data_string == 'EMPTY') return new MultiPolygon(); + $parts = explode(')),((',$data_string); $polys = array(); foreach ($parts as $part) { @@ -127,6 +147,10 @@ private function parseMultiPolygon($data_string) { private function parseGeometryCollection($data_string) { $data_string = $this->trimParens($data_string); + + // If it's marked as empty, then return an empty geom-collection + if ($data_string == 'EMPTY') return new GeometryCollection(); + $geometries = array(); $matches = array(); $str = preg_replace('/,\s*([A-Za-z])/', '|$1', $data_string); @@ -179,7 +203,10 @@ public function write(Geometry $geometry) { return $writer->write($geometry->geos()); } - if ($data = $this->extractData($geometry)) { + if ($geometry->isEmpty()) { + return strtoupper($geometry->geometryType()).' EMPTY'; + } + else if ($data = $this->extractData($geometry)) { return strtoupper($geometry->geometryType()).' ('.$data.')'; } } diff --git a/lib/geometry/Collection.class.php b/lib/geometry/Collection.class.php index 64eedf89..b247c1e7 100644 --- a/lib/geometry/Collection.class.php +++ b/lib/geometry/Collection.class.php @@ -17,19 +17,18 @@ abstract class Collection extends Geometry * * @param array $components array of geometries */ - public function __construct(array $components) { + public function __construct($components = array()) { + if (!is_array($components)) { + throw new Exception("Component geometries must be passed as an array"); + } foreach ($components as $component) { if ($component instanceof Geometry) { $this->components[] = $component; } else { - throw new Exception("Cannot create empty collection"); + throw new Exception("Cannot create a collection with non-geometries"); } } - - if (empty($this->components)) { - throw new Exception("Cannot create empty ".get_called_class()); - } } /** @@ -177,6 +176,18 @@ public function dimension() { return $dimension; } + // A collection is empty if it has no components OR all it's components are empty + public function isEmpty() { + if (!count($this->components)) { + return TRUE; + } + else { + foreach ($this->components as $component) { + if (!$component->isEmpty()) return FALSE; + } + return TRUE; + } + } // Not valid for this geometry type // -------------------------------- diff --git a/lib/geometry/Geometry.class.php b/lib/geometry/Geometry.class.php index e4e2a993..03e4505d 100644 --- a/lib/geometry/Geometry.class.php +++ b/lib/geometry/Geometry.class.php @@ -29,7 +29,7 @@ abstract public function exteriorRing(); abstract public function numInteriorRings(); abstract public function interiorRingN($n); abstract public function dimension(); - + abstract public function isEmpty(); // Abtract: Non-Standard // --------------------- @@ -315,11 +315,6 @@ public function isMeasured() { return FALSE; } - public function isEmpty() { - // geoPHP does not yet support empty geometries - return FALSE; - } - public function coordinateDimension() { // geoPHP only supports 2-dimentional space return 2; diff --git a/lib/geometry/LineString.class.php b/lib/geometry/LineString.class.php index d14506fa..b3e14c66 100644 --- a/lib/geometry/LineString.class.php +++ b/lib/geometry/LineString.class.php @@ -13,9 +13,9 @@ class LineString extends Collection * @param array $points An array of at least two points with * which to build the LineString */ - public function __construct(array $points) { - if (count($points) < 2) { - throw new Exception("Cannot construct LineString with less than two points"); + public function __construct($points = array()) { + if (count($points) == 1) { + throw new Exception("Cannot construct a LineString with a single point"); } // Call the Collection constructor to build the LineString diff --git a/lib/geometry/Point.class.php b/lib/geometry/Point.class.php index fd4322ac..dcaff1b7 100644 --- a/lib/geometry/Point.class.php +++ b/lib/geometry/Point.class.php @@ -110,6 +110,10 @@ public function dimension() { return 0; } + public function isEmpty() { + return FALSE; + } + // Not valid for this geometry type public function numGeometries() { return NULL; } public function geometryN($n) { return NULL; }