Fixed: Not selecting a datalabel used to issue a notice(undefined offset)
[phpmyadmin/ammaryasirr.git] / libraries / gis / pma_gis_visualization.php
blob29f2e1c11cd002707e3023633e4a02de9619d44f
1 <?php
2 /**
3 * Generates the JavaScripts needed to visualize GIS data.
5 * @package phpMyAdmin-GIS
6 */
7 class PMA_GIS_Visualization
9 /**
10 * @var array Raw data for the visualization
12 private $_data;
14 /**
15 * @var array Set of default settigs values are here.
17 private $_settings = array(
19 // Array of colors to be used for GIS visualizations.
20 'colors' => array(
21 '#B02EE0',
22 '#E0642E',
23 '#E0D62E',
24 '#2E97E0',
25 '#BCE02E',
26 '#E02E75',
27 '#5CE02E',
28 '#E0B02E',
29 '#0022E0',
30 '#726CB1',
31 '#481A36',
32 '#BAC658',
33 '#127224',
34 '#825119',
35 '#238C74',
36 '#4C489B',
37 '#87C9BF',
40 // The width of the GIS visualization.
41 'width' => 600,
43 // The height of the GIS visualization.
44 'height' => 450,
47 /**
48 * @var array Options that the user has specified.
50 private $_userSpecifiedSettings = null;
52 /**
53 * Returns the settings array
55 * @return the settings array.
57 public function getSettings()
59 return $this->_settings;
62 /**
63 * Constructor. Stores user specified options.
65 * @param array $data Data for the visualization
66 * @param array $options Users specified options
68 public function __construct($data, $options)
70 $this->_userSpecifiedSettings = $options;
71 $this->_data = $data;
74 /**
75 * All the variable initialization, options handling has to be done here.
77 * @return nothing
79 protected function init()
81 $this->_handleOptions();
84 /**
85 * A function which handles passed parameters. Useful if desired
86 * chart needs to be a little bit different from the default one.
88 * @return nothing
90 private function _handleOptions()
92 if (! is_null($this->_userSpecifiedSettings)) {
93 $this->_settings = array_merge($this->_settings, $this->_userSpecifiedSettings);
97 /**
98 * Sanitizes the file name.
100 * @param string $file_name file name
101 * @param string $ext extension of the file
103 * @return the sanitized file name
105 private function _sanitizeName($file_name, $ext)
107 // convert filename to iso-8859-1, it is safer
108 $file_name = PMA_convert_string('utf-8', 'iso-8859-1', $file_name);
110 // Check if the user already added extension;
111 // get the substring where the extension would be if it was included
112 $extension_start_pos = strlen($file_name) - strlen($ext) - 1;
113 $user_extension = substr($file_name, $extension_start_pos, strlen($file_name));
114 $required_extension = "." . $ext;
115 if (strtolower($user_extension) != $required_extension) {
116 $file_name .= $required_extension;
118 return $file_name;
122 * Handles common tasks of writing the visualization to file for various formats.
124 * @param string $file_name file name
125 * @param string $type mime type
126 * @param string $ext extension of the file
128 * @return nothing
130 private function _toFile($file_name, $type, $ext)
132 $file_name = $this->_sanitizeName($file_name, $ext);
134 ob_clean();
136 PMA_download_header($file_name, $type);
140 * Generate the visualization in SVG format.
142 * @return the generated image resource
144 private function _svg()
146 $this->init();
148 $output = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' . "\n";
149 $output .= '<svg version="1.1" xmlns:svg="http://www.w3.org/2000/svg"'
150 . ' xmlns="http://www.w3.org/2000/svg" width="' . $this->_settings['width'] . '"'
151 . ' height="' . $this->_settings['height'] . '">';
152 $output .= '<g id="groupPanel">';
154 $scale_data = $this->_scaleDataSet($this->_data);
155 $output .= $this->_prepareDataSet($this->_data, $scale_data, 'svg', '');
157 $output .= '</g>';
158 $output .= '</svg>';
160 return $output;
164 * Get the visualization as a SVG.
166 * @return the visualization as a SVG
168 public function asSVG()
170 $output = $this->_svg();
171 return $output;
175 * Saves as a SVG image to a file.
177 * @param string $file_name File name
179 * @return nothing
181 public function toFileAsSvg($file_name)
183 $img = $this->_svg();
184 $this->_toFile($file_name, 'image/svg+xml', 'svg');
185 echo($img);
189 * Generate the visualization in PNG format.
191 * @return the generated image resource
193 private function _png()
195 $this->init();
197 // create image
198 $image = imagecreatetruecolor($this->_settings['width'], $this->_settings['height']);
200 // fill the background
201 $bg = imagecolorallocate($image, 229, 229, 229);
202 imagefilledrectangle(
203 $image, 0, 0, $this->_settings['width'] - 1,
204 $this->_settings['height'] - 1, $bg
207 $scale_data = $this->_scaleDataSet($this->_data);
208 $image = $this->_prepareDataSet($this->_data, $scale_data, 'png', $image);
210 return $image;
214 * Get the visualization as a PNG.
216 * @return the visualization as a PNG
218 public function asPng()
220 $img = $this->_png();
222 // render and save it to variable
223 ob_start();
224 imagepng($img, null, 9, PNG_ALL_FILTERS);
225 imagedestroy($img);
226 $output = ob_get_contents();
227 ob_end_clean();
229 // base64 encode
230 $encoded = base64_encode($output);
231 return '<img src="data:image/png;base64,'. $encoded .'" />';
235 * Saves as a PNG image to a file.
237 * @param string $file_name File name
239 * @return nothing
241 public function toFileAsPng($file_name)
243 $img = $this->_png();
244 $this->_toFile($file_name, 'image/png', 'png');
245 imagepng($img, null, 9, PNG_ALL_FILTERS);
246 imagedestroy($img);
250 * Get the code for visualization with OpenLayers.
252 * @return the code for visualization with OpenLayers
254 public function asOl()
256 $this->init();
257 $scale_data = $this->_scaleDataSet($this->_data);
258 $output =
259 'var options = {'
260 . 'projection: new OpenLayers.Projection("EPSG:900913"),'
261 . 'displayProjection: new OpenLayers.Projection("EPSG:4326"),'
262 . 'units: "m",'
263 . 'numZoomLevels: 18,'
264 . 'maxResolution: 156543.0339,'
265 . 'maxExtent: new OpenLayers.Bounds(-20037508, -20037508, 20037508, 20037508),'
266 . 'restrictedExtent: new OpenLayers.Bounds(-20037508, -20037508, 20037508, 20037508)'
267 . '};'
268 . 'var map = new OpenLayers.Map("openlayersmap", options);'
269 . 'var layerNone = new OpenLayers.Layer.Boxes("None", {isBaseLayer: true});'
270 . 'var layerMapnik = new OpenLayers.Layer.OSM.Mapnik("Mapnik");'
271 . 'var layerOsmarender = new OpenLayers.Layer.OSM.Osmarender("Osmarender");'
272 . 'var layerCycleMap = new OpenLayers.Layer.OSM.CycleMap("CycleMap");'
273 . 'map.addLayers([layerMapnik, layerOsmarender, layerCycleMap, layerNone]);'
274 . 'var vectorLayer = new OpenLayers.Layer.Vector("Data");'
275 . 'var bound;';
276 $output .= $this->_prepareDataSet($this->_data, $scale_data, 'ol', '');
277 $output .=
278 'map.addLayer(vectorLayer);'
279 . 'map.zoomToExtent(bound);'
280 . 'if (map.getZoom() < 2) {'
281 . 'map.zoomTo(2);'
282 . '}'
283 . 'map.addControl(new OpenLayers.Control.LayerSwitcher());'
284 . 'map.addControl(new OpenLayers.Control.MousePosition());';
285 return $output;
289 * Saves as a PDF to a file.
291 * @param string $file_name File name
293 * @return nothing
295 public function toFileAsPdf($file_name)
297 $this->init();
299 include_once './libraries/tcpdf/tcpdf.php';
301 // create pdf
302 $pdf = new TCPDF('', 'pt', $GLOBALS['cfg']['PDFDefaultPageSize'], true, 'UTF-8', false);
304 // disable header and footer
305 $pdf->setPrintHeader(false);
306 $pdf->setPrintFooter(false);
308 //set auto page breaks
309 $pdf->SetAutoPageBreak(false);
311 // add a page
312 $pdf->AddPage();
314 $scale_data = $this->_scaleDataSet($this->_data);
315 $pdf = $this->_prepareDataSet($this->_data, $scale_data, 'pdf', $pdf);
317 // sanitize file name
318 $file_name = $this->_sanitizeName($file_name, 'pdf');
320 ob_clean();
321 $pdf->Output($file_name, 'D');
325 * Calculates the scale, horizontal and vertical offset that should be used.
327 * @param array $data Row data
329 * @return an array containing the scale, x and y offsets
331 private function _scaleDataSet($data)
333 $min_max = array();
334 $border = 15;
335 // effective width and height of the plot
336 $plot_width = $this->_settings['width'] - 2 * $border;
337 $plot_height = $this->_settings['height'] - 2 * $border;
339 foreach ($data as $row) {
341 // Figure out the data type
342 $ref_data = $row[$this->_settings['spatialColumn']];
343 $type_pos = stripos($ref_data, '(');
344 $type = substr($ref_data, 0, $type_pos);
346 $gis_obj = PMA_GIS_Factory::factory($type);
347 if (! $gis_obj) {
348 continue;
350 $scale_data = $gis_obj->scaleRow($row[$this->_settings['spatialColumn']]);
352 // Upadate minimum/maximum values for x and y cordinates.
353 $c_maxX = (float) $scale_data['maxX'];
354 if (! isset($min_max['maxX']) || $c_maxX > $min_max['maxX']) {
355 $min_max['maxX'] = $c_maxX;
358 $c_minX = (float) $scale_data['minX'];
359 if (! isset($min_max['minX']) || $c_minX < $min_max['minX']) {
360 $min_max['minX'] = $c_minX;
363 $c_maxY = (float) $scale_data['maxY'];
364 if (! isset($min_max['maxY']) || $c_maxY > $min_max['maxY']) {
365 $min_max['maxY'] = $c_maxY;
368 $c_minY = (float) $scale_data['minY'];
369 if (! isset($min_max['minY']) || $c_minY < $min_max['minY']) {
370 $min_max['minY'] = $c_minY;
374 // scale the visualization
375 $x_ratio = ($min_max['maxX'] - $min_max['minX']) / $plot_width;
376 $y_ratio = ($min_max['maxY'] - $min_max['minY']) / $plot_height;
377 $ratio = ($x_ratio > $y_ratio) ? $x_ratio : $y_ratio;
379 $scale = ($ratio != 0) ? (1 / $ratio) : 1;
381 if ($x_ratio < $y_ratio) {
382 // center horizontally
383 $x = ($min_max['maxX'] + $min_max['minX'] - $plot_width / $scale) / 2;
384 // fit vertically
385 $y = $min_max['minY'] - ($border / $scale);
386 } else {
387 // fit horizontally
388 $x = $min_max['minX'] - ($border / $scale);
389 // center vertically
390 $y =($min_max['maxY'] + $min_max['minY'] - $plot_height / $scale) / 2;
393 return array(
394 'scale' => $scale,
395 'x' => $x,
396 'y' => $y,
397 'minX' => $min_max['minX'],
398 'maxX' => $min_max['maxX'],
399 'minY' => $min_max['minY'],
400 'maxY' => $min_max['maxY'],
401 'height' => $this->_settings['height'],
406 * Prepares and return the dataset as needed by the visualization.
408 * @param array $data Raw data
409 * @param array $scale_data Data related to scaling
410 * @param string $format Format of the visulaization
411 * @param image $results Image object in the case of png
413 * @return the formatted array of data.
415 private function _prepareDataSet($data, $scale_data, $format, $results)
417 $color_number = 0;
419 // loop through the rows
420 foreach ($data as $row) {
421 $index = $color_number % sizeof($this->_settings['colors']);
423 // Figure out the data type
424 $ref_data = $row[$this->_settings['spatialColumn']];
425 $type_pos = stripos($ref_data, '(');
426 $type = substr($ref_data, 0, $type_pos);
428 $gis_obj = PMA_GIS_Factory::factory($type);
429 if (! $gis_obj) {
430 continue;
432 $label = '';
433 if (isset($this->_settings['labelColumn'])
434 && isset($row[$this->_settings['labelColumn']])
436 $label = $row[$this->_settings['labelColumn']];
439 if ($format == 'svg') {
440 $results .= $gis_obj->prepareRowAsSvg(
441 $row[$this->_settings['spatialColumn']], $label,
442 $this->_settings['colors'][$index], $scale_data
444 } elseif ($format == 'png') {
445 $results = $gis_obj->prepareRowAsPng(
446 $row[$this->_settings['spatialColumn']], $label,
447 $this->_settings['colors'][$index], $scale_data, $results
449 } elseif ($format == 'pdf') {
450 $results = $gis_obj->prepareRowAsPdf(
451 $row[$this->_settings['spatialColumn']], $label,
452 $this->_settings['colors'][$index], $scale_data, $results
454 } elseif ($format == 'ol') {
455 $results .= $gis_obj->prepareRowAsOl(
456 $row[$this->_settings['spatialColumn']], $row['srid'],
457 $label, $this->_settings['colors'][$index], $scale_data
460 $color_number++;
462 return $results;