ImportShp.php 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * ESRI Shape file import plugin for phpMyAdmin
  5. *
  6. * @package PhpMyAdmin-Import
  7. * @subpackage ESRI_Shape
  8. */
  9. namespace PhpMyAdmin\Plugins\Import;
  10. use PhpMyAdmin\Gis\GisFactory;
  11. use PhpMyAdmin\Gis\GisMultiLineString;
  12. use PhpMyAdmin\Gis\GisMultiPoint;
  13. use PhpMyAdmin\Gis\GisPoint;
  14. use PhpMyAdmin\Gis\GisPolygon;
  15. use PhpMyAdmin\Import;
  16. use PhpMyAdmin\Message;
  17. use PhpMyAdmin\Plugins\ImportPlugin;
  18. use PhpMyAdmin\Properties\Plugins\ImportPluginProperties;
  19. use PhpMyAdmin\Sanitize;
  20. use PhpMyAdmin\ZipExtension;
  21. /**
  22. * Handles the import for ESRI Shape files
  23. *
  24. * @package PhpMyAdmin-Import
  25. * @subpackage ESRI_Shape
  26. */
  27. class ImportShp extends ImportPlugin
  28. {
  29. /**
  30. * @var ZipExtension
  31. */
  32. private $zipExtension;
  33. /**
  34. * Constructor
  35. */
  36. public function __construct()
  37. {
  38. $this->setProperties();
  39. if (extension_loaded('zip')) {
  40. $this->zipExtension = new ZipExtension();
  41. }
  42. }
  43. /**
  44. * Sets the import plugin properties.
  45. * Called in the constructor.
  46. *
  47. * @return void
  48. */
  49. protected function setProperties()
  50. {
  51. $importPluginProperties = new ImportPluginProperties();
  52. $importPluginProperties->setText(__('ESRI Shape File'));
  53. $importPluginProperties->setExtension('shp');
  54. $importPluginProperties->setOptions(array());
  55. $importPluginProperties->setOptionsText(__('Options'));
  56. $this->properties = $importPluginProperties;
  57. }
  58. /**
  59. * Handles the whole import logic
  60. *
  61. * @param array &$sql_data 2-element array with sql data
  62. *
  63. * @return void
  64. */
  65. public function doImport(array &$sql_data = array())
  66. {
  67. global $db, $error, $finished,
  68. $import_file, $local_import_file, $message;
  69. $GLOBALS['finished'] = false;
  70. $compression = $GLOBALS['import_handle']->getCompression();
  71. $shp = new ShapeFileImport(1);
  72. // If the zip archive has more than one file,
  73. // get the correct content to the buffer from .shp file.
  74. if ($compression == 'application/zip'
  75. && $this->zipExtension->getNumberOfFiles($import_file) > 1
  76. ) {
  77. if ($GLOBALS['import_handle']->openZip('/^.*\.shp$/i') === false) {
  78. $message = Message::error(
  79. __('There was an error importing the ESRI shape file: "%s".')
  80. );
  81. $message->addParam($GLOBALS['import_handle']->getError());
  82. return;
  83. }
  84. }
  85. $temp_dbf_file = false;
  86. // We need dbase extension to handle .dbf file
  87. if (extension_loaded('dbase')) {
  88. $temp = $GLOBALS['PMA_Config']->getTempDir('shp');
  89. // If we can extract the zip archive to 'TempDir'
  90. // and use the files in it for import
  91. if ($compression == 'application/zip' && ! is_null($temp)) {
  92. $dbf_file_name = $this->zipExtension->findFile(
  93. $import_file,
  94. '/^.*\.dbf$/i'
  95. );
  96. // If the corresponding .dbf file is in the zip archive
  97. if ($dbf_file_name) {
  98. // Extract the .dbf file and point to it.
  99. $extracted = $this->zipExtension->extract(
  100. $import_file,
  101. $dbf_file_name
  102. );
  103. if ($extracted !== false) {
  104. $dbf_file_path = $temp . (PMA_IS_WINDOWS ? '\\' : '/')
  105. . Sanitize::sanitizeFilename($dbf_file_name, true);
  106. $handle = fopen($dbf_file_path, 'wb');
  107. if ($handle !== false) {
  108. fwrite($handle, $extracted);
  109. fclose($handle);
  110. $temp_dbf_file = true;
  111. // Replace the .dbf with .*, as required
  112. // by the bsShapeFiles library.
  113. $file_name = substr(
  114. $dbf_file_path, 0, strlen($dbf_file_path) - 4
  115. ) . '.*';
  116. $shp->FileName = $file_name;
  117. }
  118. }
  119. }
  120. } elseif (!empty($local_import_file)
  121. && !empty($GLOBALS['cfg']['UploadDir'])
  122. && $compression == 'none'
  123. ) {
  124. // If file is in UploadDir, use .dbf file in the same UploadDir
  125. // to load extra data.
  126. // Replace the .shp with .*,
  127. // so the bsShapeFiles library correctly locates .dbf file.
  128. $file_name = mb_substr(
  129. $import_file,
  130. 0,
  131. mb_strlen($import_file) - 4
  132. ) . '.*';
  133. $shp->FileName = $file_name;
  134. }
  135. }
  136. // Delete the .dbf file extracted to 'TempDir'
  137. if ($temp_dbf_file
  138. && isset($dbf_file_path)
  139. && @file_exists($dbf_file_path)
  140. ) {
  141. unlink($dbf_file_path);
  142. }
  143. // Load data
  144. $shp->loadFromFile('');
  145. if ($shp->lastError != "") {
  146. $error = true;
  147. $message = Message::error(
  148. __('There was an error importing the ESRI shape file: "%s".')
  149. );
  150. $message->addParam($shp->lastError);
  151. return;
  152. }
  153. switch ($shp->shapeType) {
  154. // ESRI Null Shape
  155. case 0:
  156. break;
  157. // ESRI Point
  158. case 1:
  159. $gis_type = 'point';
  160. break;
  161. // ESRI PolyLine
  162. case 3:
  163. $gis_type = 'multilinestring';
  164. break;
  165. // ESRI Polygon
  166. case 5:
  167. $gis_type = 'multipolygon';
  168. break;
  169. // ESRI MultiPoint
  170. case 8:
  171. $gis_type = 'multipoint';
  172. break;
  173. default:
  174. $error = true;
  175. $message = Message::error(
  176. __('MySQL Spatial Extension does not support ESRI type "%s".')
  177. );
  178. $message->addParam($shp->getShapeName());
  179. return;
  180. }
  181. if (isset($gis_type)) {
  182. /** @var GisMultiLineString|\PhpMyAdmin\Gis\GisMultiPoint|\PhpMyAdmin\Gis\GisPoint|GisPolygon $gis_obj */
  183. $gis_obj = GisFactory::factory($gis_type);
  184. } else {
  185. $gis_obj = null;
  186. }
  187. $num_rows = count($shp->records);
  188. // If .dbf file is loaded, the number of extra data columns
  189. $num_data_cols = isset($shp->DBFHeader) ? count($shp->DBFHeader) : 0;
  190. $rows = array();
  191. $col_names = array();
  192. if ($num_rows != 0) {
  193. foreach ($shp->records as $record) {
  194. $tempRow = array();
  195. if ($gis_obj == null) {
  196. $tempRow[] = null;
  197. } else {
  198. $tempRow[] = "GeomFromText('"
  199. . $gis_obj->getShape($record->SHPData) . "')";
  200. }
  201. if (isset($shp->DBFHeader)) {
  202. foreach ($shp->DBFHeader as $c) {
  203. $cell = trim($record->DBFData[$c[0]]);
  204. if (!strcmp($cell, '')) {
  205. $cell = 'NULL';
  206. }
  207. $tempRow[] = $cell;
  208. }
  209. }
  210. $rows[] = $tempRow;
  211. }
  212. }
  213. if (count($rows) == 0) {
  214. $error = true;
  215. $message = Message::error(
  216. __('The imported file does not contain any data!')
  217. );
  218. return;
  219. }
  220. // Column names for spatial column and the rest of the columns,
  221. // if they are available
  222. $col_names[] = 'SPATIAL';
  223. for ($n = 0; $n < $num_data_cols; $n++) {
  224. $col_names[] = $shp->DBFHeader[$n][0];
  225. }
  226. // Set table name based on the number of tables
  227. if (strlen($db) > 0) {
  228. $result = $GLOBALS['dbi']->fetchResult('SHOW TABLES');
  229. $table_name = 'TABLE ' . (count($result) + 1);
  230. } else {
  231. $table_name = 'TBL_NAME';
  232. }
  233. $tables = array(array($table_name, $col_names, $rows));
  234. // Use data from shape file to chose best-fit MySQL types for each column
  235. $analyses = array();
  236. $analyses[] = Import::analyzeTable($tables[0]);
  237. $table_no = 0;
  238. $spatial_col = 0;
  239. $analyses[$table_no][Import::TYPES][$spatial_col] = Import::GEOMETRY;
  240. $analyses[$table_no][Import::FORMATTEDSQL][$spatial_col] = true;
  241. // Set database name to the currently selected one, if applicable
  242. if (strlen($db) > 0) {
  243. $db_name = $db;
  244. $options = array('create_db' => false);
  245. } else {
  246. $db_name = 'SHP_DB';
  247. $options = null;
  248. }
  249. // Created and execute necessary SQL statements from data
  250. $null_param = null;
  251. Import::buildSql($db_name, $tables, $analyses, $null_param, $options, $sql_data);
  252. unset($tables);
  253. unset($analyses);
  254. $finished = true;
  255. $error = false;
  256. // Commit any possible data in buffers
  257. Import::runQuery('', '', $sql_data);
  258. }
  259. /**
  260. * Returns specified number of bytes from the buffer.
  261. * Buffer automatically fetches next chunk of data when the buffer
  262. * falls short.
  263. * Sets $eof when $GLOBALS['finished'] is set and the buffer falls short.
  264. *
  265. * @param int $length number of bytes
  266. *
  267. * @return string
  268. */
  269. public static function readFromBuffer($length)
  270. {
  271. global $buffer, $eof;
  272. if (strlen($buffer) < $length) {
  273. if ($GLOBALS['finished']) {
  274. $eof = true;
  275. } else {
  276. $buffer .= Import::getNextChunk();
  277. }
  278. }
  279. $result = substr($buffer, 0, $length);
  280. $buffer = substr($buffer, $length);
  281. return $result;
  282. }
  283. }