PageRenderTime 41ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/classes/Gis/GisGeometryCollection.php

http://github.com/phpmyadmin/phpmyadmin
PHP | 385 lines | 204 code | 61 blank | 120 comment | 28 complexity | 7e9bc2b62ac3708659b1a5069c4d23e0 MD5 | raw file
Possible License(s): GPL-2.0, MIT, LGPL-3.0
  1. <?php
  2. /**
  3. * Handles actions related to GIS GEOMETRYCOLLECTION objects
  4. */
  5. declare(strict_types=1);
  6. namespace PhpMyAdmin\Gis;
  7. use PhpMyAdmin\Image\ImageWrapper;
  8. use TCPDF;
  9. use function array_merge;
  10. use function count;
  11. use function mb_strpos;
  12. use function mb_substr;
  13. use function str_split;
  14. /**
  15. * Handles actions related to GIS GEOMETRYCOLLECTION objects
  16. */
  17. class GisGeometryCollection extends GisGeometry
  18. {
  19. /** @var self */
  20. private static $instance;
  21. /**
  22. * A private constructor; prevents direct creation of object.
  23. *
  24. * @access private
  25. */
  26. private function __construct()
  27. {
  28. }
  29. /**
  30. * Returns the singleton.
  31. *
  32. * @return GisGeometryCollection the singleton
  33. *
  34. * @access public
  35. */
  36. public static function singleton()
  37. {
  38. if (! isset(self::$instance)) {
  39. self::$instance = new GisGeometryCollection();
  40. }
  41. return self::$instance;
  42. }
  43. /**
  44. * Scales each row.
  45. *
  46. * @param string $spatial spatial data of a row
  47. *
  48. * @return array array containing the min, max values for x and y coordinates
  49. *
  50. * @access public
  51. */
  52. public function scaleRow($spatial)
  53. {
  54. $min_max = [];
  55. // Trim to remove leading 'GEOMETRYCOLLECTION(' and trailing ')'
  56. $goem_col = mb_substr($spatial, 19, -1);
  57. // Split the geometry collection object to get its constituents.
  58. $sub_parts = $this->explodeGeomCol($goem_col);
  59. foreach ($sub_parts as $sub_part) {
  60. $type_pos = mb_strpos($sub_part, '(');
  61. if ($type_pos === false) {
  62. continue;
  63. }
  64. $type = mb_substr($sub_part, 0, $type_pos);
  65. $gis_obj = GisFactory::factory($type);
  66. if (! $gis_obj) {
  67. continue;
  68. }
  69. $scale_data = $gis_obj->scaleRow($sub_part);
  70. // Update minimum/maximum values for x and y coordinates.
  71. $c_maxX = (float) $scale_data['maxX'];
  72. if (! isset($min_max['maxX']) || $c_maxX > $min_max['maxX']) {
  73. $min_max['maxX'] = $c_maxX;
  74. }
  75. $c_minX = (float) $scale_data['minX'];
  76. if (! isset($min_max['minX']) || $c_minX < $min_max['minX']) {
  77. $min_max['minX'] = $c_minX;
  78. }
  79. $c_maxY = (float) $scale_data['maxY'];
  80. if (! isset($min_max['maxY']) || $c_maxY > $min_max['maxY']) {
  81. $min_max['maxY'] = $c_maxY;
  82. }
  83. $c_minY = (float) $scale_data['minY'];
  84. if (isset($min_max['minY']) && $c_minY >= $min_max['minY']) {
  85. continue;
  86. }
  87. $min_max['minY'] = $c_minY;
  88. }
  89. return $min_max;
  90. }
  91. /**
  92. * Adds to the PNG image object, the data related to a row in the GIS dataset.
  93. *
  94. * @param string $spatial GIS POLYGON object
  95. * @param string|null $label Label for the GIS POLYGON object
  96. * @param string $color Color for the GIS POLYGON object
  97. * @param array $scale_data Array containing data related to scaling
  98. */
  99. public function prepareRowAsPng(
  100. $spatial,
  101. ?string $label,
  102. $color,
  103. array $scale_data,
  104. ImageWrapper $image
  105. ): ImageWrapper {
  106. // Trim to remove leading 'GEOMETRYCOLLECTION(' and trailing ')'
  107. $goem_col = mb_substr($spatial, 19, -1);
  108. // Split the geometry collection object to get its constituents.
  109. $sub_parts = $this->explodeGeomCol($goem_col);
  110. foreach ($sub_parts as $sub_part) {
  111. $type_pos = mb_strpos($sub_part, '(');
  112. if ($type_pos === false) {
  113. continue;
  114. }
  115. $type = mb_substr($sub_part, 0, $type_pos);
  116. $gis_obj = GisFactory::factory($type);
  117. if (! $gis_obj) {
  118. continue;
  119. }
  120. $image = $gis_obj->prepareRowAsPng($sub_part, $label, $color, $scale_data, $image);
  121. }
  122. return $image;
  123. }
  124. /**
  125. * Adds to the TCPDF instance, the data related to a row in the GIS dataset.
  126. *
  127. * @param string $spatial GIS GEOMETRYCOLLECTION object
  128. * @param string|null $label label for the GIS GEOMETRYCOLLECTION object
  129. * @param string $color color for the GIS GEOMETRYCOLLECTION object
  130. * @param array $scale_data array containing data related to scaling
  131. * @param TCPDF $pdf TCPDF instance
  132. *
  133. * @return TCPDF the modified TCPDF instance
  134. *
  135. * @access public
  136. */
  137. public function prepareRowAsPdf($spatial, ?string $label, $color, array $scale_data, $pdf)
  138. {
  139. // Trim to remove leading 'GEOMETRYCOLLECTION(' and trailing ')'
  140. $goem_col = mb_substr($spatial, 19, -1);
  141. // Split the geometry collection object to get its constituents.
  142. $sub_parts = $this->explodeGeomCol($goem_col);
  143. foreach ($sub_parts as $sub_part) {
  144. $type_pos = mb_strpos($sub_part, '(');
  145. if ($type_pos === false) {
  146. continue;
  147. }
  148. $type = mb_substr($sub_part, 0, $type_pos);
  149. $gis_obj = GisFactory::factory($type);
  150. if (! $gis_obj) {
  151. continue;
  152. }
  153. $pdf = $gis_obj->prepareRowAsPdf($sub_part, $label, $color, $scale_data, $pdf);
  154. }
  155. return $pdf;
  156. }
  157. /**
  158. * Prepares and returns the code related to a row in the GIS dataset as SVG.
  159. *
  160. * @param string $spatial GIS GEOMETRYCOLLECTION object
  161. * @param string $label label for the GIS GEOMETRYCOLLECTION object
  162. * @param string $color color for the GIS GEOMETRYCOLLECTION object
  163. * @param array $scale_data array containing data related to scaling
  164. *
  165. * @return string the code related to a row in the GIS dataset
  166. *
  167. * @access public
  168. */
  169. public function prepareRowAsSvg($spatial, $label, $color, array $scale_data)
  170. {
  171. $row = '';
  172. // Trim to remove leading 'GEOMETRYCOLLECTION(' and trailing ')'
  173. $goem_col = mb_substr($spatial, 19, -1);
  174. // Split the geometry collection object to get its constituents.
  175. $sub_parts = $this->explodeGeomCol($goem_col);
  176. foreach ($sub_parts as $sub_part) {
  177. $type_pos = mb_strpos($sub_part, '(');
  178. if ($type_pos === false) {
  179. continue;
  180. }
  181. $type = mb_substr($sub_part, 0, $type_pos);
  182. $gis_obj = GisFactory::factory($type);
  183. if (! $gis_obj) {
  184. continue;
  185. }
  186. $row .= $gis_obj->prepareRowAsSvg($sub_part, $label, $color, $scale_data);
  187. }
  188. return $row;
  189. }
  190. /**
  191. * Prepares JavaScript related to a row in the GIS dataset
  192. * to visualize it with OpenLayers.
  193. *
  194. * @param string $spatial GIS GEOMETRYCOLLECTION object
  195. * @param int $srid spatial reference ID
  196. * @param string $label label for the GIS GEOMETRYCOLLECTION object
  197. * @param array $color color for the GIS GEOMETRYCOLLECTION object
  198. * @param array $scale_data array containing data related to scaling
  199. *
  200. * @return string JavaScript related to a row in the GIS dataset
  201. *
  202. * @access public
  203. */
  204. public function prepareRowAsOl($spatial, int $srid, $label, $color, array $scale_data)
  205. {
  206. $row = '';
  207. // Trim to remove leading 'GEOMETRYCOLLECTION(' and trailing ')'
  208. $goem_col = mb_substr($spatial, 19, -1);
  209. // Split the geometry collection object to get its constituents.
  210. $sub_parts = $this->explodeGeomCol($goem_col);
  211. foreach ($sub_parts as $sub_part) {
  212. $type_pos = mb_strpos($sub_part, '(');
  213. if ($type_pos === false) {
  214. continue;
  215. }
  216. $type = mb_substr($sub_part, 0, $type_pos);
  217. $gis_obj = GisFactory::factory($type);
  218. if (! $gis_obj) {
  219. continue;
  220. }
  221. $row .= $gis_obj->prepareRowAsOl($sub_part, $srid, $label, $color, $scale_data);
  222. }
  223. return $row;
  224. }
  225. /**
  226. * Splits the GEOMETRYCOLLECTION object and get its constituents.
  227. *
  228. * @param string $geom_col geometry collection string
  229. *
  230. * @return array the constituents of the geometry collection object
  231. *
  232. * @access private
  233. */
  234. private function explodeGeomCol($geom_col)
  235. {
  236. $sub_parts = [];
  237. $br_count = 0;
  238. $start = 0;
  239. $count = 0;
  240. foreach (str_split($geom_col) as $char) {
  241. if ($char === '(') {
  242. $br_count++;
  243. } elseif ($char === ')') {
  244. $br_count--;
  245. if ($br_count == 0) {
  246. $sub_parts[] = mb_substr($geom_col, $start, $count + 1 - $start);
  247. $start = $count + 2;
  248. }
  249. }
  250. $count++;
  251. }
  252. return $sub_parts;
  253. }
  254. /**
  255. * Generates the WKT with the set of parameters passed by the GIS editor.
  256. *
  257. * @param array $gis_data GIS data
  258. * @param int $index index into the parameter object
  259. * @param string|null $empty value for empty points
  260. *
  261. * @return string WKT with the set of parameters passed by the GIS editor
  262. *
  263. * @access public
  264. */
  265. public function generateWkt(array $gis_data, $index, $empty = '')
  266. {
  267. $geom_count = $gis_data['GEOMETRYCOLLECTION']['geom_count'] ?? 1;
  268. $wkt = 'GEOMETRYCOLLECTION(';
  269. for ($i = 0; $i < $geom_count; $i++) {
  270. if (! isset($gis_data[$i]['gis_type'])) {
  271. continue;
  272. }
  273. $type = $gis_data[$i]['gis_type'];
  274. $gis_obj = GisFactory::factory($type);
  275. if (! $gis_obj) {
  276. continue;
  277. }
  278. $wkt .= $gis_obj->generateWkt($gis_data, $i, $empty) . ',';
  279. }
  280. if (isset($gis_data[0]['gis_type'])) {
  281. $wkt = mb_substr($wkt, 0, -1);
  282. }
  283. return $wkt . ')';
  284. }
  285. /**
  286. * Generates parameters for the GIS data editor from the value of the GIS column.
  287. *
  288. * @param string $value of the GIS column
  289. *
  290. * @return array parameters for the GIS editor from the value of the GIS column
  291. *
  292. * @access public
  293. */
  294. public function generateParams($value)
  295. {
  296. $params = [];
  297. $data = GisGeometry::generateParams($value);
  298. $params['srid'] = $data['srid'];
  299. $wkt = $data['wkt'];
  300. // Trim to remove leading 'GEOMETRYCOLLECTION(' and trailing ')'
  301. $goem_col = mb_substr($wkt, 19, -1);
  302. // Split the geometry collection object to get its constituents.
  303. $sub_parts = $this->explodeGeomCol($goem_col);
  304. $params['GEOMETRYCOLLECTION']['geom_count'] = count($sub_parts);
  305. $i = 0;
  306. foreach ($sub_parts as $sub_part) {
  307. $type_pos = mb_strpos($sub_part, '(');
  308. if ($type_pos === false) {
  309. continue;
  310. }
  311. $type = mb_substr($sub_part, 0, $type_pos);
  312. /**
  313. * @var GisMultiPolygon|GisPolygon|GisMultiPoint|GisPoint|GisMultiLineString|GisLineString $gis_obj
  314. */
  315. $gis_obj = GisFactory::factory($type);
  316. if (! $gis_obj) {
  317. continue;
  318. }
  319. $params = array_merge($params, $gis_obj->generateParams($sub_part, $i));
  320. $i++;
  321. }
  322. return $params;
  323. }
  324. }