PageRenderTime 37ms CodeModel.GetById 7ms RepoModel.GetById 0ms app.codeStats 0ms

/application/libraries/Wkt.php

https://github.com/yamamoto123/Ushahidi_Web
PHP | 553 lines | 329 code | 59 blank | 165 comment | 9 complexity | 6e34c2654b24addd0ba1e4e8b34280fe MD5 | raw file
  1. <?php
  2. /**
  3. * PHP Geometry/WKT encoder/decoder
  4. *
  5. * Mainly inspired/adapted from OpenLayers( http://www.openlayers.org )
  6. * Openlayers/format/WKT.js
  7. *
  8. * @package GeoJSON
  9. * @subpackage WKT
  10. * @author Camptocamp <info@camptocamp.com>
  11. * @author Ushahidi Team <team@ushahidi.com>
  12. * @copyright Copyright (c) 2009, Camptocamp <info@camptocamp.com>
  13. * @copyright Ushahidi - http://www.ushahidi.com
  14. * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License (LGPL)
  15. */
  16. class WKT {
  17. private $regExes = array(
  18. 'typeStr' => '/^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/',
  19. 'spaces' => '/\s+/',
  20. 'parenComma' => '/\)\s*,\s*\(/',
  21. 'doubleParenComma' => '/\)\s*\)\s*,\s*\(\s*\(/',
  22. 'trimParens' => '/^\s*\(?(.*?)\)?\s*$/'
  23. );
  24. const POINT = 'point';
  25. const MULTIPOINT = 'multipoint';
  26. const LINESTRING = 'linestring';
  27. const MULTILINESTRING = 'multilinestring';
  28. const LINEARRING = 'linearring';
  29. const POLYGON = 'polygon';
  30. const MULTIPOLYGON = 'multipolygon';
  31. const GEOMETRYCOLLECTION= 'geometrycollection';
  32. /**
  33. * Read WKT string into geometry objects
  34. *
  35. * @param string $WKT A WKT string
  36. *
  37. * @return Geometry|GeometryCollection
  38. */
  39. public function read($WKT)
  40. {
  41. $matches = array();
  42. if (!preg_match($this->regExes['typeStr'], $WKT, $matches))
  43. {
  44. return null;
  45. }
  46. return $this->parse(strtolower($matches[1]), $matches[2]);
  47. }
  48. /**
  49. * Parse WKT string into geometry objects
  50. *
  51. * @param string $WKT A WKT string
  52. *
  53. * @return Geometry|GeometryCollection
  54. */
  55. public function parse($type, $str)
  56. {
  57. $matches = array();
  58. $components = array();
  59. switch ($type)
  60. {
  61. case self::POINT:
  62. $coords = $this->pregExplode('spaces', $str);
  63. return new Point($coords[0], $coords[1]);
  64. case self::MULTIPOINT:
  65. foreach (explode(',', trim($str)) as $point)
  66. {
  67. $components[] = $this->parse(self::POINT, $point);
  68. }
  69. return new MultiPoint($components);
  70. case self::LINESTRING:
  71. foreach (explode(',', trim($str)) as $point)
  72. {
  73. $components[] = $this->parse(self::POINT, $point);
  74. }
  75. return new LineString($components);
  76. case self::MULTILINESTRING:
  77. $lines = $this->pregExplode('parenComma', $str);
  78. foreach ($lines as $l)
  79. {
  80. $line = preg_replace($this->regExes['trimParens'], '$1', $l);
  81. $components[] = $this->parse(self::LINESTRING, $line);
  82. }
  83. return new MultiLineString($components);
  84. case self::POLYGON:
  85. $rings= $this->pregExplode('parenComma', $str);
  86. foreach ($rings as $r)
  87. {
  88. $ring = preg_replace($this->regExes['trimParens'], '$1', $r);
  89. $linestring = $this->parse(self::LINESTRING, $ring);
  90. $components[] = new LinearRing($linestring->getComponents());
  91. }
  92. return new Polygon($components);
  93. case self::MULTIPOLYGON:
  94. $polygons = $this->pregExplode('doubleParenComma', $str);
  95. foreach ($polygons as $p)
  96. {
  97. $polygon = preg_replace($this->regExes['trimParens'], '$1', $p);
  98. $components[] = $this->parse(self::POLYGON, $polygon);
  99. }
  100. return new MultiPolygon($components);
  101. case self::GEOMETRYCOLLECTION:
  102. $str = preg_replace('/,\s*([A-Za-z])/', '|$1', $str);
  103. $wktArray = explode('|', trim($str));
  104. foreach ($wktArray as $wkt)
  105. {
  106. $components[] = $this->read($wkt);
  107. }
  108. return new GeometryCollection($components);
  109. default:
  110. return null;
  111. }
  112. }
  113. /**
  114. * Split string according to first match of passed regEx index of $regExes
  115. *
  116. */
  117. protected function pregExplode($regEx, $str)
  118. {
  119. $matches = array();
  120. preg_match($this->regExes[$regEx], $str, $matches);
  121. return empty($matches)?array(trim($str)):explode($matches[0], trim($str));
  122. }
  123. /**
  124. * Serialize geometries into a WKT string.
  125. *
  126. * @param Geometry $geometry
  127. *
  128. * @return string The WKT string representation of the input geometries
  129. */
  130. public function write(Geometry $geometry)
  131. {
  132. $type = strtolower(get_class($geometry));
  133. if (is_null($data = $this->extract($geometry)))
  134. {
  135. return null;
  136. }
  137. return strtoupper($type).'('.$data.')';
  138. }
  139. /**
  140. * Extract geometry to a WKT string
  141. *
  142. * @param Geometry $geometry A Geometry object
  143. *
  144. * @return strin
  145. */
  146. public function extract(Geometry $geometry)
  147. {
  148. $array = array();
  149. switch (strtolower(get_class($geometry)))
  150. {
  151. case self::POINT:
  152. return $geometry->getX().' '.$geometry->getY();
  153. case self::MULTIPOINT:
  154. case self::LINESTRING:
  155. case self::LINEARRING:
  156. foreach ($geometry as $geom)
  157. {
  158. $array[] = $this->extract($geom);
  159. }
  160. return implode(',', $array);
  161. case self::MULTILINESTRING:
  162. case self::POLYGON:
  163. case self::MULTIPOLYGON:
  164. foreach ($geometry as $geom)
  165. {
  166. $array[] = '('.$this->extract($geom).')';
  167. }
  168. return implode(',', $array);
  169. case self::GEOMETRYCOLLECTION:
  170. foreach ($geometry as $geom)
  171. {
  172. $array[] = strtoupper(get_class($geom)).'('.$this->extract($geom).')';
  173. }
  174. return implode(',', $array);
  175. default:
  176. return null;
  177. }
  178. }
  179. /**
  180. * Loads a WKT string into a Geometry Object
  181. *
  182. * @param string $WKT
  183. *
  184. * @return Geometry
  185. */
  186. static public function load($WKT)
  187. {
  188. $instance = new self;
  189. return $instance->read($WKT);
  190. }
  191. /**
  192. * Dumps a Geometry Object into a WKT string
  193. *
  194. * @param Geometry $geometry
  195. *
  196. * @return String A WKT string corresponding to passed object
  197. */
  198. static public function dump(Geometry $geometry)
  199. {
  200. $instance = new self;
  201. return $instance->write($geometry);
  202. }
  203. }
  204. abstract class Geometry
  205. {
  206. protected $geom_type;
  207. abstract public function getCoordinates();
  208. /**
  209. * Accessor for the geometry type
  210. *
  211. * @return string The Geometry type.
  212. */
  213. public function getGeomType()
  214. {
  215. return $this->geom_type;
  216. }
  217. /**
  218. * Returns an array suitable for serialization
  219. *
  220. * @return array
  221. */
  222. public function getGeoInterface()
  223. {
  224. return array(
  225. 'type'=> $this->getGeomType(),
  226. 'coordinates'=> $this->getCoordinates()
  227. );
  228. }
  229. /**
  230. * Shortcut to dump geometry as GeoJSON
  231. *
  232. * @return string The GeoJSON representation of the geometry
  233. */
  234. public function __toString()
  235. {
  236. return $this->toGeoJSON();
  237. }
  238. /**
  239. * Dumps Geometry as GeoJSON
  240. *
  241. * @return string The GeoJSON representation of the geometry
  242. */
  243. public function toGeoJSON()
  244. {
  245. return json_encode($this->getGeoInterface());
  246. }
  247. }
  248. abstract class Collection extends Geometry implements Iterator
  249. {
  250. protected $components = array();
  251. /**
  252. * Constructor
  253. *
  254. * @param array $components The components array
  255. */
  256. public function __construct(array $components)
  257. {
  258. foreach ($components as $component)
  259. {
  260. $this->add($component);
  261. }
  262. }
  263. private function add($component)
  264. {
  265. $this->components[] = $component;
  266. }
  267. /**
  268. * An accessor method which recursively calls itself to build the coordinates array
  269. *
  270. * @return array The coordinates array
  271. */
  272. public function getCoordinates()
  273. {
  274. $coordinates = array();
  275. foreach ($this->components as $component)
  276. {
  277. $coordinates[] = $component->getCoordinates();
  278. }
  279. return $coordinates;
  280. }
  281. /**
  282. * Returns Colection components
  283. *
  284. * @return array
  285. */
  286. public function getComponents()
  287. {
  288. return $this->components;
  289. }
  290. # Iterator Interface functions
  291. public function rewind()
  292. {
  293. reset($this->components);
  294. }
  295. public function current()
  296. {
  297. return current($this->components);
  298. }
  299. public function key()
  300. {
  301. return key($this->components);
  302. }
  303. public function next()
  304. {
  305. return next($this->components);
  306. }
  307. public function valid()
  308. {
  309. return $this->current() !== false;
  310. }
  311. }
  312. class GeometryCollection extends Collection
  313. {
  314. protected $geom_type = 'GeometryCollection';
  315. /**
  316. * Constructor
  317. *
  318. * @param array $geometries The Geometries array
  319. */
  320. public function __construct(array $geometries = null)
  321. {
  322. parent::__construct($geometries);
  323. }
  324. /**
  325. * Returns an array suitable for serialization
  326. *
  327. * Overrides the one defined in parent class
  328. *
  329. * @return array
  330. */
  331. public function getGeoInterface()
  332. {
  333. $geometries = array();
  334. foreach ($this->components as $geometry)
  335. {
  336. $geometries[] = $geometry->getGeoInterface();
  337. }
  338. return array(
  339. 'type' => $this->getGeomType(),
  340. 'geometries' => $geometries
  341. );
  342. }
  343. }
  344. class Point extends Geometry
  345. {
  346. private $position = array(2);
  347. protected $geom_type = 'Point';
  348. /**
  349. * Constructor
  350. *
  351. * @param float $x The x coordinate (or longitude)
  352. * @param float $y The y coordinate (or latitude)
  353. */
  354. public function __construct($x, $y)
  355. {
  356. if (!is_numeric($x) || !is_numeric($y))
  357. {
  358. throw new Exception("Bad coordinates: x and y should be numeric");
  359. }
  360. $this->position = array($x, $y);
  361. }
  362. /**
  363. * An accessor method which returns the coordinates array
  364. *
  365. * @return array The coordinates array
  366. */
  367. public function getCoordinates()
  368. {
  369. return $this->position;
  370. }
  371. /**
  372. * Returns X coordinate of the point
  373. *
  374. * @return integer The X coordinate
  375. */
  376. public function getX()
  377. {
  378. return $this->position[0];
  379. }
  380. /**
  381. * Returns X coordinate of the point
  382. *
  383. * @return integer The X coordinate
  384. */
  385. public function getY()
  386. {
  387. return $this->position[1];
  388. }
  389. }
  390. class LineString extends Collection
  391. {
  392. protected $geom_type = 'LineString';
  393. /**
  394. * Constructor
  395. *
  396. * @param array $positions The Point array
  397. */
  398. public function __construct(array $positions)
  399. {
  400. if (count($positions) > 1)
  401. {
  402. parent::__construct($positions);
  403. }
  404. else
  405. {
  406. throw new Exception("Linestring with less than two points");
  407. }
  408. }
  409. }
  410. class LinearRing extends LineString
  411. {
  412. protected $geom_type = 'LinearRing';
  413. /**
  414. * Constructor
  415. *
  416. * @param array $positions The Point array
  417. */
  418. public function __construct(array $positions)
  419. {
  420. if (count($positions) > 1)
  421. {
  422. parent::__construct($positions);
  423. }
  424. else
  425. {
  426. throw new Exception("Linestring with less than two points");
  427. }
  428. }
  429. }
  430. class Polygon extends Collection
  431. {
  432. protected $geom_type = 'Polygon';
  433. /**
  434. * Constructor
  435. *
  436. * The first linestring is the outer ring
  437. * The subsequent ones are holes
  438. * All linestrings should be linearrings
  439. *
  440. * @param array $linestrings The LineString array
  441. */
  442. public function __construct(array $linestrings)
  443. {
  444. // the GeoJSON spec (http://geojson.org/geojson-spec.html) says nothing about linestring count.
  445. // What should we do ?
  446. if (count($linestrings) > 0)
  447. {
  448. parent::__construct($linestrings);
  449. }
  450. else
  451. {
  452. throw new Exception("Polygon without an exterior ring");
  453. }
  454. }
  455. }
  456. class MultiPoint extends Collection
  457. {
  458. protected $geom_type = 'MultiPoint';
  459. /**
  460. * Constructor
  461. *
  462. * @param array $points The Point array
  463. */
  464. public function __construct(array $points)
  465. {
  466. parent::__construct($points);
  467. }
  468. }
  469. class MultiLineString extends Collection
  470. {
  471. protected $geom_type = 'MultiLineString';
  472. /**
  473. * Constructor
  474. *
  475. * @param array $linestrings The LineString array
  476. */
  477. public function __construct(array $linestrings)
  478. {
  479. parent::__construct($linestrings);
  480. }
  481. }
  482. class MultiPolygon extends Collection
  483. {
  484. protected $geom_type = 'MultiPolygon';
  485. /**
  486. * Constructor
  487. *
  488. * @param array $polygons The Polygon array
  489. */
  490. public function __construct(array $polygons)
  491. {
  492. parent::__construct($polygons);
  493. }
  494. }