WKB.class.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. <?php
  2. /*
  3. * (c) Patrick Hayes
  4. *
  5. * This code is open-source and licenced under the Modified BSD License.
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. /**
  10. * PHP Geometry/WKB encoder/decoder
  11. *
  12. */
  13. class WKB extends GeoAdapter
  14. {
  15. private $dimension = 2;
  16. private $z = FALSE;
  17. private $m = FALSE;
  18. /**
  19. * Read WKB into geometry objects
  20. *
  21. * @param string $wkb
  22. * Well-known-binary string
  23. * @param bool $is_hex_string
  24. * If this is a hexedecimal string that is in need of packing
  25. *
  26. * @return Geometry
  27. */
  28. public function read($wkb, $is_hex_string = FALSE) {
  29. if ($is_hex_string) {
  30. $wkb = pack('H*',$wkb);
  31. }
  32. if (empty($wkb)) {
  33. throw new Exception('Cannot read empty WKB geometry. Found ' . gettype($wkb));
  34. }
  35. $mem = fopen('php://memory', 'r+');
  36. fwrite($mem, $wkb);
  37. fseek($mem, 0);
  38. $geometry = $this->getGeometry($mem);
  39. fclose($mem);
  40. return $geometry;
  41. }
  42. function getGeometry(&$mem) {
  43. $base_info = unpack("corder/ctype/cz/cm/cs", fread($mem, 5));
  44. if ($base_info['order'] !== 1) {
  45. throw new Exception('Only NDR (little endian) SKB format is supported at the moment');
  46. }
  47. if ($base_info['z']) {
  48. $this->dimension++;
  49. $this->z = TRUE;
  50. }
  51. if ($base_info['m']) {
  52. $this->dimension++;
  53. $this->m = TRUE;
  54. }
  55. // If there is SRID information, ignore it - use EWKB Adapter to get SRID support
  56. if ($base_info['s']) {
  57. fread($mem, 4);
  58. }
  59. switch ($base_info['type']) {
  60. case 1:
  61. return $this->getPoint($mem);
  62. case 2:
  63. return $this->getLinstring($mem);
  64. case 3:
  65. return $this->getPolygon($mem);
  66. case 4:
  67. return $this->getMulti($mem,'point');
  68. case 5:
  69. return $this->getMulti($mem,'line');
  70. case 6:
  71. return $this->getMulti($mem,'polygon');
  72. case 7:
  73. return $this->getMulti($mem,'geometry');
  74. }
  75. }
  76. function getPoint(&$mem) {
  77. $point_coords = unpack("d*", fread($mem,$this->dimension*8));
  78. return new Point($point_coords[1],$point_coords[2]);
  79. }
  80. function getLinstring(&$mem) {
  81. // Get the number of points expected in this string out of the first 4 bytes
  82. $line_length = unpack('L',fread($mem,4));
  83. // Return an empty linestring if there is no line-length
  84. if (!$line_length[1]) return new LineString();
  85. // Read the nubmer of points x2 (each point is two coords) into decimal-floats
  86. $line_coords = unpack('d*', fread($mem,$line_length[1]*$this->dimension*8));
  87. // We have our coords, build up the linestring
  88. $components = array();
  89. $i = 1;
  90. $num_coords = count($line_coords);
  91. while ($i <= $num_coords) {
  92. $components[] = new Point($line_coords[$i],$line_coords[$i+1]);
  93. $i += 2;
  94. }
  95. return new LineString($components);
  96. }
  97. function getPolygon(&$mem) {
  98. // Get the number of linestring expected in this poly out of the first 4 bytes
  99. $poly_length = unpack('L',fread($mem,4));
  100. $components = array();
  101. $i = 1;
  102. while ($i <= $poly_length[1]) {
  103. $components[] = $this->getLinstring($mem);
  104. $i++;
  105. }
  106. return new Polygon($components);
  107. }
  108. function getMulti(&$mem, $type) {
  109. // Get the number of items expected in this multi out of the first 4 bytes
  110. $multi_length = unpack('L',fread($mem,4));
  111. $components = array();
  112. $i = 1;
  113. while ($i <= $multi_length[1]) {
  114. $components[] = $this->getGeometry($mem);
  115. $i++;
  116. }
  117. switch ($type) {
  118. case 'point':
  119. return new MultiPoint($components);
  120. case 'line':
  121. return new MultiLineString($components);
  122. case 'polygon':
  123. return new MultiPolygon($components);
  124. case 'geometry':
  125. return new GeometryCollection($components);
  126. }
  127. }
  128. /**
  129. * Serialize geometries into WKB string.
  130. *
  131. * @param Geometry $geometry
  132. *
  133. * @return string The WKB string representation of the input geometries
  134. */
  135. public function write(Geometry $geometry, $write_as_hex = FALSE) {
  136. // We always write into NDR (little endian)
  137. $wkb = pack('c',1);
  138. switch ($geometry->getGeomType()) {
  139. case 'Point';
  140. $wkb .= pack('L',1);
  141. $wkb .= $this->writePoint($geometry);
  142. break;
  143. case 'LineString';
  144. $wkb .= pack('L',2);
  145. $wkb .= $this->writeLineString($geometry);
  146. break;
  147. case 'Polygon';
  148. $wkb .= pack('L',3);
  149. $wkb .= $this->writePolygon($geometry);
  150. break;
  151. case 'MultiPoint';
  152. $wkb .= pack('L',4);
  153. $wkb .= $this->writeMulti($geometry);
  154. break;
  155. case 'MultiLineString';
  156. $wkb .= pack('L',5);
  157. $wkb .= $this->writeMulti($geometry);
  158. break;
  159. case 'MultiPolygon';
  160. $wkb .= pack('L',6);
  161. $wkb .= $this->writeMulti($geometry);
  162. break;
  163. case 'GeometryCollection';
  164. $wkb .= pack('L',7);
  165. $wkb .= $this->writeMulti($geometry);
  166. break;
  167. }
  168. if ($write_as_hex) {
  169. $unpacked = unpack('H*',$wkb);
  170. return $unpacked[1];
  171. }
  172. else {
  173. return $wkb;
  174. }
  175. }
  176. function writePoint($point) {
  177. // Set the coords
  178. $wkb = pack('dd',$point->x(), $point->y());
  179. return $wkb;
  180. }
  181. function writeLineString($line) {
  182. // Set the number of points in this line
  183. $wkb = pack('L',$line->numPoints());
  184. // Set the coords
  185. foreach ($line->getComponents() as $point) {
  186. $wkb .= pack('dd',$point->x(), $point->y());
  187. }
  188. return $wkb;
  189. }
  190. function writePolygon($poly) {
  191. // Set the number of lines in this poly
  192. $wkb = pack('L',$poly->numGeometries());
  193. // Write the lines
  194. foreach ($poly->getComponents() as $line) {
  195. $wkb .= $this->writeLineString($line);
  196. }
  197. return $wkb;
  198. }
  199. function writeMulti($geometry) {
  200. // Set the number of components
  201. $wkb = pack('L',$geometry->numGeometries());
  202. // Write the components
  203. foreach ($geometry->getComponents() as $component) {
  204. $wkb .= $this->write($component);
  205. }
  206. return $wkb;
  207. }
  208. }