PageRenderTime 48ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Maps/WMSDataParser.php

http://github.com/modolabs/Kurogo-Mobile-Web
PHP | 303 lines | 258 code | 38 blank | 7 comment | 33 complexity | d0d949a6485533baa2a2072f98ab38b5 MD5 | raw file
Possible License(s): LGPL-3.0, LGPL-2.1
  1. <?php
  2. require_once realpath(LIB_DIR.'/XMLElement.php');
  3. class WMSLayer extends XMLElement
  4. {
  5. const GEOGRAPHIC_PROJECTION = 'WGS84'; // something unique
  6. protected $name = 'LAYER';
  7. private $layerName; // "name" in feed
  8. private $queryable;
  9. private $title;
  10. private $abstract;
  11. private $projections = array();
  12. private $bboxes = array(); // bboxes in available projections
  13. private $styles = array();
  14. private $minScaleDenom = null;
  15. private $maxScaleDenom = null;
  16. public function canDrawAtScale($scale)
  17. {
  18. // add a small buffer for rounding errors
  19. // TODO figure out if this is necessary
  20. // 0.752 is the largest value that works for Harvard's WMS server
  21. if ($this->minScaleDenom !== null && $scale < $this->minScaleDenom * 0.752)
  22. return false;
  23. if ($this->maxScaleDenom !== null && $scale > $this->maxScaleDenom * 1.1)
  24. return false;
  25. return true;
  26. }
  27. public function getProjections()
  28. {
  29. return array_keys($this->projections);
  30. }
  31. public function getBBoxForProjection($proj) {
  32. if ($proj === null)
  33. $proj = self::GEOGRAPHIC_PROJECTION;
  34. if (isset($this->projections[$proj]))
  35. $proj = $this->projections[$proj];
  36. return $this->bboxes[$proj];
  37. }
  38. public function getLayerName() {
  39. return $this->layerName;
  40. }
  41. public function getTitle() {
  42. return $this->title;
  43. }
  44. public function getDefaultStyle() {
  45. if (count($this->styles))
  46. return $this->styles[0];
  47. return null;
  48. }
  49. public function __construct($name, $attribs)
  50. {
  51. $this->setAttribs($attribs);
  52. }
  53. public function addElement(XMLElement $element)
  54. {
  55. $name = $element->name();
  56. $value = $element->value();
  57. switch ($name)
  58. {
  59. case 'NAME':
  60. $this->layerName = $value;
  61. break;
  62. case 'TITLE':
  63. $this->title = $value;
  64. break;
  65. case 'ABSTRACT':
  66. $this->abstract = $value;
  67. break;
  68. case 'MAXSCALEDENOMINATOR':
  69. $this->maxScaleDenom = $value;
  70. break;
  71. case 'MINSCALEDENOMINATOR':
  72. $this->minScaleDenom = $value;
  73. break;
  74. case 'CRS':
  75. $projNumber = end(explode(':', $value));
  76. if ($projNumber == '4326') {
  77. // 4326 flips x and y cooordinates (x = lat, y = lon)
  78. // TODO we need a better way to deal with this special case
  79. $this->projections[$projNumber] = self::GEOGRAPHIC_PROJECTION;
  80. } else {
  81. $this->projections[$projNumber] = $value;
  82. }
  83. break;
  84. case 'EX_GEOGRAPHICBOUNDINGBOX':
  85. $this->bboxes[self::GEOGRAPHIC_PROJECTION] = array(
  86. 'xmin' => floatval($element->getProperty('WESTBOUNDLONGITUDE')),
  87. 'xmax' => floatval($element->getProperty('EASTBOUNDLONGITUDE')),
  88. 'ymin' => floatval($element->getProperty('SOUTHBOUNDLATITUDE')),
  89. 'ymax' => floatval($element->getProperty('NORTHBOUNDLATITUDE')),
  90. );
  91. break;
  92. case 'BOUNDINGBOX':
  93. $this->bboxes[$element->getAttrib('CRS')] = array(
  94. 'xmin' => floatval($element->getAttrib('MINX')),
  95. 'xmax' => floatval($element->getAttrib('MAXX')),
  96. 'ymin' => floatval($element->getAttrib('MINY')),
  97. 'ymax' => floatval($element->getAttrib('MAXY')),
  98. );
  99. break;
  100. case 'STYLE':
  101. $this->styles[] = $element;
  102. break;
  103. default:
  104. parent::addElement($element);
  105. break;
  106. }
  107. }
  108. }
  109. class WMSStyle extends XMLElement
  110. {
  111. protected $name = 'STYLE';
  112. private $styleName; // "name" in feed
  113. private $title;
  114. public function __construct($name, $attribs)
  115. {
  116. $this->setAttribs($attribs);
  117. }
  118. public function getStyleName()
  119. {
  120. return $this->styleName;
  121. }
  122. public function addElement(XMLElement $element)
  123. {
  124. $name = $element->name();
  125. $value = $element->value();
  126. switch ($name)
  127. {
  128. case 'NAME':
  129. $this->styleName = $value;
  130. break;
  131. case 'TITLE':
  132. $this->title = $value;
  133. break;
  134. default:
  135. parent::addElement($element);
  136. break;
  137. }
  138. }
  139. }
  140. class WMSDataParser extends DataParser
  141. {
  142. protected $serviceTitle;
  143. protected $serviceName;
  144. protected $serviceAbstract;
  145. protected $boundingLayer;
  146. protected $layers = array();
  147. protected $imageFormats = array();
  148. protected $data;
  149. // images can't exceed these dimensions
  150. protected $maxWidth;
  151. protected $maxHeight;
  152. protected $elementStack = array();
  153. public function getBBoxForProjection($proj) {
  154. return $this->boundingLayer->getBBoxForProjection($proj);
  155. }
  156. public function getLayer($layerName)
  157. {
  158. return $this->layers[$layerName];
  159. }
  160. public function getProjections()
  161. {
  162. return $this->boundingLayer->getProjections();
  163. }
  164. public function getLayerNames()
  165. {
  166. $layerNames = array();
  167. foreach ($this->layers as $name => $layer) {
  168. $layerNames[] = $layer->getLayerName();
  169. }
  170. return $layerNames;
  171. }
  172. protected function startElement($xml_parser, $name, $attribs)
  173. {
  174. $this->data = '';
  175. switch ($name) {
  176. case 'GETMAP':
  177. break;
  178. case 'LAYER':
  179. $this->elementStack[] = new WMSLayer($name, $attribs);
  180. break;
  181. case 'STYLE':
  182. $this->elementStack[] = new WMSStyle($name, $attribs);
  183. break;
  184. default:
  185. $this->elementStack[] = new XMLElement($name, $attribs);
  186. break;
  187. }
  188. }
  189. protected function endElement($xml_parser, $name)
  190. {
  191. if ($element = array_pop($this->elementStack)) {
  192. $element->setValue($this->data, false);
  193. $parent = end($this->elementStack);
  194. if (!$parent) {
  195. $this->root = $element;
  196. } else {
  197. switch ($name) {
  198. case 'NAME':
  199. if ($parent->name() == 'SERVICE') {
  200. $this->serviceName = $element->value();
  201. } else {
  202. $parent->addElement($element);
  203. }
  204. break;
  205. case 'TITLE':
  206. if ($parent->name() == 'SERVICE') {
  207. $this->serviceTitle = $element->value();
  208. } else {
  209. $parent->addElement($element);
  210. }
  211. break;
  212. case 'ABSTRACT':
  213. if ($parent->name() == 'SERVICE') {
  214. $this->serviceAbstract = $element->value();
  215. } else {
  216. $parent->addElement($element);
  217. }
  218. break;
  219. case 'MAXWIDTH':
  220. $this->maxWidth = $element->value();
  221. break;
  222. case 'MAXHEIGHT':
  223. $this->maxHeight = $element->value();
  224. break;
  225. case 'FORMAT':
  226. if ($parent->name() == 'GETMAP') {
  227. $this->imageFormats[] = $element->value();
  228. } else {
  229. $parent->addElement($element);
  230. }
  231. break;
  232. case 'LAYER':
  233. if ($parent->name() == 'LAYER') {
  234. $this->layers[$element->getLayerName()] = $element;
  235. } else {
  236. $this->boundingLayer = $element;
  237. }
  238. default:
  239. $parent->addElement($element);
  240. break;
  241. }
  242. }
  243. }
  244. $this->data = '';
  245. }
  246. protected function characterData($xml_parser, $data)
  247. {
  248. $this->data .= $data;
  249. }
  250. public function parseData($contents) {
  251. $xml_parser = xml_parser_create();
  252. // use case-folding so we are sure to find the tag in $map_array
  253. xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);
  254. xml_parser_set_option($xml_parser, XML_OPTION_SKIP_WHITE, true);
  255. $this->setEncoding(xml_parser_get_option($xml_parser, XML_OPTION_TARGET_ENCODING));
  256. xml_set_element_handler($xml_parser, array($this,"startElement"), array($this,"endElement"));
  257. xml_set_character_data_handler($xml_parser, array($this,"characterData"));
  258. if (!xml_parse($xml_parser, $contents)) {
  259. throw new KurogoDataException(sprintf("XML error: %s at line %d",
  260. xml_error_string(xml_get_error_code($xml_parser)),
  261. xml_get_current_line_number($xml_parser)));
  262. }
  263. xml_parser_free($xml_parser);
  264. }
  265. }