1. Check existence of mb_string, mysql and xml extensions before installation.
[openemr.git] / phpmyadmin / libraries / gis / GIS_Visualization.class.php
blob393018a021eabc2c288657efd92b53333c4cfea7
1 <?php
2 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 /**
4 * Handles visualization of GIS data
6 * @package PhpMyAdmin-GIS
7 */
9 if (! defined('PHPMYADMIN')) {
10 exit;
13 require_once 'libraries/sql.lib.php';
15 /**
16 * Handles visualization of GIS data
18 * @package PhpMyAdmin-GIS
20 class PMA_GIS_Visualization
22 /**
23 * @var array Raw data for the visualization
25 private $_data;
27 private $_modified_sql;
29 /**
30 * @var array Set of default settings values are here.
32 private $_settings = array(
34 // Array of colors to be used for GIS visualizations.
35 'colors' => array(
36 '#B02EE0',
37 '#E0642E',
38 '#E0D62E',
39 '#2E97E0',
40 '#BCE02E',
41 '#E02E75',
42 '#5CE02E',
43 '#E0B02E',
44 '#0022E0',
45 '#726CB1',
46 '#481A36',
47 '#BAC658',
48 '#127224',
49 '#825119',
50 '#238C74',
51 '#4C489B',
52 '#87C9BF',
55 // The width of the GIS visualization.
56 'width' => 600,
58 // The height of the GIS visualization.
59 'height' => 450,
62 /**
63 * @var array Options that the user has specified.
65 private $_userSpecifiedSettings = null;
67 /**
68 * Returns the settings array
70 * @return array the settings array
71 * @access public
73 public function getSettings()
75 return $this->_settings;
78 /**
79 * Factory
81 * @param string $sql_query SQL to fetch raw data for visualization
82 * @param array $options Users specified options
83 * @param integer $row number of rows
84 * @param integer $pos start position
86 * @return PMA_GIS_Visualization
88 * @access public
90 public static function get($sql_query, $options, $row, $pos)
92 return new PMA_GIS_Visualization($sql_query, $options, $row, $pos);
95 /**
96 * Get visualization
98 * @param array $data Raw data, if set, parameters other than $options will be
99 * ignored
100 * @param array $options Users specified options
102 * @return PMA_GIS_Visualization
104 public static function getByData($data, $options)
106 return new PMA_GIS_Visualization(null, $options, null, null, $data);
110 * Check if data has SRID
112 * @return bool
114 public function hasSrid()
116 foreach ($this->_data as $row) {
117 if ($row['srid'] != 0) {
118 return true;
121 return false;
125 * Constructor. Stores user specified options.
127 * @param string $sql_query SQL to fetch raw data for visualization
128 * @param array $options Users specified options
129 * @param integer $row number of rows
130 * @param integer $pos start position
131 * @param array $data raw data. If set, parameters other than $options
132 * will be ignored
134 * @access public
136 private function __construct($sql_query, $options, $row, $pos, $data = null)
138 $this->_userSpecifiedSettings = $options;
139 if (isset($data)) {
140 $this->_data = $data;
141 } else {
142 $this->_modified_sql = $this->_modifySqlQuery($sql_query, $row, $pos);
143 $this->_data = $this->_fetchRawData();
149 * All the variable initialization, options handling has to be done here.
151 * @return void
152 * @access protected
154 protected function init()
156 $this->_handleOptions();
160 * Returns sql for fetching raw data
162 * @param string $sql_query The SQL to modify.
163 * @param integer $rows Number of rows.
164 * @param integer $pos Start position.
166 * @return string the modified sql query.
168 private function _modifySqlQuery($sql_query, $rows, $pos)
170 $modified_query = 'SELECT ';
171 // If label column is chosen add it to the query
172 if (! empty($this->_userSpecifiedSettings['labelColumn'])) {
173 $modified_query .= PMA_Util::backquote(
174 $this->_userSpecifiedSettings['labelColumn']
176 . ', ';
178 // Wrap the spatial column with 'ASTEXT()' function and add it
179 $modified_query .= 'ASTEXT('
180 . PMA_Util::backquote($this->_userSpecifiedSettings['spatialColumn'])
181 . ') AS ' . PMA_Util::backquote(
182 $this->_userSpecifiedSettings['spatialColumn']
184 . ', ';
186 // Get the SRID
187 $modified_query .= 'SRID('
188 . PMA_Util::backquote($this->_userSpecifiedSettings['spatialColumn'])
189 . ') AS ' . PMA_Util::backquote('srid') . ' ';
191 // Append the original query as the inner query
192 $modified_query .= 'FROM (' . $sql_query . ') AS '
193 . PMA_Util::backquote('temp_gis');
195 // LIMIT clause
196 if (is_numeric($rows) && $rows > 0) {
197 $modified_query .= ' LIMIT ';
198 if (is_numeric($pos) && $pos >= 0) {
199 $modified_query .= $pos . ', ' . $rows;
200 } else {
201 $modified_query .= $rows;
205 return $modified_query;
209 * Returns raw data for GIS visualization.
211 * @return string the raw data.
213 private function _fetchRawData()
215 $modified_result = $GLOBALS['dbi']->tryQuery($this->_modified_sql);
217 $data = array();
218 while ($row = $GLOBALS['dbi']->fetchAssoc($modified_result)) {
219 $data[] = $row;
222 return $data;
226 * A function which handles passed parameters. Useful if desired
227 * chart needs to be a little bit different from the default one.
229 * @return void
230 * @access private
232 private function _handleOptions()
234 if (! is_null($this->_userSpecifiedSettings)) {
235 $this->_settings = array_merge(
236 $this->_settings,
237 $this->_userSpecifiedSettings
243 * Sanitizes the file name.
245 * @param string $file_name file name
246 * @param string $ext extension of the file
248 * @return string the sanitized file name
249 * @access private
251 private function _sanitizeName($file_name, $ext)
253 $file_name = PMA_sanitizeFilename($file_name);
255 // Check if the user already added extension;
256 // get the substring where the extension would be if it was included
257 $extension_start_pos = /*overload*/mb_strlen($file_name)
258 - /*overload*/mb_strlen($ext) - 1;
259 $user_extension = /*overload*/mb_substr(
260 $file_name, $extension_start_pos, /*overload*/mb_strlen($file_name)
262 $required_extension = "." . $ext;
263 if (/*overload*/mb_strtolower($user_extension) != $required_extension) {
264 $file_name .= $required_extension;
266 return $file_name;
270 * Handles common tasks of writing the visualization to file for various formats.
272 * @param string $file_name file name
273 * @param string $type mime type
274 * @param string $ext extension of the file
276 * @return void
277 * @access private
279 private function _toFile($file_name, $type, $ext)
281 $file_name = $this->_sanitizeName($file_name, $ext);
282 PMA_downloadHeader($file_name, $type);
286 * Generate the visualization in SVG format.
288 * @return string the generated image resource
289 * @access private
291 private function _svg()
293 $this->init();
295 $output = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' . "\n";
296 $output .= '<svg version="1.1" xmlns:svg="http://www.w3.org/2000/svg"'
297 . ' xmlns="http://www.w3.org/2000/svg"'
298 . ' width="' . $this->_settings['width'] . '"'
299 . ' height="' . $this->_settings['height'] . '">';
300 $output .= '<g id="groupPanel">';
302 $scale_data = $this->_scaleDataSet($this->_data);
303 $output .= $this->_prepareDataSet($this->_data, $scale_data, 'svg', '');
305 $output .= '</g>';
306 $output .= '</svg>';
308 return $output;
312 * Get the visualization as a SVG.
314 * @return string the visualization as a SVG
315 * @access public
317 public function asSVG()
319 $output = $this->_svg();
320 return $output;
324 * Saves as a SVG image to a file.
326 * @param string $file_name File name
328 * @return void
329 * @access public
331 public function toFileAsSvg($file_name)
333 $img = $this->_svg();
334 $this->_toFile($file_name, 'image/svg+xml', 'svg');
335 echo($img);
339 * Generate the visualization in PNG format.
341 * @return resource the generated image resource
342 * @access private
344 private function _png()
346 $this->init();
348 // create image
349 $image = imagecreatetruecolor(
350 $this->_settings['width'],
351 $this->_settings['height']
354 // fill the background
355 $bg = imagecolorallocate($image, 229, 229, 229);
356 imagefilledrectangle(
357 $image, 0, 0, $this->_settings['width'] - 1,
358 $this->_settings['height'] - 1, $bg
361 $scale_data = $this->_scaleDataSet($this->_data);
362 $image = $this->_prepareDataSet($this->_data, $scale_data, 'png', $image);
364 return $image;
368 * Get the visualization as a PNG.
370 * @return string the visualization as a PNG
371 * @access public
373 public function asPng()
375 $img = $this->_png();
377 // render and save it to variable
378 ob_start();
379 imagepng($img, null, 9, PNG_ALL_FILTERS);
380 imagedestroy($img);
381 $output = ob_get_contents();
382 ob_end_clean();
384 // base64 encode
385 $encoded = base64_encode($output);
386 return '<img src="data:image/png;base64,' . $encoded . '" />';
390 * Saves as a PNG image to a file.
392 * @param string $file_name File name
394 * @return void
395 * @access public
397 public function toFileAsPng($file_name)
399 $img = $this->_png();
400 $this->_toFile($file_name, 'image/png', 'png');
401 imagepng($img, null, 9, PNG_ALL_FILTERS);
402 imagedestroy($img);
406 * Get the code for visualization with OpenLayers.
408 * @return string the code for visualization with OpenLayers
409 * @access public
411 public function asOl()
413 $this->init();
414 $scale_data = $this->_scaleDataSet($this->_data);
415 $output
416 = 'var options = {'
417 . 'projection: new OpenLayers.Projection("EPSG:900913"),'
418 . 'displayProjection: new OpenLayers.Projection("EPSG:4326"),'
419 . 'units: "m",'
420 . 'numZoomLevels: 18,'
421 . 'maxResolution: 156543.0339,'
422 . 'maxExtent: new OpenLayers.Bounds('
423 . '-20037508, -20037508, 20037508, 20037508),'
424 . 'restrictedExtent: new OpenLayers.Bounds('
425 . '-20037508, -20037508, 20037508, 20037508)'
426 . '};'
427 . 'var map = new OpenLayers.Map("openlayersmap", options);'
428 . 'var layerNone = new OpenLayers.Layer.Boxes('
429 . '"None", {isBaseLayer: true});'
430 . 'var layerMapnik = new OpenLayers.Layer.OSM.Mapnik("Mapnik");'
431 . 'var layerCycleMap = new OpenLayers.Layer.OSM.CycleMap("CycleMap");'
432 . 'map.addLayers([layerMapnik,layerCycleMap,layerNone]);'
433 . 'var vectorLayer = new OpenLayers.Layer.Vector("Data");'
434 . 'var bound;';
435 $output .= $this->_prepareDataSet($this->_data, $scale_data, 'ol', '');
436 $output .=
437 'map.addLayer(vectorLayer);'
438 . 'map.zoomToExtent(bound);'
439 . 'if (map.getZoom() < 2) {'
440 . 'map.zoomTo(2);'
441 . '}'
442 . 'map.addControl(new OpenLayers.Control.LayerSwitcher());'
443 . 'map.addControl(new OpenLayers.Control.MousePosition());';
444 return $output;
448 * Saves as a PDF to a file.
450 * @param string $file_name File name
452 * @return void
453 * @access public
455 public function toFileAsPdf($file_name)
457 $this->init();
459 include_once './libraries/tcpdf/tcpdf.php';
461 // create pdf
462 $pdf = new TCPDF(
463 '', 'pt', $GLOBALS['cfg']['PDFDefaultPageSize'], true, 'UTF-8', false
466 // disable header and footer
467 $pdf->setPrintHeader(false);
468 $pdf->setPrintFooter(false);
470 //set auto page breaks
471 $pdf->SetAutoPageBreak(false);
473 // add a page
474 $pdf->AddPage();
476 $scale_data = $this->_scaleDataSet($this->_data);
477 $pdf = $this->_prepareDataSet($this->_data, $scale_data, 'pdf', $pdf);
479 // sanitize file name
480 $file_name = $this->_sanitizeName($file_name, 'pdf');
481 $pdf->Output($file_name, 'D');
485 * Convert file to image
487 * @param string $format Output format
489 * @return string File
491 public function toImage($format)
493 if ($format == 'svg') {
494 return $this->asSvg();
495 } elseif ($format == 'png') {
496 return $this->asPng();
497 } elseif ($format == 'ol') {
498 return $this->asOl();
503 * Convert file to given format
505 * @param string $filename Filename
506 * @param string $format Output format
508 * @return void
510 public function toFile($filename, $format)
512 if ($format == 'svg') {
513 $this->toFileAsSvg($filename);
514 } elseif ($format == 'png') {
515 $this->toFileAsPng($filename);
516 } elseif ($format == 'pdf') {
517 $this->toFileAsPdf($filename);
522 * Calculates the scale, horizontal and vertical offset that should be used.
524 * @param array $data Row data
526 * @return array an array containing the scale, x and y offsets
527 * @access private
529 private function _scaleDataSet($data)
531 $min_max = array();
532 $border = 15;
533 // effective width and height of the plot
534 $plot_width = $this->_settings['width'] - 2 * $border;
535 $plot_height = $this->_settings['height'] - 2 * $border;
537 foreach ($data as $row) {
539 // Figure out the data type
540 $ref_data = $row[$this->_settings['spatialColumn']];
541 $type_pos = /*overload*/mb_stripos($ref_data, '(');
542 $type = /*overload*/mb_substr($ref_data, 0, $type_pos);
544 $gis_obj = PMA_GIS_Factory::factory($type);
545 if (! $gis_obj) {
546 continue;
548 $scale_data = $gis_obj->scaleRow(
549 $row[$this->_settings['spatialColumn']]
552 // Update minimum/maximum values for x and y coordinates.
553 $c_maxX = (float) $scale_data['maxX'];
554 if (! isset($min_max['maxX']) || $c_maxX > $min_max['maxX']) {
555 $min_max['maxX'] = $c_maxX;
558 $c_minX = (float) $scale_data['minX'];
559 if (! isset($min_max['minX']) || $c_minX < $min_max['minX']) {
560 $min_max['minX'] = $c_minX;
563 $c_maxY = (float) $scale_data['maxY'];
564 if (! isset($min_max['maxY']) || $c_maxY > $min_max['maxY']) {
565 $min_max['maxY'] = $c_maxY;
568 $c_minY = (float) $scale_data['minY'];
569 if (! isset($min_max['minY']) || $c_minY < $min_max['minY']) {
570 $min_max['minY'] = $c_minY;
574 // scale the visualization
575 $x_ratio = ($min_max['maxX'] - $min_max['minX']) / $plot_width;
576 $y_ratio = ($min_max['maxY'] - $min_max['minY']) / $plot_height;
577 $ratio = ($x_ratio > $y_ratio) ? $x_ratio : $y_ratio;
579 $scale = ($ratio != 0) ? (1 / $ratio) : 1;
581 if ($x_ratio < $y_ratio) {
582 // center horizontally
583 $x = ($min_max['maxX'] + $min_max['minX'] - $plot_width / $scale) / 2;
584 // fit vertically
585 $y = $min_max['minY'] - ($border / $scale);
586 } else {
587 // fit horizontally
588 $x = $min_max['minX'] - ($border / $scale);
589 // center vertically
590 $y =($min_max['maxY'] + $min_max['minY'] - $plot_height / $scale) / 2;
593 return array(
594 'scale' => $scale,
595 'x' => $x,
596 'y' => $y,
597 'minX' => $min_max['minX'],
598 'maxX' => $min_max['maxX'],
599 'minY' => $min_max['minY'],
600 'maxY' => $min_max['maxY'],
601 'height' => $this->_settings['height'],
606 * Prepares and return the dataset as needed by the visualization.
608 * @param array $data Raw data
609 * @param array $scale_data Data related to scaling
610 * @param string $format Format of the visualization
611 * @param object $results Image object in the case of png
612 * TCPDF object in the case of pdf
614 * @return mixed the formatted array of data
615 * @access private
617 private function _prepareDataSet($data, $scale_data, $format, $results)
619 $color_number = 0;
621 // loop through the rows
622 foreach ($data as $row) {
623 $index = $color_number % sizeof($this->_settings['colors']);
625 // Figure out the data type
626 $ref_data = $row[$this->_settings['spatialColumn']];
627 $type_pos = /*overload*/mb_stripos($ref_data, '(');
628 $type = /*overload*/mb_substr($ref_data, 0, $type_pos);
630 $gis_obj = PMA_GIS_Factory::factory($type);
631 if (! $gis_obj) {
632 continue;
634 $label = '';
635 if (isset($this->_settings['labelColumn'])
636 && isset($row[$this->_settings['labelColumn']])
638 $label = $row[$this->_settings['labelColumn']];
641 if ($format == 'svg') {
642 $results .= $gis_obj->prepareRowAsSvg(
643 $row[$this->_settings['spatialColumn']], $label,
644 $this->_settings['colors'][$index], $scale_data
646 } elseif ($format == 'png') {
647 $results = $gis_obj->prepareRowAsPng(
648 $row[$this->_settings['spatialColumn']], $label,
649 $this->_settings['colors'][$index], $scale_data, $results
651 } elseif ($format == 'pdf') {
652 $results = $gis_obj->prepareRowAsPdf(
653 $row[$this->_settings['spatialColumn']], $label,
654 $this->_settings['colors'][$index], $scale_data, $results
656 } elseif ($format == 'ol') {
657 $results .= $gis_obj->prepareRowAsOl(
658 $row[$this->_settings['spatialColumn']], $row['srid'],
659 $label, $this->_settings['colors'][$index], $scale_data
662 $color_number++;
664 return $results;
668 * Set user specified settings
670 * @param array $userSpecifiedSettings User specified settings
672 * @return void
674 public function setUserSpecifiedSettings($userSpecifiedSettings)
676 $this->_userSpecifiedSettings = $userSpecifiedSettings;