WKT.class.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. <?php
  2. /**
  3. * WKT (Well Known Text) Adapter
  4. */
  5. class WKT extends GeoAdapter
  6. {
  7. /**
  8. * Read WKT string into geometry objects
  9. *
  10. * @param string $WKT A WKT string
  11. *
  12. * @return Geometry
  13. */
  14. public function read($wkt) {
  15. $wkt = trim($wkt);
  16. // If it contains a ';', then it contains additional SRID data
  17. if (strpos($wkt,';')) {
  18. $parts = explode(';', $wkt);
  19. $wkt = $parts[1];
  20. $eparts = explode('=',$parts[0]);
  21. $srid = $eparts[1];
  22. }
  23. else {
  24. $srid = NULL;
  25. }
  26. // If geos is installed, then we take a shortcut and let it parse the WKT
  27. if (geoPHP::geosInstalled()) {
  28. $reader = new GEOSWKTReader();
  29. if ($srid) {
  30. $geom = geoPHP::geosToGeometry($reader->read($wkt));
  31. $geom->setSRID($srid);
  32. return $geom;
  33. }
  34. else {
  35. return geoPHP::geosToGeometry($reader->read($wkt));
  36. }
  37. }
  38. $wkt = str_replace(', ', ',', $wkt);
  39. // For each geometry type, check to see if we have a match at the
  40. // beggining of the string. If we do, then parse using that type
  41. foreach (geoPHP::geometryList() as $geom_type) {
  42. $wkt_geom = strtoupper($geom_type);
  43. if (strtoupper(substr($wkt, 0, strlen($wkt_geom))) == $wkt_geom) {
  44. $data_string = $this->getDataString($wkt, $wkt_geom);
  45. $method = 'parse'.$geom_type;
  46. if ($srid) {
  47. $geom = $this->$method($data_string);
  48. $geom->setSRID($srid);
  49. return $geom;
  50. }
  51. else {
  52. return $this->$method($data_string);
  53. }
  54. }
  55. }
  56. }
  57. private function parsePoint($data_string) {
  58. $data_string = $this->trimParens($data_string);
  59. $parts = explode(' ',$data_string);
  60. return new Point($parts[0], $parts[1]);
  61. }
  62. private function parseLineString($data_string) {
  63. $data_string = $this->trimParens($data_string);
  64. // If it's marked as empty, then return an empty line
  65. if ($data_string == 'EMPTY') return new LineString();
  66. $parts = explode(',',$data_string);
  67. $points = array();
  68. foreach ($parts as $part) {
  69. $points[] = $this->parsePoint($part);
  70. }
  71. return new LineString($points);
  72. }
  73. private function parsePolygon($data_string) {
  74. $data_string = $this->trimParens($data_string);
  75. // If it's marked as empty, then return an empty polygon
  76. if ($data_string == 'EMPTY') return new Polygon();
  77. $parts = explode('),(',$data_string);
  78. $lines = array();
  79. foreach ($parts as $part) {
  80. if (!$this->beginsWith($part,'(')) $part = '(' . $part;
  81. if (!$this->endsWith($part,')')) $part = $part . ')';
  82. $lines[] = $this->parseLineString($part);
  83. }
  84. return new Polygon($lines);
  85. }
  86. private function parseMultiPoint($data_string) {
  87. $data_string = $this->trimParens($data_string);
  88. // If it's marked as empty, then return an empty MutiPoint
  89. if ($data_string == 'EMPTY') return new MultiPoint();
  90. $parts = explode(',',$data_string);
  91. $points = array();
  92. foreach ($parts as $part) {
  93. $points[] = $this->parsePoint($part);
  94. }
  95. return new MultiPoint($points);
  96. }
  97. private function parseMultiLineString($data_string) {
  98. $data_string = $this->trimParens($data_string);
  99. // If it's marked as empty, then return an empty multi-linestring
  100. if ($data_string == 'EMPTY') return new MultiLineString();
  101. $parts = explode('),(',$data_string);
  102. $lines = array();
  103. foreach ($parts as $part) {
  104. // Repair the string if the explode broke it
  105. if (!$this->beginsWith($part,'(')) $part = '(' . $part;
  106. if (!$this->endsWith($part,')')) $part = $part . ')';
  107. $lines[] = $this->parseLineString($part);
  108. }
  109. return new MultiLineString($lines);
  110. }
  111. private function parseMultiPolygon($data_string) {
  112. $data_string = $this->trimParens($data_string);
  113. // If it's marked as empty, then return an empty multi-polygon
  114. if ($data_string == 'EMPTY') return new MultiPolygon();
  115. $parts = explode(')),((',$data_string);
  116. $polys = array();
  117. foreach ($parts as $part) {
  118. // Repair the string if the explode broke it
  119. if (!$this->beginsWith($part,'((')) $part = '((' . $part;
  120. if (!$this->endsWith($part,'))')) $part = $part . '))';
  121. $polys[] = $this->parsePolygon($part);
  122. }
  123. return new MultiPolygon($polys);
  124. }
  125. private function parseGeometryCollection($data_string) {
  126. $data_string = $this->trimParens($data_string);
  127. // If it's marked as empty, then return an empty geom-collection
  128. if ($data_string == 'EMPTY') return new GeometryCollection();
  129. $geometries = array();
  130. $matches = array();
  131. $str = preg_replace('/,\s*([A-Za-z])/', '|$1', $data_string);
  132. $components = explode('|', trim($str));
  133. foreach ($components as $component) {
  134. $geometries[] = $this->read($component);
  135. }
  136. return new GeometryCollection($geometries);
  137. }
  138. protected function getDataString($wkt, $type) {
  139. return substr($wkt, strlen($type));
  140. }
  141. /**
  142. * Trim the parenthesis and spaces
  143. */
  144. protected function trimParens($str) {
  145. $str = trim($str);
  146. // We want to only strip off one set of parenthesis
  147. if ($this->beginsWith($str, '(')) {
  148. return substr($str,1,-1);
  149. }
  150. else return $str;
  151. }
  152. protected function beginsWith($str, $char) {
  153. if (substr($str,0,strlen($char)) == $char) return TRUE;
  154. else return FALSE;
  155. }
  156. protected function endsWith($str, $char) {
  157. if (substr($str,(0 - strlen($char))) == $char) return TRUE;
  158. else return FALSE;
  159. }
  160. /**
  161. * Serialize geometries into a WKT string.
  162. *
  163. * @param Geometry $geometry
  164. *
  165. * @return string The WKT string representation of the input geometries
  166. */
  167. public function write(Geometry $geometry) {
  168. // If geos is installed, then we take a shortcut and let it write the WKT
  169. if (geoPHP::geosInstalled()) {
  170. $writer = new GEOSWKTWriter();
  171. $writer->setTrim(TRUE);
  172. return $writer->write($geometry->geos());
  173. }
  174. if ($geometry->isEmpty()) {
  175. return strtoupper($geometry->geometryType()).' EMPTY';
  176. }
  177. else if ($data = $this->extractData($geometry)) {
  178. return strtoupper($geometry->geometryType()).' ('.$data.')';
  179. }
  180. }
  181. /**
  182. * Extract geometry to a WKT string
  183. *
  184. * @param Geometry $geometry A Geometry object
  185. *
  186. * @return string
  187. */
  188. public function extractData($geometry) {
  189. $parts = array();
  190. switch ($geometry->geometryType()) {
  191. case 'Point':
  192. return $geometry->getX().' '.$geometry->getY();
  193. case 'LineString':
  194. foreach ($geometry->getComponents() as $component) {
  195. $parts[] = $this->extractData($component);
  196. }
  197. return implode(', ', $parts);
  198. case 'Polygon':
  199. case 'MultiPoint':
  200. case 'MultiLineString':
  201. case 'MultiPolygon':
  202. foreach ($geometry->getComponents() as $component) {
  203. $parts[] = '('.$this->extractData($component).')';
  204. }
  205. return implode(', ', $parts);
  206. case 'GeometryCollection':
  207. foreach ($geometry->getComponents() as $component) {
  208. $parts[] = strtoupper($component->geometryType()).' ('.$this->extractData($component).')';
  209. }
  210. return implode(', ', $parts);
  211. }
  212. }
  213. }