Translated using Weblate.
[phpmyadmin.git] / libraries / import / shp.php
blob88220591e77f6bc7c08d01ee3ed3cfc2ace3cfad
1 <?php
2 /**
3 * ESRI Shape file import plugin for phpMyAdmin
5 * @package PhpMyAdmin-Import
6 * @subpackage ESRI_Shape
7 */
8 if (! defined('PHPMYADMIN')) {
9 exit;
12 // Drizzle does not suppost GIS data types
13 if (PMA_DRIZZLE) {
14 return;
17 if (isset($plugin_list)) {
18 $plugin_list['shp'] = array(
19 'text' => __('ESRI Shape File'),
20 'extension' => 'shp',
21 'options' => array(),
22 'options_text' => __('Options'),
24 } else {
26 if ((int) ini_get('memory_limit') < 512) {
27 @ini_set('memory_limit', '512M');
29 @set_time_limit(300);
32 // Append the bfShapeFiles directory to the include path variable
33 set_include_path(get_include_path() . PATH_SEPARATOR . getcwd() . '/libraries/bfShapeFiles/');
34 include_once './libraries/bfShapeFiles/ShapeFile.lib.php';
36 $GLOBALS['finished'] = false;
37 $buffer = '';
38 $eof = false;
40 // Returns specified number of bytes from the buffer.
41 // Buffer automatically fetches next chunk of data when the buffer falls short.
42 // Sets $eof when $GLOBALS['finished'] is set and the buffer falls short.
43 function readFromBuffer($length){
44 global $buffer, $eof;
46 if (strlen($buffer) < $length) {
47 if ($GLOBALS['finished']) {
48 $eof = true;
49 } else {
50 $buffer .= PMA_importGetNextChunk();
53 $result = substr($buffer, 0, $length);
54 $buffer = substr($buffer, $length);
55 return $result;
58 /**
59 * This class extends ShapeFile class to cater the following phpMyAdmin
60 * specific requirements.
61 * 1) To load data from .dbf file only when the dBase extension is available.
62 * 2) To use PMA_importGetNextChunk() functionality to read data, rather than
63 * reading directly from a file. Using readFromBuffer() in place of fread().
64 * This makes it possible to use compressions.
66 class PMA_ShapeFile extends ShapeFile
68 function _isDbaseLoaded()
70 return extension_loaded('dbase');
73 function loadFromFile($FileName)
75 $this->_loadHeaders();
76 $this->_loadRecords();
77 if ($this->_isDbaseLoaded()) {
78 $this->_closeDBFFile();
82 function _loadHeaders()
84 readFromBuffer(24);
85 $this->fileLength = loadData("N", readFromBuffer(4));
87 readFromBuffer(4);
88 $this->shapeType = loadData("V", readFromBuffer(4));
90 $this->boundingBox = array();
91 $this->boundingBox["xmin"] = loadData("d", readFromBuffer(8));
92 $this->boundingBox["ymin"] = loadData("d", readFromBuffer(8));
93 $this->boundingBox["xmax"] = loadData("d", readFromBuffer(8));
94 $this->boundingBox["ymax"] = loadData("d", readFromBuffer(8));
96 if ($this->_isDbaseLoaded() && $this->_openDBFFile()) {
97 $this->DBFHeader = $this->_loadDBFHeader();
101 function _loadRecords()
103 global $eof;
104 readFromBuffer(32);
105 while (true) {
106 $record = new PMA_ShapeRecord(-1);
107 $record->loadFromFile($this->SHPFile, $this->DBFFile);
108 if ($record->lastError != "") {
109 return false;
111 if ($eof) {
112 break;
115 $this->records[] = $record;
121 * This class extends ShapeRecord class to cater the following phpMyAdmin
122 * specific requirements.
123 * 1) To load data from .dbf file only when the dBase extension is available.
124 * 2) To use PMA_importGetNextChunk() functionality to read data, rather than
125 * reading directly from a file. Using readFromBuffer() in place of fread().
126 * This makes it possible to use compressions.
128 class PMA_ShapeRecord extends ShapeRecord
130 function loadFromFile(&$SHPFile, &$DBFFile)
132 $this->DBFFile = $DBFFile;
133 $this->_loadHeaders();
135 switch ($this->shapeType) {
136 case 0:
137 $this->_loadNullRecord();
138 break;
139 case 1:
140 $this->_loadPointRecord();
141 break;
142 case 3:
143 $this->_loadPolyLineRecord();
144 break;
145 case 5:
146 $this->_loadPolygonRecord();
147 break;
148 case 8:
149 $this->_loadMultiPointRecord();
150 break;
151 default:
152 $this->setError(sprintf("The Shape Type '%s' is not supported.", $this->shapeType));
153 break;
155 if (extension_loaded('dbase') && isset($this->DBFFile)) {
156 $this->_loadDBFData();
160 function _loadHeaders()
162 $this->recordNumber = loadData("N", readFromBuffer(4));
163 //We read the length of the record
164 $tmp = loadData("N", readFromBuffer(4));
165 $this->shapeType = loadData("V", readFromBuffer(4));
168 function _loadPoint()
170 $data = array();
172 $data["x"] = loadData("d", readFromBuffer(8));
173 $data["y"] = loadData("d", readFromBuffer(8));
175 return $data;
178 function _loadMultiPointRecord()
180 $this->SHPData = array();
181 $this->SHPData["xmin"] = loadData("d", readFromBuffer(8));
182 $this->SHPData["ymin"] = loadData("d", readFromBuffer(8));
183 $this->SHPData["xmax"] = loadData("d", readFromBuffer(8));
184 $this->SHPData["ymax"] = loadData("d", readFromBuffer(8));
186 $this->SHPData["numpoints"] = loadData("V", readFromBuffer(4));
188 for ($i = 0; $i <= $this->SHPData["numpoints"]; $i++) {
189 $this->SHPData["points"][] = $this->_loadPoint();
193 function _loadPolyLineRecord()
195 $this->SHPData = array();
196 $this->SHPData["xmin"] = loadData("d", readFromBuffer(8));
197 $this->SHPData["ymin"] = loadData("d", readFromBuffer(8));
198 $this->SHPData["xmax"] = loadData("d", readFromBuffer(8));
199 $this->SHPData["ymax"] = loadData("d", readFromBuffer(8));
201 $this->SHPData["numparts"] = loadData("V", readFromBuffer(4));
202 $this->SHPData["numpoints"] = loadData("V", readFromBuffer(4));
204 for ($i = 0; $i < $this->SHPData["numparts"]; $i++) {
205 $this->SHPData["parts"][$i] = loadData("V", readFromBuffer(4));
208 $readPoints = 0;
209 reset($this->SHPData["parts"]);
210 while (list($partIndex, $partData) = each($this->SHPData["parts"])) {
211 if (! isset($this->SHPData["parts"][$partIndex]["points"])
212 || !is_array($this->SHPData["parts"][$partIndex]["points"])
214 $this->SHPData["parts"][$partIndex] = array();
215 $this->SHPData["parts"][$partIndex]["points"] = array();
217 while (! in_array($readPoints, $this->SHPData["parts"])
218 && ($readPoints < ($this->SHPData["numpoints"]))
220 $this->SHPData["parts"][$partIndex]["points"][] = $this->_loadPoint();
221 $readPoints++;
227 $shp = new PMA_ShapeFile(1);
228 // If the zip archive has more than one file,
229 // get the correct content to the buffer from .shp file.
230 if ($compression == 'application/zip' && PMA_getNoOfFilesInZip($import_file) > 1) {
231 $zip_content = PMA_getZipContents($import_file, '/^.*\.shp$/i');
232 $GLOBALS['import_text'] = $zip_content['data'];
235 $temp_dbf_file = false;
236 // We need dbase extension to handle .dbf file
237 if (extension_loaded('dbase')) {
238 // If we can extract the zip archive to 'TempDir'
239 // and use the files in it for import
240 if ($compression == 'application/zip'
241 && ! empty($cfg['TempDir'])
242 && is_writable($cfg['TempDir'])
244 $dbf_file_name = PMA_findFileFromZipArchive('/^.*\.dbf$/i', $import_file);
245 // If the corresponding .dbf file is in the zip archive
246 if ($dbf_file_name) {
247 // Extract the .dbf file and point to it.
248 $extracted = PMA_zipExtract(
249 $import_file,
250 realpath($cfg['TempDir']),
251 array($dbf_file_name)
253 if ($extracted) {
254 $dbf_file_path = realpath($cfg['TempDir'])
255 . (PMA_IS_WINDOWS ? '\\' : '/') . $dbf_file_name;
256 $temp_dbf_file = true;
257 // Replace the .dbf with .*, as required by the bsShapeFiles library.
258 $file_name = substr($dbf_file_path, 0, strlen($dbf_file_path) - 4) . '.*';
259 $shp->FileName = $file_name;
263 // If file is in UploadDir, use .dbf file in the same UploadDir
264 // to load extra data.
265 elseif (! empty($local_import_file)
266 && ! empty($cfg['UploadDir'])
267 && $compression == 'none'
269 // Replace the .shp with .*,
270 // so the bsShapeFiles library correctly locates .dbf file.
271 $file_name = substr($import_file, 0, strlen($import_file) - 4) . '.*';
272 $shp->FileName = $file_name;
276 // Load data
277 $shp->loadFromFile('');
278 if ($shp->lastError != "") {
279 $error = true;
280 $message = PMA_Message::error(__('There was an error importing the ESRI shape file: "%s".'));
281 $message->addParam($shp->lastError);
282 return;
285 // Delete the .dbf file extracted to 'TempDir'
286 if ($temp_dbf_file) {
287 unlink($dbf_file_path);
290 $esri_types = array(
291 0 => 'Null Shape',
292 1 => 'Point',
293 3 => 'PolyLine',
294 5 => 'Polygon',
295 8 => 'MultiPoint',
296 11 => 'PointZ',
297 13 => 'PolyLineZ',
298 15 => 'PolygonZ',
299 18 => 'MultiPointZ',
300 21 => 'PointM',
301 23 => 'PolyLineM',
302 25 => 'PolygonM',
303 28 => 'MultiPointM',
304 31 => 'MultiPatch',
307 include_once './libraries/gis/pma_gis_geometry.php';
308 switch ($shp->shapeType) {
309 // ESRI Null Shape
310 case 0:
311 $gis_obj = null;
312 break;
313 // ESRI Point
314 case 1:
315 include_once './libraries/gis/pma_gis_point.php';
316 $gis_obj = PMA_GIS_Point::singleton();
317 break;
318 // ESRI PolyLine
319 case 3:
320 include_once './libraries/gis/pma_gis_multilinestring.php';
321 $gis_obj = PMA_GIS_Multilinestring::singleton();
322 break;
323 // ESRI Polygon
324 case 5:
325 include_once './libraries/gis/pma_gis_multipolygon.php';
326 $gis_obj = PMA_GIS_Multipolygon::singleton();
327 break;
328 // ESRI MultiPoint
329 case 8:
330 include_once './libraries/gis/pma_gis_multipoint.php';
331 $gis_obj = PMA_GIS_Multipoint::singleton();
332 break;
333 default:
334 $error = true;
335 if (! isset($esri_types[$shp->shapeType])) {
336 $message = PMA_Message::error(__('You tried to import an invalid file or the imported file contains invalid data'));
337 } else {
338 $message = PMA_Message::error(__('MySQL Spatial Extension does not support ESRI type "%s".'));
339 $message->addParam($param);
341 return;
344 $num_rows = count($shp->records);
345 // If .dbf file is loaded, the number of extra data columns
346 $num_data_cols = isset($shp->DBFHeader) ? count($shp->DBFHeader) : 0;
348 $rows = array();
349 $col_names = array();
350 if ($num_rows != 0) {
351 foreach ($shp->records as $record) {
352 $tempRow = array();
353 if ($gis_obj == null) {
354 $tempRow[] = null;
355 } else {
356 $tempRow[] = "GeomFromText('" . $gis_obj->getShape($record->SHPData) . "')";
359 if (isset($shp->DBFHeader)) {
360 foreach ($shp->DBFHeader as $c) {
361 $cell = trim($record->DBFData[$c[0]]);
363 if (! strcmp($cell, '')) {
364 $cell = 'NULL';
367 $tempRow[] = $cell;
370 $rows[] = $tempRow;
374 if (count($rows) == 0) {
375 $error = true;
376 $message = PMA_Message::error(__('The imported file does not contain any data'));
377 return;
380 // Column names for spatial column and the rest of the columns,
381 // if they are available
382 $col_names[] = 'SPATIAL';
383 for ($n = 0; $n < $num_data_cols; $n++) {
384 $col_names[] = $shp->DBFHeader[$n][0];
387 // Set table name based on the number of tables
388 if (strlen($db)) {
389 $result = PMA_DBI_fetch_result('SHOW TABLES');
390 $table_name = 'TABLE '.(count($result) + 1);
391 } else {
392 $table_name = 'TBL_NAME';
394 $tables = array(array($table_name, $col_names, $rows));
396 // Use data from shape file to chose best-fit MySQL types for each column
397 $analyses = array();
398 $analyses[] = PMA_analyzeTable($tables[0]);
400 $table_no = 0; $spatial_col = 0;
401 $analyses[$table_no][TYPES][$spatial_col] = GEOMETRY;
402 $analyses[$table_no][FORMATTEDSQL][$spatial_col] = true;
404 // Set database name to the currently selected one, if applicable
405 if (strlen($db)) {
406 $db_name = $db;
407 $options = array('create_db' => false);
408 } else {
409 $db_name = 'SHP_DB';
410 $options = null;
413 // Created and execute necessary SQL statements from data
414 $null_param = null;
415 PMA_buildSQL($db_name, $tables, $analyses, $null_param, $options);
417 unset($tables);
418 unset($analyses);
420 $finished = true;
421 $error = false;
423 // Commit any possible data in buffers
424 PMA_importRunQuery();