GIS_Visualization.class.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Handles visualization of GIS data
  5. *
  6. * @package PhpMyAdmin-GIS
  7. */
  8. if (! defined('PHPMYADMIN')) {
  9. exit;
  10. }
  11. /**
  12. * Handles visualization of GIS data
  13. *
  14. * @package PhpMyAdmin-GIS
  15. */
  16. class PMA_GIS_Visualization
  17. {
  18. /**
  19. * @var array Raw data for the visualization
  20. */
  21. private $_data;
  22. /**
  23. * @var array Set of default settigs values are here.
  24. */
  25. private $_settings = array(
  26. // Array of colors to be used for GIS visualizations.
  27. 'colors' => array(
  28. '#B02EE0',
  29. '#E0642E',
  30. '#E0D62E',
  31. '#2E97E0',
  32. '#BCE02E',
  33. '#E02E75',
  34. '#5CE02E',
  35. '#E0B02E',
  36. '#0022E0',
  37. '#726CB1',
  38. '#481A36',
  39. '#BAC658',
  40. '#127224',
  41. '#825119',
  42. '#238C74',
  43. '#4C489B',
  44. '#87C9BF',
  45. ),
  46. // The width of the GIS visualization.
  47. 'width' => 600,
  48. // The height of the GIS visualization.
  49. 'height' => 450,
  50. );
  51. /**
  52. * @var array Options that the user has specified.
  53. */
  54. private $_userSpecifiedSettings = null;
  55. /**
  56. * Returns the settings array
  57. *
  58. * @return array the settings array
  59. * @access public
  60. */
  61. public function getSettings()
  62. {
  63. return $this->_settings;
  64. }
  65. /**
  66. * Constructor. Stores user specified options.
  67. *
  68. * @param array $data Data for the visualization
  69. * @param array $options Users specified options
  70. *
  71. * @access public
  72. */
  73. public function __construct($data, $options)
  74. {
  75. $this->_userSpecifiedSettings = $options;
  76. $this->_data = $data;
  77. }
  78. /**
  79. * All the variable initialization, options handling has to be done here.
  80. *
  81. * @return void
  82. * @access protected
  83. */
  84. protected function init()
  85. {
  86. $this->_handleOptions();
  87. }
  88. /**
  89. * A function which handles passed parameters. Useful if desired
  90. * chart needs to be a little bit different from the default one.
  91. *
  92. * @return void
  93. * @access private
  94. */
  95. private function _handleOptions()
  96. {
  97. if (! is_null($this->_userSpecifiedSettings)) {
  98. $this->_settings = array_merge(
  99. $this->_settings,
  100. $this->_userSpecifiedSettings
  101. );
  102. }
  103. }
  104. /**
  105. * Sanitizes the file name.
  106. *
  107. * @param string $file_name file name
  108. * @param string $ext extension of the file
  109. *
  110. * @return string the sanitized file name
  111. * @access private
  112. */
  113. private function _sanitizeName($file_name, $ext)
  114. {
  115. $file_name = PMA_sanitizeFilename($file_name);
  116. // Check if the user already added extension;
  117. // get the substring where the extension would be if it was included
  118. $extension_start_pos = strlen($file_name) - strlen($ext) - 1;
  119. $user_extension = substr(
  120. $file_name, $extension_start_pos, strlen($file_name)
  121. );
  122. $required_extension = "." . $ext;
  123. if (strtolower($user_extension) != $required_extension) {
  124. $file_name .= $required_extension;
  125. }
  126. return $file_name;
  127. }
  128. /**
  129. * Handles common tasks of writing the visualization to file for various formats.
  130. *
  131. * @param string $file_name file name
  132. * @param string $type mime type
  133. * @param string $ext extension of the file
  134. *
  135. * @return void
  136. * @access private
  137. */
  138. private function _toFile($file_name, $type, $ext)
  139. {
  140. $file_name = $this->_sanitizeName($file_name, $ext);
  141. PMA_downloadHeader($file_name, $type);
  142. }
  143. /**
  144. * Generate the visualization in SVG format.
  145. *
  146. * @return string the generated image resource
  147. * @access private
  148. */
  149. private function _svg()
  150. {
  151. $this->init();
  152. $output = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' . "\n";
  153. $output .= '<svg version="1.1" xmlns:svg="http://www.w3.org/2000/svg"'
  154. . ' xmlns="http://www.w3.org/2000/svg"'
  155. . ' width="' . $this->_settings['width'] . '"'
  156. . ' height="' . $this->_settings['height'] . '">';
  157. $output .= '<g id="groupPanel">';
  158. $scale_data = $this->_scaleDataSet($this->_data);
  159. $output .= $this->_prepareDataSet($this->_data, $scale_data, 'svg');
  160. $output .= '</g>';
  161. $output .= '</svg>';
  162. return $output;
  163. }
  164. /**
  165. * Get the visualization as a SVG.
  166. *
  167. * @return string the visualization as a SVG
  168. * @access public
  169. */
  170. public function asSVG()
  171. {
  172. $output = $this->_svg();
  173. return $output;
  174. }
  175. /**
  176. * Saves as a SVG image to a file.
  177. *
  178. * @param string $file_name File name
  179. *
  180. * @return void
  181. * @access public
  182. */
  183. public function toFileAsSvg($file_name)
  184. {
  185. $img = $this->_svg();
  186. $this->_toFile($file_name, 'image/svg+xml', 'svg');
  187. echo($img);
  188. }
  189. /**
  190. * Generate the visualization in PNG format.
  191. *
  192. * @return resource the generated image resource
  193. * @access private
  194. */
  195. private function _png()
  196. {
  197. $this->init();
  198. // create image
  199. $image = imagecreatetruecolor(
  200. $this->_settings['width'],
  201. $this->_settings['height']
  202. );
  203. // fill the background
  204. $bg = imagecolorallocate($image, 229, 229, 229);
  205. imagefilledrectangle(
  206. $image, 0, 0, $this->_settings['width'] - 1,
  207. $this->_settings['height'] - 1, $bg
  208. );
  209. $scale_data = $this->_scaleDataSet($this->_data);
  210. $image = $this->_prepareDataSet($this->_data, $scale_data, 'png', $image);
  211. return $image;
  212. }
  213. /**
  214. * Get the visualization as a PNG.
  215. *
  216. * @return string the visualization as a PNG
  217. * @access public
  218. */
  219. public function asPng()
  220. {
  221. $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();
  228. // base64 encode
  229. $encoded = base64_encode($output);
  230. return '<img src="data:image/png;base64,' . $encoded . '" />';
  231. }
  232. /**
  233. * Saves as a PNG image to a file.
  234. *
  235. * @param string $file_name File name
  236. *
  237. * @return void
  238. * @access public
  239. */
  240. public function toFileAsPng($file_name)
  241. {
  242. $img = $this->_png();
  243. $this->_toFile($file_name, 'image/png', 'png');
  244. imagepng($img, null, 9, PNG_ALL_FILTERS);
  245. imagedestroy($img);
  246. }
  247. /**
  248. * Get the code for visualization with OpenLayers.
  249. *
  250. * @return string the code for visualization with OpenLayers
  251. * @access public
  252. */
  253. public function asOl()
  254. {
  255. $this->init();
  256. $scale_data = $this->_scaleDataSet($this->_data);
  257. $output
  258. = 'var options = {'
  259. . 'projection: new OpenLayers.Projection("EPSG:900913"),'
  260. . 'displayProjection: new OpenLayers.Projection("EPSG:4326"),'
  261. . 'units: "m",'
  262. . 'numZoomLevels: 18,'
  263. . 'maxResolution: 156543.0339,'
  264. . 'maxExtent: new OpenLayers.Bounds('
  265. . '-20037508, -20037508, 20037508, 20037508),'
  266. . 'restrictedExtent: new OpenLayers.Bounds('
  267. . '-20037508, -20037508, 20037508, 20037508)'
  268. . '};'
  269. . 'var map = new OpenLayers.Map("openlayersmap", options);'
  270. . 'var layerNone = new OpenLayers.Layer.Boxes('
  271. . '"None", {isBaseLayer: true});'
  272. . 'var layerMapnik = new OpenLayers.Layer.OSM.Mapnik("Mapnik");'
  273. . 'var layerCycleMap = new OpenLayers.Layer.OSM.CycleMap("CycleMap");'
  274. . 'map.addLayers([layerMapnik,layerCycleMap,layerNone]);'
  275. . 'var vectorLayer = new OpenLayers.Layer.Vector("Data");'
  276. . 'var bound;';
  277. $output .= $this->_prepareDataSet($this->_data, $scale_data, 'ol');
  278. $output .=
  279. 'map.addLayer(vectorLayer);'
  280. . 'map.zoomToExtent(bound);'
  281. . 'if (map.getZoom() < 2) {'
  282. . 'map.zoomTo(2);'
  283. . '}'
  284. . 'map.addControl(new OpenLayers.Control.LayerSwitcher());'
  285. . 'map.addControl(new OpenLayers.Control.MousePosition());';
  286. return $output;
  287. }
  288. /**
  289. * Saves as a PDF to a file.
  290. *
  291. * @param string $file_name File name
  292. *
  293. * @return void
  294. * @access public
  295. */
  296. public function toFileAsPdf($file_name)
  297. {
  298. $this->init();
  299. include_once './libraries/tcpdf/tcpdf.php';
  300. // create pdf
  301. $pdf = new TCPDF(
  302. '', 'pt', $GLOBALS['cfg']['PDFDefaultPageSize'], true, 'UTF-8', false
  303. );
  304. // disable header and footer
  305. $pdf->setPrintHeader(false);
  306. $pdf->setPrintFooter(false);
  307. //set auto page breaks
  308. $pdf->SetAutoPageBreak(false);
  309. // add a page
  310. $pdf->AddPage();
  311. $scale_data = $this->_scaleDataSet($this->_data);
  312. $pdf = $this->_prepareDataSet($this->_data, $scale_data, 'pdf', $pdf);
  313. // sanitize file name
  314. $file_name = $this->_sanitizeName($file_name, 'pdf');
  315. $pdf->Output($file_name, 'D');
  316. }
  317. /**
  318. * Calculates the scale, horizontal and vertical offset that should be used.
  319. *
  320. * @param array $data Row data
  321. *
  322. * @return array an array containing the scale, x and y offsets
  323. * @access private
  324. */
  325. private function _scaleDataSet($data)
  326. {
  327. $min_max = array();
  328. $border = 15;
  329. // effective width and height of the plot
  330. $plot_width = $this->_settings['width'] - 2 * $border;
  331. $plot_height = $this->_settings['height'] - 2 * $border;
  332. foreach ($data as $row) {
  333. // Figure out the data type
  334. $ref_data = $row[$this->_settings['spatialColumn']];
  335. $type_pos = stripos($ref_data, '(');
  336. $type = substr($ref_data, 0, $type_pos);
  337. $gis_obj = PMA_GIS_Factory::factory($type);
  338. if (! $gis_obj) {
  339. continue;
  340. }
  341. $scale_data = $gis_obj->scaleRow(
  342. $row[$this->_settings['spatialColumn']]
  343. );
  344. // Upadate minimum/maximum values for x and y cordinates.
  345. $c_maxX = (float) $scale_data['maxX'];
  346. if (! isset($min_max['maxX']) || $c_maxX > $min_max['maxX']) {
  347. $min_max['maxX'] = $c_maxX;
  348. }
  349. $c_minX = (float) $scale_data['minX'];
  350. if (! isset($min_max['minX']) || $c_minX < $min_max['minX']) {
  351. $min_max['minX'] = $c_minX;
  352. }
  353. $c_maxY = (float) $scale_data['maxY'];
  354. if (! isset($min_max['maxY']) || $c_maxY > $min_max['maxY']) {
  355. $min_max['maxY'] = $c_maxY;
  356. }
  357. $c_minY = (float) $scale_data['minY'];
  358. if (! isset($min_max['minY']) || $c_minY < $min_max['minY']) {
  359. $min_max['minY'] = $c_minY;
  360. }
  361. }
  362. // scale the visualization
  363. $x_ratio = ($min_max['maxX'] - $min_max['minX']) / $plot_width;
  364. $y_ratio = ($min_max['maxY'] - $min_max['minY']) / $plot_height;
  365. $ratio = ($x_ratio > $y_ratio) ? $x_ratio : $y_ratio;
  366. $scale = ($ratio != 0) ? (1 / $ratio) : 1;
  367. if ($x_ratio < $y_ratio) {
  368. // center horizontally
  369. $x = ($min_max['maxX'] + $min_max['minX'] - $plot_width / $scale) / 2;
  370. // fit vertically
  371. $y = $min_max['minY'] - ($border / $scale);
  372. } else {
  373. // fit horizontally
  374. $x = $min_max['minX'] - ($border / $scale);
  375. // center vertically
  376. $y =($min_max['maxY'] + $min_max['minY'] - $plot_height / $scale) / 2;
  377. }
  378. return array(
  379. 'scale' => $scale,
  380. 'x' => $x,
  381. 'y' => $y,
  382. 'minX' => $min_max['minX'],
  383. 'maxX' => $min_max['maxX'],
  384. 'minY' => $min_max['minY'],
  385. 'maxY' => $min_max['maxY'],
  386. 'height' => $this->_settings['height'],
  387. );
  388. }
  389. /**
  390. * Prepares and return the dataset as needed by the visualization.
  391. *
  392. * @param array $data Raw data
  393. * @param array $scale_data Data related to scaling
  394. * @param string $format Format of the visulaization
  395. * @param null|object $results Image object in the case of png
  396. * TCPDF object in the case of pdf
  397. *
  398. * @return mixed the formatted array of data
  399. * @access private
  400. */
  401. private function _prepareDataSet($data, $scale_data, $format, $results = null)
  402. {
  403. $color_number = 0;
  404. // loop through the rows
  405. foreach ($data as $row) {
  406. $index = $color_number % sizeof($this->_settings['colors']);
  407. // Figure out the data type
  408. $ref_data = $row[$this->_settings['spatialColumn']];
  409. $type_pos = stripos($ref_data, '(');
  410. $type = substr($ref_data, 0, $type_pos);
  411. $gis_obj = PMA_GIS_Factory::factory($type);
  412. if (! $gis_obj) {
  413. continue;
  414. }
  415. $label = '';
  416. if (isset($this->_settings['labelColumn'])
  417. && isset($row[$this->_settings['labelColumn']])
  418. ) {
  419. $label = $row[$this->_settings['labelColumn']];
  420. }
  421. if ($format == 'svg') {
  422. $results .= $gis_obj->prepareRowAsSvg(
  423. $row[$this->_settings['spatialColumn']], $label,
  424. $this->_settings['colors'][$index], $scale_data
  425. );
  426. } elseif ($format == 'png') {
  427. $results = $gis_obj->prepareRowAsPng(
  428. $row[$this->_settings['spatialColumn']], $label,
  429. $this->_settings['colors'][$index], $scale_data, $results
  430. );
  431. } elseif ($format == 'pdf') {
  432. $results = $gis_obj->prepareRowAsPdf(
  433. $row[$this->_settings['spatialColumn']], $label,
  434. $this->_settings['colors'][$index], $scale_data, $results
  435. );
  436. } elseif ($format == 'ol') {
  437. $results .= $gis_obj->prepareRowAsOl(
  438. $row[$this->_settings['spatialColumn']], $row['srid'],
  439. $label, $this->_settings['colors'][$index], $scale_data
  440. );
  441. }
  442. $color_number++;
  443. }
  444. return $results;
  445. }
  446. }
  447. ?>