WKB.class.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  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. if (!empty($point_coords)) {
  79. return new Point($point_coords[1],$point_coords[2]);
  80. }
  81. else {
  82. return new Point(); // EMPTY point
  83. }
  84. }
  85. function getLinstring(&$mem) {
  86. // Get the number of points expected in this string out of the first 4 bytes
  87. $line_length = unpack('L',fread($mem,4));
  88. // Return an empty linestring if there is no line-length
  89. if (!$line_length[1]) return new LineString();
  90. // Read the nubmer of points x2 (each point is two coords) into decimal-floats
  91. $line_coords = unpack('d*', fread($mem,$line_length[1]*$this->dimension*8));
  92. // We have our coords, build up the linestring
  93. $components = array();
  94. $i = 1;
  95. $num_coords = count($line_coords);
  96. while ($i <= $num_coords) {
  97. $components[] = new Point($line_coords[$i],$line_coords[$i+1]);
  98. $i += 2;
  99. }
  100. return new LineString($components);
  101. }
  102. function getPolygon(&$mem) {
  103. // Get the number of linestring expected in this poly out of the first 4 bytes
  104. $poly_length = unpack('L',fread($mem,4));
  105. $components = array();
  106. $i = 1;
  107. while ($i <= $poly_length[1]) {
  108. $components[] = $this->getLinstring($mem);
  109. $i++;
  110. }
  111. return new Polygon($components);
  112. }
  113. function getMulti(&$mem, $type) {
  114. // Get the number of items expected in this multi out of the first 4 bytes
  115. $multi_length = unpack('L',fread($mem,4));
  116. $components = array();
  117. $i = 1;
  118. while ($i <= $multi_length[1]) {
  119. $components[] = $this->getGeometry($mem);
  120. $i++;
  121. }
  122. switch ($type) {
  123. case 'point':
  124. return new MultiPoint($components);
  125. case 'line':
  126. return new MultiLineString($components);
  127. case 'polygon':
  128. return new MultiPolygon($components);
  129. case 'geometry':
  130. return new GeometryCollection($components);
  131. }
  132. }
  133. /**
  134. * Serialize geometries into WKB string.
  135. *
  136. * @param Geometry $geometry
  137. *
  138. * @return string The WKB string representation of the input geometries
  139. */
  140. public function write(Geometry $geometry, $write_as_hex = FALSE) {
  141. // We always write into NDR (little endian)
  142. $wkb = pack('c',1);
  143. switch ($geometry->getGeomType()) {
  144. case 'Point';
  145. $wkb .= pack('L',1);
  146. $wkb .= $this->writePoint($geometry);
  147. break;
  148. case 'LineString';
  149. $wkb .= pack('L',2);
  150. $wkb .= $this->writeLineString($geometry);
  151. break;
  152. case 'Polygon';
  153. $wkb .= pack('L',3);
  154. $wkb .= $this->writePolygon($geometry);
  155. break;
  156. case 'MultiPoint';
  157. $wkb .= pack('L',4);
  158. $wkb .= $this->writeMulti($geometry);
  159. break;
  160. case 'MultiLineString';
  161. $wkb .= pack('L',5);
  162. $wkb .= $this->writeMulti($geometry);
  163. break;
  164. case 'MultiPolygon';
  165. $wkb .= pack('L',6);
  166. $wkb .= $this->writeMulti($geometry);
  167. break;
  168. case 'GeometryCollection';
  169. $wkb .= pack('L',7);
  170. $wkb .= $this->writeMulti($geometry);
  171. break;
  172. }
  173. if ($write_as_hex) {
  174. $unpacked = unpack('H*',$wkb);
  175. return $unpacked[1];
  176. }
  177. else {
  178. return $wkb;
  179. }
  180. }
  181. function writePoint($point) {
  182. // Set the coords
  183. if (!$point->isEmpty()) {
  184. $wkb = pack('dd',$point->x(), $point->y());
  185. return $wkb;
  186. } else {
  187. return '';
  188. }
  189. }
  190. function writeLineString($line) {
  191. // Set the number of points in this line
  192. $wkb = pack('L',$line->numPoints());
  193. // Set the coords
  194. foreach ($line->getComponents() as $point) {
  195. $wkb .= pack('dd',$point->x(), $point->y());
  196. }
  197. return $wkb;
  198. }
  199. function writePolygon($poly) {
  200. // Set the number of lines in this poly
  201. $wkb = pack('L',$poly->numGeometries());
  202. // Write the lines
  203. foreach ($poly->getComponents() as $line) {
  204. $wkb .= $this->writeLineString($line);
  205. }
  206. return $wkb;
  207. }
  208. function writeMulti($geometry) {
  209. // Set the number of components
  210. $wkb = pack('L',$geometry->numGeometries());
  211. // Write the components
  212. foreach ($geometry->getComponents() as $component) {
  213. $wkb .= $this->write($component);
  214. }
  215. return $wkb;
  216. }
  217. }