3 * ESRI Shape file import plugin for phpMyAdmin
5 * @package PhpMyAdmin-Import
6 * @subpackage ESRI_Shape
8 if (! defined('PHPMYADMIN')) {
12 // Drizzle does not suppost GIS data types
17 if (isset($plugin_list)) {
18 $plugin_list['shp'] = array(
19 'text' => __('ESRI Shape File'),
22 'options_text' => __('Options'),
26 if ((int) ini_get('memory_limit') < 512) {
27 @ini_set
('memory_limit', '512M');
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;
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){
46 if (strlen($buffer) < $length) {
47 if ($GLOBALS['finished']) {
50 $buffer .= PMA_importGetNextChunk();
53 $result = substr($buffer, 0, $length);
54 $buffer = substr($buffer, $length);
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()
85 $this->fileLength
= loadData("N", 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()
106 $record = new PMA_ShapeRecord(-1);
107 $record->loadFromFile($this->SHPFile
, $this->DBFFile
);
108 if ($record->lastError
!= "") {
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
) {
137 $this->_loadNullRecord();
140 $this->_loadPointRecord();
143 $this->_loadPolyLineRecord();
146 $this->_loadPolygonRecord();
149 $this->_loadMultiPointRecord();
152 $this->setError(sprintf("The Shape Type '%s' is not supported.", $this->shapeType
));
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()
172 $data["x"] = loadData("d", readFromBuffer(8));
173 $data["y"] = loadData("d", readFromBuffer(8));
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));
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();
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(
250 realpath($cfg['TempDir']),
251 array($dbf_file_name)
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;
277 $shp->loadFromFile('');
278 if ($shp->lastError
!= "") {
280 $message = PMA_Message
::error(__('There was an error importing the ESRI shape file: "%s".'));
281 $message->addParam($shp->lastError
);
285 // Delete the .dbf file extracted to 'TempDir'
286 if ($temp_dbf_file) {
287 unlink($dbf_file_path);
307 include_once './libraries/gis/pma_gis_geometry.php';
308 switch ($shp->shapeType
) {
315 include_once './libraries/gis/pma_gis_point.php';
316 $gis_obj = PMA_GIS_Point
::singleton();
320 include_once './libraries/gis/pma_gis_multilinestring.php';
321 $gis_obj = PMA_GIS_Multilinestring
::singleton();
325 include_once './libraries/gis/pma_gis_multipolygon.php';
326 $gis_obj = PMA_GIS_Multipolygon
::singleton();
330 include_once './libraries/gis/pma_gis_multipoint.php';
331 $gis_obj = PMA_GIS_Multipoint
::singleton();
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'));
338 $message = PMA_Message
::error(__('MySQL Spatial Extension does not support ESRI type "%s".'));
339 $message->addParam($param);
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;
349 $col_names = array();
350 if ($num_rows != 0) {
351 foreach ($shp->records
as $record) {
353 if ($gis_obj == null) {
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, '')) {
374 if (count($rows) == 0) {
376 $message = PMA_Message
::error(__('The imported file does not contain any data'));
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
389 $result = PMA_DBI_fetch_result('SHOW TABLES');
390 $table_name = 'TABLE '.(count($result) +
1);
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
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
407 $options = array('create_db' => false);
413 // Created and execute necessary SQL statements from data
415 PMA_buildSQL($db_name, $tables, $analyses, $null_param, $options);
423 // Commit any possible data in buffers
424 PMA_importRunQuery();