GisMultiLineString.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. <?php
  2. /**
  3. * Handles actions related to GIS MULTILINESTRING objects
  4. */
  5. declare(strict_types=1);
  6. namespace PhpMyAdmin\Gis;
  7. use PhpMyAdmin\Image\ImageWrapper;
  8. use TCPDF;
  9. use function count;
  10. use function explode;
  11. use function hexdec;
  12. use function json_encode;
  13. use function mb_substr;
  14. use function round;
  15. use function trim;
  16. /**
  17. * Handles actions related to GIS MULTILINESTRING objects
  18. */
  19. class GisMultiLineString extends GisGeometry
  20. {
  21. /** @var self */
  22. private static $instance;
  23. /**
  24. * A private constructor; prevents direct creation of object.
  25. */
  26. private function __construct()
  27. {
  28. }
  29. /**
  30. * Returns the singleton.
  31. *
  32. * @return GisMultiLineString the singleton
  33. */
  34. public static function singleton()
  35. {
  36. if (! isset(self::$instance)) {
  37. self::$instance = new GisMultiLineString();
  38. }
  39. return self::$instance;
  40. }
  41. /**
  42. * Scales each row.
  43. *
  44. * @param string $spatial spatial data of a row
  45. *
  46. * @return array an array containing the min, max values for x and y coordinates
  47. * @psalm-return array{minX:float,minY:float,maxX:float,maxY:float}
  48. */
  49. public function scaleRow($spatial)
  50. {
  51. $min_max = GisGeometry::EMPTY_EXTENT;
  52. // Trim to remove leading 'MULTILINESTRING((' and trailing '))'
  53. $multilinestirng = mb_substr($spatial, 17, -2);
  54. // Separate each linestring
  55. $linestirngs = explode('),(', $multilinestirng);
  56. foreach ($linestirngs as $linestring) {
  57. $min_max = $this->setMinMax($linestring, $min_max);
  58. }
  59. return $min_max;
  60. }
  61. /**
  62. * Adds to the PNG image object, the data related to a row in the GIS dataset.
  63. *
  64. * @param string $spatial GIS POLYGON object
  65. * @param string|null $label Label for the GIS POLYGON object
  66. * @param string $line_color Color for the GIS POLYGON object
  67. * @param array $scale_data Array containing data related to scaling
  68. */
  69. public function prepareRowAsPng(
  70. $spatial,
  71. ?string $label,
  72. $line_color,
  73. array $scale_data,
  74. ImageWrapper $image
  75. ): ImageWrapper {
  76. // allocate colors
  77. $black = $image->colorAllocate(0, 0, 0);
  78. $red = (int) hexdec(mb_substr($line_color, 1, 2));
  79. $green = (int) hexdec(mb_substr($line_color, 3, 2));
  80. $blue = (int) hexdec(mb_substr($line_color, 4, 2));
  81. $color = $image->colorAllocate($red, $green, $blue);
  82. $label = trim($label ?? '');
  83. // Trim to remove leading 'MULTILINESTRING((' and trailing '))'
  84. $multilinestirng = mb_substr($spatial, 17, -2);
  85. // Separate each linestring
  86. $linestirngs = explode('),(', $multilinestirng);
  87. $first_line = true;
  88. foreach ($linestirngs as $linestring) {
  89. $points_arr = $this->extractPoints($linestring, $scale_data);
  90. foreach ($points_arr as $point) {
  91. if (isset($temp_point)) {
  92. // draw line section
  93. $image->line(
  94. (int) round($temp_point[0]),
  95. (int) round($temp_point[1]),
  96. (int) round($point[0]),
  97. (int) round($point[1]),
  98. $color
  99. );
  100. }
  101. $temp_point = $point;
  102. }
  103. unset($temp_point);
  104. // print label if applicable
  105. if ($label !== '' && $first_line) {
  106. $image->string(
  107. 1,
  108. (int) round($points_arr[1][0]),
  109. (int) round($points_arr[1][1]),
  110. $label,
  111. $black
  112. );
  113. }
  114. $first_line = false;
  115. }
  116. return $image;
  117. }
  118. /**
  119. * Adds to the TCPDF instance, the data related to a row in the GIS dataset.
  120. *
  121. * @param string $spatial GIS MULTILINESTRING object
  122. * @param string|null $label Label for the GIS MULTILINESTRING object
  123. * @param string $line_color Color for the GIS MULTILINESTRING object
  124. * @param array $scale_data Array containing data related to scaling
  125. * @param TCPDF $pdf TCPDF instance
  126. *
  127. * @return TCPDF the modified TCPDF instance
  128. */
  129. public function prepareRowAsPdf($spatial, ?string $label, $line_color, array $scale_data, $pdf)
  130. {
  131. // allocate colors
  132. $red = hexdec(mb_substr($line_color, 1, 2));
  133. $green = hexdec(mb_substr($line_color, 3, 2));
  134. $blue = hexdec(mb_substr($line_color, 4, 2));
  135. $line = [
  136. 'width' => 1.5,
  137. 'color' => [
  138. $red,
  139. $green,
  140. $blue,
  141. ],
  142. ];
  143. $label = trim($label ?? '');
  144. // Trim to remove leading 'MULTILINESTRING((' and trailing '))'
  145. $multilinestirng = mb_substr($spatial, 17, -2);
  146. // Separate each linestring
  147. $linestirngs = explode('),(', $multilinestirng);
  148. $first_line = true;
  149. foreach ($linestirngs as $linestring) {
  150. $points_arr = $this->extractPoints($linestring, $scale_data);
  151. foreach ($points_arr as $point) {
  152. if (isset($temp_point)) {
  153. // draw line section
  154. $pdf->Line($temp_point[0], $temp_point[1], $point[0], $point[1], $line);
  155. }
  156. $temp_point = $point;
  157. }
  158. unset($temp_point);
  159. // print label
  160. if ($label !== '' && $first_line) {
  161. $pdf->setXY($points_arr[1][0], $points_arr[1][1]);
  162. $pdf->setFontSize(5);
  163. $pdf->Cell(0, 0, $label);
  164. }
  165. $first_line = false;
  166. }
  167. return $pdf;
  168. }
  169. /**
  170. * Prepares and returns the code related to a row in the GIS dataset as SVG.
  171. *
  172. * @param string $spatial GIS MULTILINESTRING object
  173. * @param string $label Label for the GIS MULTILINESTRING object
  174. * @param string $line_color Color for the GIS MULTILINESTRING object
  175. * @param array $scale_data Array containing data related to scaling
  176. *
  177. * @return string the code related to a row in the GIS dataset
  178. */
  179. public function prepareRowAsSvg($spatial, $label, $line_color, array $scale_data)
  180. {
  181. $line_options = [
  182. 'data-label' => $label,
  183. 'class' => 'linestring vector',
  184. 'fill' => 'none',
  185. 'stroke' => $line_color,
  186. 'stroke-width' => 2,
  187. ];
  188. // Trim to remove leading 'MULTILINESTRING((' and trailing '))'
  189. $multilinestirng = mb_substr($spatial, 17, -2);
  190. // Separate each linestring
  191. $linestirngs = explode('),(', $multilinestirng);
  192. $row = '';
  193. foreach ($linestirngs as $linestring) {
  194. $points_arr = $this->extractPoints($linestring, $scale_data);
  195. $row .= '<polyline points="';
  196. foreach ($points_arr as $point) {
  197. $row .= $point[0] . ',' . $point[1] . ' ';
  198. }
  199. $row .= '"';
  200. $line_options['id'] = $label . $this->getRandomId();
  201. foreach ($line_options as $option => $val) {
  202. $row .= ' ' . $option . '="' . trim((string) $val) . '"';
  203. }
  204. $row .= '/>';
  205. }
  206. return $row;
  207. }
  208. /**
  209. * Prepares JavaScript related to a row in the GIS dataset
  210. * to visualize it with OpenLayers.
  211. *
  212. * @param string $spatial GIS MULTILINESTRING object
  213. * @param int $srid Spatial reference ID
  214. * @param string $label Label for the GIS MULTILINESTRING object
  215. * @param array $line_color Color for the GIS MULTILINESTRING object
  216. * @param array $scale_data Array containing data related to scaling
  217. *
  218. * @return string JavaScript related to a row in the GIS dataset
  219. */
  220. public function prepareRowAsOl($spatial, int $srid, $label, $line_color, array $scale_data)
  221. {
  222. $stroke_style = [
  223. 'color' => $line_color,
  224. 'width' => 2,
  225. ];
  226. $row = 'var style = new ol.style.Style({'
  227. . 'stroke: new ol.style.Stroke(' . json_encode($stroke_style) . ')';
  228. if (trim($label) !== '') {
  229. $text_style = ['text' => trim($label)];
  230. $row .= ', text: new ol.style.Text(' . json_encode($text_style) . ')';
  231. }
  232. $row .= '});';
  233. if ($srid === 0) {
  234. $srid = 4326;
  235. }
  236. $row .= $this->getBoundsForOl($srid, $scale_data);
  237. // Trim to remove leading 'MULTILINESTRING((' and trailing '))'
  238. $multilinestirng = mb_substr($spatial, 17, -2);
  239. // Separate each linestring
  240. $linestirngs = explode('),(', $multilinestirng);
  241. return $row . $this->getLineArrayForOpenLayers($linestirngs, $srid)
  242. . 'var multiLineString = new ol.geom.MultiLineString(arr);'
  243. . 'var feature = new ol.Feature({geometry: multiLineString});'
  244. . 'feature.setStyle(style);'
  245. . 'vectorLayer.addFeature(feature);';
  246. }
  247. /**
  248. * Generate the WKT with the set of parameters passed by the GIS editor.
  249. *
  250. * @param array $gis_data GIS data
  251. * @param int $index Index into the parameter object
  252. * @param string|null $empty Value for empty points
  253. *
  254. * @return string WKT with the set of parameters passed by the GIS editor
  255. */
  256. public function generateWkt(array $gis_data, $index, $empty = '')
  257. {
  258. $data_row = $gis_data[$index]['MULTILINESTRING'] ?? null;
  259. $no_of_lines = $data_row['no_of_lines'] ?? 1;
  260. if ($no_of_lines < 1) {
  261. $no_of_lines = 1;
  262. }
  263. $wkt = 'MULTILINESTRING(';
  264. for ($i = 0; $i < $no_of_lines; $i++) {
  265. $no_of_points = $data_row[$i]['no_of_points'] ?? 2;
  266. if ($no_of_points < 2) {
  267. $no_of_points = 2;
  268. }
  269. $wkt .= '(';
  270. for ($j = 0; $j < $no_of_points; $j++) {
  271. $wkt .= (isset($data_row[$i][$j]['x'])
  272. && trim((string) $data_row[$i][$j]['x']) != ''
  273. ? $data_row[$i][$j]['x'] : $empty)
  274. . ' ' . (isset($data_row[$i][$j]['y'])
  275. && trim((string) $data_row[$i][$j]['y']) != ''
  276. ? $data_row[$i][$j]['y'] : $empty) . ',';
  277. }
  278. $wkt = mb_substr($wkt, 0, -1);
  279. $wkt .= '),';
  280. }
  281. $wkt = mb_substr($wkt, 0, -1);
  282. return $wkt . ')';
  283. }
  284. /**
  285. * Generate the WKT for the data from ESRI shape files.
  286. *
  287. * @param array $row_data GIS data
  288. *
  289. * @return string the WKT for the data from ESRI shape files
  290. */
  291. public function getShape(array $row_data)
  292. {
  293. $wkt = 'MULTILINESTRING(';
  294. for ($i = 0; $i < $row_data['numparts']; $i++) {
  295. $wkt .= '(';
  296. foreach ($row_data['parts'][$i]['points'] as $point) {
  297. $wkt .= $point['x'] . ' ' . $point['y'] . ',';
  298. }
  299. $wkt = mb_substr($wkt, 0, -1);
  300. $wkt .= '),';
  301. }
  302. $wkt = mb_substr($wkt, 0, -1);
  303. return $wkt . ')';
  304. }
  305. /**
  306. * Generate parameters for the GIS data editor from the value of the GIS column.
  307. *
  308. * @param string $value Value of the GIS column
  309. * @param int $index Index of the geometry
  310. *
  311. * @return array params for the GIS data editor from the value of the GIS column
  312. */
  313. public function generateParams($value, $index = -1)
  314. {
  315. $params = [];
  316. if ($index == -1) {
  317. $index = 0;
  318. $data = GisGeometry::generateParams($value);
  319. $params['srid'] = $data['srid'];
  320. $wkt = $data['wkt'];
  321. } else {
  322. $params[$index]['gis_type'] = 'MULTILINESTRING';
  323. $wkt = $value;
  324. }
  325. // Trim to remove leading 'MULTILINESTRING((' and trailing '))'
  326. $multilinestirng = mb_substr($wkt, 17, -2);
  327. // Separate each linestring
  328. $linestirngs = explode('),(', $multilinestirng);
  329. $params[$index]['MULTILINESTRING']['no_of_lines'] = count($linestirngs);
  330. $j = 0;
  331. foreach ($linestirngs as $linestring) {
  332. $points_arr = $this->extractPoints($linestring, null);
  333. $no_of_points = count($points_arr);
  334. $params[$index]['MULTILINESTRING'][$j]['no_of_points'] = $no_of_points;
  335. for ($i = 0; $i < $no_of_points; $i++) {
  336. $params[$index]['MULTILINESTRING'][$j][$i]['x'] = $points_arr[$i][0];
  337. $params[$index]['MULTILINESTRING'][$j][$i]['y'] = $points_arr[$i][1];
  338. }
  339. $j++;
  340. }
  341. return $params;
  342. }
  343. }