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