From 342b9d95fe803d0918217d5dda7517d24e4aac52 Mon Sep 17 00:00:00 2001 From: Patrick Hayes Date: Thu, 5 Apr 2012 02:45:24 -0700 Subject: [PATCH] Adding EWKB and EWKT adapters --- geoPHP.inc | 18 +++++-- lib/adapters/EWKB.class.php | 93 +++++++++++++++++++++++++++++++++ lib/adapters/EWKT.class.php | 27 ++++++++++ lib/adapters/WKB.class.php | 19 +++++-- lib/adapters/WKT.class.php | 31 ++++++++++- lib/geometry/Geometry.class.php | 2 +- tests/postgis.php | 82 +++++++++++++++++++++++++++++ 7 files changed, 260 insertions(+), 12 deletions(-) create mode 100644 lib/adapters/EWKB.class.php create mode 100644 lib/adapters/EWKT.class.php create mode 100644 tests/postgis.php diff --git a/geoPHP.inc b/geoPHP.inc index 6ba89878..251b1bad 100644 --- a/geoPHP.inc +++ b/geoPHP.inc @@ -11,7 +11,9 @@ include_once("lib/adapters/GeoAdapter.class.php"); // Abtract class include_once("lib/adapters/GeoJSON.class.php"); include_once("lib/adapters/WKT.class.php"); +include_once("lib/adapters/EWKT.class.php"); include_once("lib/adapters/WKB.class.php"); +include_once("lib/adapters/EWKB.class.php"); include_once("lib/adapters/KML.class.php"); include_once("lib/adapters/GPX.class.php"); include_once("lib/adapters/GeoRSS.class.php"); @@ -46,6 +48,12 @@ class geoPHP $type_map = geoPHP::getAdapterMap(); $processor_type = $type_map[$type]; + + if (!$processor_type) { + throw new exception('geoPHP could not find an adapter of type '.htmlentities($type)); + exit; + } + $processor = new $processor_type(); // Data is not an array, just pass it normally @@ -66,11 +74,13 @@ class geoPHP static function getAdapterMap() { return array ( - 'wkt' => 'WKT', - 'wkb' => 'WKB', + 'wkt' => 'WKT', + 'ewkt' => 'EWKT', + 'wkb' => 'WKB', + 'ewkb' => 'EWKB', 'json' => 'GeoJSON', - 'kml' => 'KML', - 'gpx' => 'GPX', + 'kml' => 'KML', + 'gpx' => 'GPX', 'georss' => 'GeoRSS', 'google_geocode' => 'GoogleGeocode', ); diff --git a/lib/adapters/EWKB.class.php b/lib/adapters/EWKB.class.php new file mode 100644 index 00000000..f47e7444 --- /dev/null +++ b/lib/adapters/EWKB.class.php @@ -0,0 +1,93 @@ +read($wkb); + + // If there is an SRID, add it to the geometry + if ($srid) { + $geom->setSRID($srid); + } + + return $geom; + } + + /** + * Serialize geometries into an EWKB binary string. + * + * @param Geometry $geometry + * + * @return string The Extended-WKB binary string representation of the input geometries + */ + public function write(Geometry $geometry) { + // We always write into NDR (little endian) + $wkb = pack('c',1); + + switch ($geometry->getGeomType()) { + case 'Point'; + $wkb .= pack('L',1); + $wkb .= $this->writePoint($geometry); + break; + case 'LineString'; + $wkb .= pack('L',2); + $wkb .= $this->writeLineString($geometry); + break; + case 'Polygon'; + $wkb .= pack('L',3); + $wkb .= $this->writePolygon($geometry); + break; + case 'MultiPoint'; + $wkb .= pack('L',4); + $wkb .= $this->writeMulti($geometry); + break; + case 'MultiLineString'; + $wkb .= pack('L',5); + $wkb .= $this->writeMulti($geometry); + break; + case 'MultiPolygon'; + $wkb .= pack('L',6); + $wkb .= $this->writeMulti($geometry); + break; + case 'GeometryCollection'; + $wkb .= pack('L',7); + $wkb .= $this->writeMulti($geometry); + break; + } + + if ($write_as_hex) { + $unpacked = unpack('H*',$wkb); + return $unpacked[1]; + } + else { + return $wkb; + } + } + +} diff --git a/lib/adapters/EWKT.class.php b/lib/adapters/EWKT.class.php new file mode 100644 index 00000000..e94452bc --- /dev/null +++ b/lib/adapters/EWKT.class.php @@ -0,0 +1,27 @@ +SRID(); + $wkt = ''; + if ($srid) { + $wkt = 'SRID=' . $srid . ';'; + $wkt .= $geometry->out('wkt'); + return $wkt; + } + else { + return $geometry->out('wkt'); + } + } +} diff --git a/lib/adapters/WKB.class.php b/lib/adapters/WKB.class.php index 31859600..b084f17a 100644 --- a/lib/adapters/WKB.class.php +++ b/lib/adapters/WKB.class.php @@ -17,9 +17,12 @@ class WKB extends GeoAdapter /** * Read WKB into geometry objects * - * @param string $wkb Well-known-binary string - * @param bool $is_hex_string If this is a hexedecimal string that is in need of packing - * @return Geometry|GeometryCollection + * @param string $wkb + * Well-known-binary string + * @param bool $is_hex_string + * If this is a hexedecimal string that is in need of packing + * + * @return Geometry */ public function read($wkb, $is_hex_string = FALSE) { if ($is_hex_string) { @@ -36,10 +39,16 @@ public function read($wkb, $is_hex_string = FALSE) { } function getGeometry(&$mem) { - $base_info = unpack("corder/Ltype", fread($mem, 5)); + $base_info = unpack("corder/ctype/cz/cm/cs", fread($mem, 5)); if ($base_info['order'] !== 1) { throw new Exception('Only NDR (little endian) SKB format is supported at the moment'); } + + // If there is SRID information, ignore it - use EWKB Adapter to get SRID support + if ($base_info['s']) { + fread($mem, 4); + } + switch ($base_info['type']) { case 1: return $this->getPoint($mem); @@ -69,7 +78,7 @@ function getLinstring(&$mem) { // 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)); - + // We have our coords, build up the linestring $components = array(); $i = 1; diff --git a/lib/adapters/WKT.class.php b/lib/adapters/WKT.class.php index 01ad7acd..b7d0a0ec 100644 --- a/lib/adapters/WKT.class.php +++ b/lib/adapters/WKT.class.php @@ -15,10 +15,28 @@ class WKT extends GeoAdapter public function read($wkt) { $wkt = trim($wkt); + // If it contains a ';', then it contains additional SRID data + if (strpos($wkt,';')) { + $parts = explode(';', $wkt); + $wkt = $parts[1]; + $eparts = explode('=',$parts[0]); + $srid = $eparts[1]; + } + else { + $srid = NULL; + } + // If geos is installed, then we take a shortcut and let it parse the WKT if (geoPHP::geosInstalled()) { $reader = new GEOSWKTReader(); - return geoPHP::geosToGeometry($reader->read($wkt)); + if ($srid) { + $geom = geoPHP::geosToGeometry($reader->read($wkt)); + $geom->setSRID($srid); + return $geom; + } + else { + return geoPHP::geosToGeometry($reader->read($wkt)); + } } $wkt = str_replace(', ', ',', $wkt); @@ -29,7 +47,16 @@ public function read($wkt) { if (strtoupper(substr($wkt, 0, strlen($wkt_geom))) == $wkt_geom) { $data_string = $this->getDataString($wkt, $wkt_geom); $method = 'parse'.$geom_type; - return $this->$method($data_string); + + if ($srid) { + $geom = $this->$method($data_string); + $geom->setSRID($srid); + return $geom; + } + else { + return $this->$method($data_string); + } + } } } diff --git a/lib/geometry/Geometry.class.php b/lib/geometry/Geometry.class.php index fd423d22..e4e2a993 100644 --- a/lib/geometry/Geometry.class.php +++ b/lib/geometry/Geometry.class.php @@ -6,8 +6,8 @@ abstract class Geometry { private $geos = NULL; + protected $srid = NULL; protected $geom_type; - protected $srid; // Abtract: Standard // ----------------- diff --git a/tests/postgis.php b/tests/postgis.php new file mode 100644 index 00000000..c2318588 --- /dev/null +++ b/tests/postgis.php @@ -0,0 +1,82 @@ +setSRID(4326); + test_postgis($name, $format, $geometry, $connection, 'ewkb'); + } + } + print "Testing Done!"; +} + +function test_postgis($name, $type, $geom, $connection, $format) { + global $table; + + // Let's insert into the database using GeomFromWKB + $insert_string = pg_escape_bytea($geom->out($format)); + pg_query($connection, "INSERT INTO $table (name, type, geom) values ('$name', '$type', GeomFromWKB('$insert_string'))"); + + // SELECT using asBinary PostGIS + $result = pg_fetch_all(pg_query($connection, "SELECT asBinary(geom) as geom FROM $table WHERE name='$name'")); + foreach ($result as $item) { + $wkb = pg_unescape_bytea($item['geom']); // Make sure to unescape the hex blob + $geom = geoPHP::load($wkb, $format); // We now a full geoPHP Geometry object + } + + // SELECT and INSERT directly, with no wrapping functions + $result = pg_fetch_all(pg_query($connection, "SELECT geom as geom FROM $table WHERE name='$name'")); + foreach ($result as $item) { + $wkb = pack('H*',$item['geom']); // Unpacking the hex blob + $geom = geoPHP::load($wkb, $format); // We now have a geoPHP Geometry + + // Let's re-insert directly into postGIS + // We need to unpack the WKB + $unpacked = unpack('H*', $geom->out($format)); + $insert_string = $unpacked[1]; + pg_query($connection, "INSERT INTO $table (name, type, geom) values ('$name', '$type', '$insert_string')"); + } + + // SELECT and INSERT using as EWKT (ST_GeomFromEWKT and ST_AsEWKT) + $result = pg_fetch_all(pg_query($connection, "SELECT ST_AsEWKT(geom) as geom FROM $table WHERE name='$name'")); + foreach ($result as $item) { + $wkt = $item['geom']; // Make sure to unescape the hex blob + $geom = geoPHP::load($wkt, 'ewkt'); // We now a full geoPHP Geometry object + + // Let's re-insert directly into postGIS + $insert_string = $geom->out('ewkt'); + pg_query($connection, "INSERT INTO $table (name, type, geom) values ('$name', '$type', ST_GeomFromEWKT('$insert_string'))"); + } +} +