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