123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- <?php
- /*
- * (c) Patrick Hayes
- *
- * This code is open-source and licenced under the Modified BSD License.
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
- /**
- * PHP Geometry/WKB encoder/decoder
- *
- */
- class WKB extends GeoAdapter
- {
- private $dimension = 2;
- private $z = FALSE;
- private $m = FALSE;
- /**
- * 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
- */
- public function read($wkb, $is_hex_string = FALSE) {
- if ($is_hex_string) {
- $wkb = pack('H*',$wkb);
- }
- if (empty($wkb)) {
- throw new Exception('Cannot read empty WKB geometry. Found ' . gettype($wkb));
- }
- $mem = fopen('php://memory', 'r+');
- fwrite($mem, $wkb);
- fseek($mem, 0);
- $geometry = $this->getGeometry($mem);
- fclose($mem);
- return $geometry;
- }
- function getGeometry(&$mem) {
- $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 ($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);
- }
- switch ($base_info['type']) {
- case 1:
- return $this->getPoint($mem);
- case 2:
- return $this->getLinstring($mem);
- case 3:
- return $this->getPolygon($mem);
- case 4:
- return $this->getMulti($mem,'point');
- case 5:
- return $this->getMulti($mem,'line');
- case 6:
- return $this->getMulti($mem,'polygon');
- case 7:
- return $this->getMulti($mem,'geometry');
- }
- }
- function getPoint(&$mem) {
- $point_coords = unpack("d*", fread($mem,$this->dimension*8));
- return new Point($point_coords[1],$point_coords[2]);
- }
- 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*', fread($mem,$line_length[1]*$this->dimension*8));
- // We have our coords, build up the linestring
- $components = array();
- $i = 1;
- $num_coords = count($line_coords);
- while ($i <= $num_coords) {
- $components[] = new Point($line_coords[$i],$line_coords[$i+1]);
- $i += 2;
- }
- return new LineString($components);
- }
- function getPolygon(&$mem) {
- // Get the number of linestring expected in this poly out of the first 4 bytes
- $poly_length = unpack('L',fread($mem,4));
- $components = array();
- $i = 1;
- while ($i <= $poly_length[1]) {
- $components[] = $this->getLinstring($mem);
- $i++;
- }
- return new Polygon($components);
- }
- function getMulti(&$mem, $type) {
- // Get the number of items expected in this multi out of the first 4 bytes
- $multi_length = unpack('L',fread($mem,4));
- $components = array();
- $i = 1;
- while ($i <= $multi_length[1]) {
- $components[] = $this->getGeometry($mem);
- $i++;
- }
- switch ($type) {
- case 'point':
- return new MultiPoint($components);
- case 'line':
- return new MultiLineString($components);
- case 'polygon':
- return new MultiPolygon($components);
- case 'geometry':
- return new GeometryCollection($components);
- }
- }
- /**
- * Serialize geometries into WKB string.
- *
- * @param Geometry $geometry
- *
- * @return string The WKB string representation of the input geometries
- */
- public function write(Geometry $geometry, $write_as_hex = FALSE) {
- // 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;
- }
- }
- function writePoint($point) {
- // Set the coords
- $wkb = pack('dd',$point->x(), $point->y());
- return $wkb;
- }
- function writeLineString($line) {
- // Set the number of points in this line
- $wkb = pack('L',$line->numPoints());
- // Set the coords
- foreach ($line->getComponents() as $point) {
- $wkb .= pack('dd',$point->x(), $point->y());
- }
- return $wkb;
- }
- function writePolygon($poly) {
- // Set the number of lines in this poly
- $wkb = pack('L',$poly->numGeometries());
- // Write the lines
- foreach ($poly->getComponents() as $line) {
- $wkb .= $this->writeLineString($line);
- }
- return $wkb;
- }
- function writeMulti($geometry) {
- // Set the number of components
- $wkb = pack('L',$geometry->numGeometries());
- // Write the components
- foreach ($geometry->getComponents() as $component) {
- $wkb .= $this->write($component);
- }
- return $wkb;
- }
- }
|