Upgraded phpmyadmin to 4.0.4 (All Languages) - No modifications yet
[openemr.git] / phpmyadmin / libraries / bfShapeFiles / ShapeFile.lib.php
blob56057d2c2ab67e83de918782c11268e0cb794fc6
1 <?php
2 /**
3 * BytesFall ShapeFiles library
5 * The library implements the 2D variants of the ShapeFile format as defined in
6 * http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf.
7 * The library currently supports reading and editing of ShapeFiles and the
8 * Associated information (DBF file).
10 * @package bfShapeFiles
11 * @version 0.0.2
12 * @link http://bfshapefiles.sourceforge.net/
13 * @license http://www.gnu.org/copyleft/gpl.html GPLv2
15 function loadData($type, $data) {
16 if (!$data) return $data;
17 $tmp = unpack($type, $data);
18 return current($tmp);
21 function swap($binValue) {
22 $result = $binValue{strlen($binValue) - 1};
23 for($i = strlen($binValue) - 2; $i >= 0 ; $i--) {
24 $result .= $binValue{$i};
27 return $result;
30 function packDouble($value, $mode = 'LE') {
31 $value = (double)$value;
32 $bin = pack("d", $value);
34 //We test if the conversion of an integer (1) is done as LE or BE by default
35 switch (pack ('L', 1)) {
36 case pack ('V', 1): //Little Endian
37 $result = ($mode == 'LE') ? $bin : swap($bin);
38 break;
39 case pack ('N', 1): //Big Endian
40 $result = ($mode == 'BE') ? $bin : swap($bin);
41 break;
42 default: //Some other thing, we just return false
43 $result = FALSE;
46 return $result;
48 /**
49 * ShapeFile class
51 * @package bfShapeFiles
53 class ShapeFile {
54 var $FileName;
56 var $SHPFile;
57 var $SHXFile;
58 var $DBFFile;
60 var $DBFHeader;
62 var $lastError = "";
64 var $boundingBox = array("xmin" => 0.0, "ymin" => 0.0, "xmax" => 0.0, "ymax" => 0.0);
65 var $fileLength = 0;
66 var $shapeType = 0;
68 var $records;
70 function ShapeFile($shapeType, $boundingBox = array("xmin" => 0.0, "ymin" => 0.0, "xmax" => 0.0, "ymax" => 0.0), $FileName = NULL) {
71 $this->shapeType = $shapeType;
72 $this->boundingBox = $boundingBox;
73 $this->FileName = $FileName;
74 $this->fileLength = 50;
77 function loadFromFile($FileName) {
78 $this->FileName = $FileName;
80 if (($this->_openSHPFile()) && ($this->_openDBFFile())) {
81 $this->_loadHeaders();
82 $this->_loadRecords();
83 $this->_closeSHPFile();
84 $this->_closeDBFFile();
85 } else {
86 return false;
90 function saveToFile($FileName = NULL) {
91 if ($FileName != NULL) $this->FileName = $FileName;
93 if (($this->_openSHPFile(TRUE)) && ($this->_openSHXFile(TRUE)) && ($this->_openDBFFile(TRUE))) {
94 $this->_saveHeaders();
95 $this->_saveRecords();
96 $this->_closeSHPFile();
97 $this->_closeSHXFile();
98 $this->_closeDBFFile();
99 } else {
100 return false;
104 function addRecord($record) {
105 if ((isset($this->DBFHeader)) && (is_array($this->DBFHeader))) {
106 $record->updateDBFInfo($this->DBFHeader);
109 $this->fileLength += ($record->getContentLength() + 4);
110 $this->records[] = $record;
111 $this->records[count($this->records) - 1]->recordNumber = count($this->records);
113 return (count($this->records) - 1);
116 function deleteRecord($index) {
117 if (isset($this->records[$index])) {
118 $this->fileLength -= ($this->records[$index]->getContentLength() + 4);
119 for ($i = $index; $i < (count($this->records) - 1); $i++) {
120 $this->records[$i] = $this->records[$i + 1];
122 unset($this->records[count($this->records) - 1]);
123 $this->_deleteRecordFromDBF($index);
127 function getDBFHeader() {
128 return $this->DBFHeader;
131 function setDBFHeader($header) {
132 $this->DBFHeader = $header;
134 for ($i = 0; $i < count($this->records); $i++) {
135 $this->records[$i]->updateDBFInfo($header);
139 function getIndexFromDBFData($field, $value) {
140 $result = -1;
141 for ($i = 0; $i < (count($this->records) - 1); $i++) {
142 if (isset($this->records[$i]->DBFData[$field]) && (strtoupper($this->records[$i]->DBFData[$field]) == strtoupper($value))) {
143 $result = $i;
147 return $result;
150 function _loadDBFHeader() {
151 $DBFFile = fopen(str_replace('.*', '.dbf', $this->FileName), 'r');
153 $result = array();
154 $buff32 = array();
155 $i = 1;
156 $inHeader = true;
158 while ($inHeader) {
159 if (!feof($DBFFile)) {
160 $buff32 = fread($DBFFile, 32);
161 if ($i > 1) {
162 if (substr($buff32, 0, 1) == chr(13)) {
163 $inHeader = false;
164 } else {
165 $pos = strpos(substr($buff32, 0, 10), chr(0));
166 $pos = ($pos == 0 ? 10 : $pos);
168 $fieldName = substr($buff32, 0, $pos);
169 $fieldType = substr($buff32, 11, 1);
170 $fieldLen = ord(substr($buff32, 16, 1));
171 $fieldDec = ord(substr($buff32, 17, 1));
173 array_push($result, array($fieldName, $fieldType, $fieldLen, $fieldDec));
176 $i++;
177 } else {
178 $inHeader = false;
182 fclose($DBFFile);
183 return($result);
186 function _deleteRecordFromDBF($index) {
187 if (@dbase_delete_record($this->DBFFile, $index)) {
188 @dbase_pack($this->DBFFile);
192 function _loadHeaders() {
193 fseek($this->SHPFile, 24, SEEK_SET);
194 $this->fileLength = loadData("N", fread($this->SHPFile, 4));
196 fseek($this->SHPFile, 32, SEEK_SET);
197 $this->shapeType = loadData("V", fread($this->SHPFile, 4));
199 $this->boundingBox = array();
200 $this->boundingBox["xmin"] = loadData("d", fread($this->SHPFile, 8));
201 $this->boundingBox["ymin"] = loadData("d", fread($this->SHPFile, 8));
202 $this->boundingBox["xmax"] = loadData("d", fread($this->SHPFile, 8));
203 $this->boundingBox["ymax"] = loadData("d", fread($this->SHPFile, 8));
205 $this->DBFHeader = $this->_loadDBFHeader();
208 function _saveHeaders() {
209 fwrite($this->SHPFile, pack("NNNNNN", 9994, 0, 0, 0, 0, 0));
210 fwrite($this->SHPFile, pack("N", $this->fileLength));
211 fwrite($this->SHPFile, pack("V", 1000));
212 fwrite($this->SHPFile, pack("V", $this->shapeType));
213 fwrite($this->SHPFile, packDouble($this->boundingBox['xmin']));
214 fwrite($this->SHPFile, packDouble($this->boundingBox['ymin']));
215 fwrite($this->SHPFile, packDouble($this->boundingBox['xmax']));
216 fwrite($this->SHPFile, packDouble($this->boundingBox['ymax']));
217 fwrite($this->SHPFile, pack("dddd", 0, 0, 0, 0));
219 fwrite($this->SHXFile, pack("NNNNNN", 9994, 0, 0, 0, 0, 0));
220 fwrite($this->SHXFile, pack("N", 50 + 4*count($this->records)));
221 fwrite($this->SHXFile, pack("V", 1000));
222 fwrite($this->SHXFile, pack("V", $this->shapeType));
223 fwrite($this->SHXFile, packDouble($this->boundingBox['xmin']));
224 fwrite($this->SHXFile, packDouble($this->boundingBox['ymin']));
225 fwrite($this->SHXFile, packDouble($this->boundingBox['xmax']));
226 fwrite($this->SHXFile, packDouble($this->boundingBox['ymax']));
227 fwrite($this->SHXFile, pack("dddd", 0, 0, 0, 0));
230 function _loadRecords() {
231 fseek($this->SHPFile, 100);
232 while (!feof($this->SHPFile)) {
233 $bByte = ftell($this->SHPFile);
234 $record = new ShapeRecord(-1);
235 $record->loadFromFile($this->SHPFile, $this->DBFFile);
236 $eByte = ftell($this->SHPFile);
237 if (($eByte <= $bByte) || ($record->lastError != "")) {
238 return false;
241 $this->records[] = $record;
245 function _saveRecords() {
246 if (file_exists(str_replace('.*', '.dbf', $this->FileName))) {
247 @unlink(str_replace('.*', '.dbf', $this->FileName));
249 if (!($this->DBFFile = @dbase_create(str_replace('.*', '.dbf', $this->FileName), $this->DBFHeader))) {
250 return $this->setError(sprintf("It wasn't possible to create the DBase file '%s'", str_replace('.*', '.dbf', $this->FileName)));
253 $offset = 50;
254 if (is_array($this->records) && (count($this->records) > 0)) {
255 reset($this->records);
256 while (list($index, $record) = each($this->records)) {
257 //Save the record to the .shp file
258 $record->saveToFile($this->SHPFile, $this->DBFFile, $index + 1);
260 //Save the record to the .shx file
261 fwrite($this->SHXFile, pack("N", $offset));
262 fwrite($this->SHXFile, pack("N", $record->getContentLength()));
263 $offset += (4 + $record->getContentLength());
266 @dbase_pack($this->DBFFile);
269 function _openSHPFile($toWrite = false) {
270 $this->SHPFile = @fopen(str_replace('.*', '.shp', $this->FileName), ($toWrite ? "wb+" : "rb"));
271 if (!$this->SHPFile) {
272 return $this->setError(sprintf("It wasn't possible to open the Shape file '%s'", str_replace('.*', '.shp', $this->FileName)));
275 return TRUE;
278 function _closeSHPFile() {
279 if ($this->SHPFile) {
280 fclose($this->SHPFile);
281 $this->SHPFile = NULL;
285 function _openSHXFile($toWrite = false) {
286 $this->SHXFile = @fopen(str_replace('.*', '.shx', $this->FileName), ($toWrite ? "wb+" : "rb"));
287 if (!$this->SHXFile) {
288 return $this->setError(sprintf("It wasn't possible to open the Index file '%s'", str_replace('.*', '.shx', $this->FileName)));
291 return TRUE;
294 function _closeSHXFile() {
295 if ($this->SHXFile) {
296 fclose($this->SHXFile);
297 $this->SHXFile = NULL;
301 function _openDBFFile($toWrite = false) {
302 $checkFunction = $toWrite ? "is_writable" : "is_readable";
303 if (($toWrite) && (!file_exists(str_replace('.*', '.dbf', $this->FileName)))) {
304 if (!@dbase_create(str_replace('.*', '.dbf', $this->FileName), $this->DBFHeader)) {
305 return $this->setError(sprintf("It wasn't possible to create the DBase file '%s'", str_replace('.*', '.dbf', $this->FileName)));
308 if ($checkFunction(str_replace('.*', '.dbf', $this->FileName))) {
309 $this->DBFFile = dbase_open(str_replace('.*', '.dbf', $this->FileName), ($toWrite ? 2 : 0));
310 if (!$this->DBFFile) {
311 return $this->setError(sprintf("It wasn't possible to open the DBase file '%s'", str_replace('.*', '.dbf', $this->FileName)));
313 } else {
314 return $this->setError(sprintf("It wasn't possible to find the DBase file '%s'", str_replace('.*', '.dbf', $this->FileName)));
316 return TRUE;
319 function _closeDBFFile() {
320 if ($this->DBFFile) {
321 dbase_close($this->DBFFile);
322 $this->DBFFile = NULL;
326 function setError($error) {
327 $this->lastError = $error;
328 return false;
332 class ShapeRecord {
333 var $SHPFile = NULL;
334 var $DBFFile = NULL;
336 var $recordNumber = NULL;
337 var $shapeType = NULL;
339 var $lastError = "";
341 var $SHPData = array();
342 var $DBFData = array();
344 function ShapeRecord($shapeType) {
345 $this->shapeType = $shapeType;
348 function loadFromFile(&$SHPFile, &$DBFFile) {
349 $this->SHPFile = $SHPFile;
350 $this->DBFFile = $DBFFile;
351 $this->_loadHeaders();
353 switch ($this->shapeType) {
354 case 0:
355 $this->_loadNullRecord();
356 break;
357 case 1:
358 $this->_loadPointRecord();
359 break;
360 case 3:
361 $this->_loadPolyLineRecord();
362 break;
363 case 5:
364 $this->_loadPolygonRecord();
365 break;
366 case 8:
367 $this->_loadMultiPointRecord();
368 break;
369 default:
370 $this->setError(sprintf("The Shape Type '%s' is not supported.", $this->shapeType));
371 break;
373 $this->_loadDBFData();
376 function saveToFile(&$SHPFile, &$DBFFile, $recordNumber) {
377 $this->SHPFile = $SHPFile;
378 $this->DBFFile = $DBFFile;
379 $this->recordNumber = $recordNumber;
380 $this->_saveHeaders();
382 switch ($this->shapeType) {
383 case 0:
384 $this->_saveNullRecord();
385 break;
386 case 1:
387 $this->_savePointRecord();
388 break;
389 case 3:
390 $this->_savePolyLineRecord();
391 break;
392 case 5:
393 $this->_savePolygonRecord();
394 break;
395 case 8:
396 $this->_saveMultiPointRecord();
397 break;
398 default:
399 $this->setError(sprintf("The Shape Type '%s' is not supported.", $this->shapeType));
400 break;
402 $this->_saveDBFData();
405 function updateDBFInfo($header) {
406 $tmp = $this->DBFData;
407 unset($this->DBFData);
408 $this->DBFData = array();
409 reset($header);
410 while (list($key, $value) = each($header)) {
411 $this->DBFData[$value[0]] = (isset($tmp[$value[0]])) ? $tmp[$value[0]] : "";
415 function _loadHeaders() {
416 $this->recordNumber = loadData("N", fread($this->SHPFile, 4));
417 $tmp = loadData("N", fread($this->SHPFile, 4)); //We read the length of the record
418 $this->shapeType = loadData("V", fread($this->SHPFile, 4));
421 function _saveHeaders() {
422 fwrite($this->SHPFile, pack("N", $this->recordNumber));
423 fwrite($this->SHPFile, pack("N", $this->getContentLength()));
424 fwrite($this->SHPFile, pack("V", $this->shapeType));
427 function _loadPoint() {
428 $data = array();
430 $data["x"] = loadData("d", fread($this->SHPFile, 8));
431 $data["y"] = loadData("d", fread($this->SHPFile, 8));
433 return $data;
436 function _savePoint($data) {
437 fwrite($this->SHPFile, packDouble($data["x"]));
438 fwrite($this->SHPFile, packDouble($data["y"]));
441 function _loadNullRecord() {
442 $this->SHPData = array();
445 function _saveNullRecord() {
446 //Don't save anything
449 function _loadPointRecord() {
450 $this->SHPData = $this->_loadPoint();
453 function _savePointRecord() {
454 $this->_savePoint($this->SHPData);
457 function _loadMultiPointRecord() {
458 $this->SHPData = array();
459 $this->SHPData["xmin"] = loadData("d", fread($this->SHPFile, 8));
460 $this->SHPData["ymin"] = loadData("d", fread($this->SHPFile, 8));
461 $this->SHPData["xmax"] = loadData("d", fread($this->SHPFile, 8));
462 $this->SHPData["ymax"] = loadData("d", fread($this->SHPFile, 8));
464 $this->SHPData["numpoints"] = loadData("V", fread($this->SHPFile, 4));
466 for ($i = 0; $i <= $this->SHPData["numpoints"]; $i++) {
467 $this->SHPData["points"][] = $this->_loadPoint();
471 function _saveMultiPointRecord() {
472 fwrite($this->SHPFile, pack("dddd", $this->SHPData["xmin"], $this->SHPData["ymin"], $this->SHPData["xmax"], $this->SHPData["ymax"]));
474 fwrite($this->SHPFile, pack("V", $this->SHPData["numpoints"]));
476 for ($i = 0; $i <= $this->SHPData["numpoints"]; $i++) {
477 $this->_savePoint($this->SHPData["points"][$i]);
481 function _loadPolyLineRecord() {
482 $this->SHPData = array();
483 $this->SHPData["xmin"] = loadData("d", fread($this->SHPFile, 8));
484 $this->SHPData["ymin"] = loadData("d", fread($this->SHPFile, 8));
485 $this->SHPData["xmax"] = loadData("d", fread($this->SHPFile, 8));
486 $this->SHPData["ymax"] = loadData("d", fread($this->SHPFile, 8));
488 $this->SHPData["numparts"] = loadData("V", fread($this->SHPFile, 4));
489 $this->SHPData["numpoints"] = loadData("V", fread($this->SHPFile, 4));
491 for ($i = 0; $i < $this->SHPData["numparts"]; $i++) {
492 $this->SHPData["parts"][$i] = loadData("V", fread($this->SHPFile, 4));
495 $firstIndex = ftell($this->SHPFile);
496 $readPoints = 0;
497 reset($this->SHPData["parts"]);
498 while (list($partIndex, $partData) = each($this->SHPData["parts"])) {
499 if (!isset($this->SHPData["parts"][$partIndex]["points"]) || !is_array($this->SHPData["parts"][$partIndex]["points"])) {
500 $this->SHPData["parts"][$partIndex] = array();
501 $this->SHPData["parts"][$partIndex]["points"] = array();
503 while (!in_array($readPoints, $this->SHPData["parts"]) && ($readPoints < ($this->SHPData["numpoints"])) && !feof($this->SHPFile)) {
504 $this->SHPData["parts"][$partIndex]["points"][] = $this->_loadPoint();
505 $readPoints++;
509 fseek($this->SHPFile, $firstIndex + ($readPoints*16));
512 function _savePolyLineRecord() {
513 fwrite($this->SHPFile, pack("dddd", $this->SHPData["xmin"], $this->SHPData["ymin"], $this->SHPData["xmax"], $this->SHPData["ymax"]));
515 fwrite($this->SHPFile, pack("VV", $this->SHPData["numparts"], $this->SHPData["numpoints"]));
517 for ($i = 0; $i < $this->SHPData["numparts"]; $i++) {
518 fwrite($this->SHPFile, pack("V", count($this->SHPData["parts"][$i])));
521 reset($this->SHPData["parts"]);
522 foreach ($this->SHPData["parts"] as $partData){
523 reset($partData["points"]);
524 while (list($pointIndex, $pointData) = each($partData["points"])) {
525 $this->_savePoint($pointData);
530 function _loadPolygonRecord() {
531 $this->_loadPolyLineRecord();
534 function _savePolygonRecord() {
535 $this->_savePolyLineRecord();
538 function addPoint($point, $partIndex = 0) {
539 switch ($this->shapeType) {
540 case 0:
541 //Don't add anything
542 break;
543 case 1:
544 //Substitutes the value of the current point
545 $this->SHPData = $point;
546 break;
547 case 3:
548 case 5:
549 //Adds a new point to the selected part
550 if (!isset($this->SHPData["xmin"]) || ($this->SHPData["xmin"] > $point["x"])) $this->SHPData["xmin"] = $point["x"];
551 if (!isset($this->SHPData["ymin"]) || ($this->SHPData["ymin"] > $point["y"])) $this->SHPData["ymin"] = $point["y"];
552 if (!isset($this->SHPData["xmax"]) || ($this->SHPData["xmax"] < $point["x"])) $this->SHPData["xmax"] = $point["x"];
553 if (!isset($this->SHPData["ymax"]) || ($this->SHPData["ymax"] < $point["y"])) $this->SHPData["ymax"] = $point["y"];
555 $this->SHPData["parts"][$partIndex]["points"][] = $point;
557 $this->SHPData["numparts"] = count($this->SHPData["parts"]);
558 $this->SHPData["numpoints"]++;
559 break;
560 case 8:
561 //Adds a new point
562 if (!isset($this->SHPData["xmin"]) || ($this->SHPData["xmin"] > $point["x"])) $this->SHPData["xmin"] = $point["x"];
563 if (!isset($this->SHPData["ymin"]) || ($this->SHPData["ymin"] > $point["y"])) $this->SHPData["ymin"] = $point["y"];
564 if (!isset($this->SHPData["xmax"]) || ($this->SHPData["xmax"] < $point["x"])) $this->SHPData["xmax"] = $point["x"];
565 if (!isset($this->SHPData["ymax"]) || ($this->SHPData["ymax"] < $point["y"])) $this->SHPData["ymax"] = $point["y"];
567 $this->SHPData["points"][] = $point;
568 $this->SHPData["numpoints"]++;
569 break;
570 default:
571 $this->setError(sprintf("The Shape Type '%s' is not supported.", $this->shapeType));
572 break;
576 function deletePoint($pointIndex = 0, $partIndex = 0) {
577 switch ($this->shapeType) {
578 case 0:
579 //Don't delete anything
580 break;
581 case 1:
582 //Sets the value of the point to zero
583 $this->SHPData["x"] = 0.0;
584 $this->SHPData["y"] = 0.0;
585 break;
586 case 3:
587 case 5:
588 //Deletes the point from the selected part, if exists
589 if (isset($this->SHPData["parts"][$partIndex]) && isset($this->SHPData["parts"][$partIndex]["points"][$pointIndex])) {
590 for ($i = $pointIndex; $i < (count($this->SHPData["parts"][$partIndex]["points"]) - 1); $i++) {
591 $this->SHPData["parts"][$partIndex]["points"][$i] = $this->SHPData["parts"][$partIndex]["points"][$i + 1];
593 unset($this->SHPData["parts"][$partIndex]["points"][count($this->SHPData["parts"][$partIndex]["points"]) - 1]);
595 $this->SHPData["numparts"] = count($this->SHPData["parts"]);
596 $this->SHPData["numpoints"]--;
598 break;
599 case 8:
600 //Deletes the point, if exists
601 if (isset($this->SHPData["points"][$pointIndex])) {
602 for ($i = $pointIndex; $i < (count($this->SHPData["points"]) - 1); $i++) {
603 $this->SHPData["points"][$i] = $this->SHPData["points"][$i + 1];
605 unset($this->SHPData["points"][count($this->SHPData["points"]) - 1]);
607 $this->SHPData["numpoints"]--;
609 break;
610 default:
611 $this->setError(sprintf("The Shape Type '%s' is not supported.", $this->shapeType));
612 break;
616 function getContentLength() {
617 switch ($this->shapeType) {
618 case 0:
619 $result = 0;
620 break;
621 case 1:
622 $result = 10;
623 break;
624 case 3:
625 case 5:
626 $result = 22 + 2*count($this->SHPData["parts"]);
627 for ($i = 0; $i < count($this->SHPData["parts"]); $i++) {
628 $result += 8*count($this->SHPData["parts"][$i]["points"]);
630 break;
631 case 8:
632 $result = 20 + 8*count($this->SHPData["points"]);
633 break;
634 default:
635 $result = false;
636 $this->setError(sprintf("The Shape Type '%s' is not supported.", $this->shapeType));
637 break;
639 return $result;
642 function _loadDBFData() {
643 $this->DBFData = @dbase_get_record_with_names($this->DBFFile, $this->recordNumber);
644 unset($this->DBFData["deleted"]);
647 function _saveDBFData() {
648 unset($this->DBFData["deleted"]);
649 if ($this->recordNumber <= dbase_numrecords($this->DBFFile)) {
650 if (!dbase_replace_record($this->DBFFile, array_values($this->DBFData), $this->recordNumber)) {
651 $this->setError("I wasn't possible to update the information in the DBF file.");
653 } else {
654 if (!dbase_add_record($this->DBFFile, array_values($this->DBFData))) {
655 $this->setError("I wasn't possible to add the information to the DBF file.");
660 function setError($error) {
661 $this->lastError = $error;
662 return false;