Completed
Push — master ( 17dfa5...3a9b5c )
by Michal
03:31
created

ShapeFile::addRecord()   F

Complexity

Conditions 25
Paths 800

Size

Total Lines 43
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 2 Features 0
Metric Value
c 4
b 2
f 0
dl 0
loc 43
rs 2.771
cc 25
eloc 25
nc 800
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * phpMyAdmin ShapeFile library
4
 * <https://github.com/phpmyadmin/shapefile/>
5
 *
6
 * Copyright 2006-2007 Ovidio <ovidio AT users.sourceforge.net>
7
 * Copyright 2016 Michal Čihař <[email protected]>
8
 *
9
 * This program is free software; you can redistribute it and/or
10
 * modify it under the terms of the GNU General Public License
11
 * as published by the Free Software Foundation.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, you can download one from
20
 * http://www.gnu.org/copyleft/gpl.html.
21
 */
22
namespace ShapeFile;
23
24
/**
25
 * ShapeFile class
26
 *
27
 * @package bfShapeFiles
28
 */
29
class ShapeFile {
30
    private $FileName;
31
32
    private $SHPFile;
33
    private $SHXFile;
34
    private $DBFFile;
35
36
    private $DBFHeader;
37
38
    public $lastError = '';
39
40
    private $boundingBox = array('xmin' => 0.0, 'ymin' => 0.0, 'xmax' => 0.0, 'ymax' => 0.0);
41
    private $fileLength = 0;
42
    private $shapeType = 0;
43
44
    public $records;
45
46
    /**
47
     * @param integer $shapeType
48
     */
49
    public function __construct($shapeType, $boundingBox = array('xmin' => 0.0, 'ymin' => 0.0, 'xmax' => 0.0, 'ymax' => 0.0), $FileName = null) {
50
        $this->shapeType = $shapeType;
51
        $this->boundingBox = $boundingBox;
52
        $this->FileName = $FileName;
53
        $this->fileLength = 50; // The value for file length is the total length of the file in 16-bit words (including the fifty 16-bit words that make up the header).
54
    }
55
56
    /**
57
     * @param string $FileName
58
     */
59
    public function loadFromFile($FileName) {
60
        $this->FileName = $FileName;
61
62
        if (($this->_openSHPFile()) && ($this->_openDBFFile())) {
63
            $this->_loadHeaders();
64
            $this->_loadRecords();
65
            $this->_closeSHPFile();
66
            $this->_closeDBFFile();
67
        } else {
68
            return false;
69
        }
70
    }
71
72
    /**
73
     * @param string|null $FileName Name of file to open
74
     */
75
    public function saveToFile($FileName = null) {
76
        if (! is_null($FileName)) {
77
            $this->FileName = $FileName;
78
        }
79
80
        if (($this->_openSHPFile(true)) && ($this->_openSHXFile(true)) && ($this->_openDBFFile(true))) {
81
            $this->_saveHeaders();
82
            $this->_saveRecords();
83
            $this->_closeSHPFile();
84
            $this->_closeSHXFile();
85
            $this->_closeDBFFile();
86
        } else {
87
            return false;
88
        }
89
    }
90
91
    /**
92
     * Generates filename with given extension
93
     *
94
     * @param string $extension Extension to use (including dot)
95
     *
96
     * @return string
97
     */
98
    private function _getFilename($extension)
99
    {
100
        return str_replace('.*', $extension, $this->FileName);
101
    }
102
103
    /**
104
     * @param ShapeRecord $record
105
     */
106
    public function addRecord($record) {
107
        if ((isset($this->DBFHeader)) && (is_array($this->DBFHeader))) {
108
            $record->updateDBFInfo($this->DBFHeader);
109
        }
110
111
        $this->fileLength += ($record->getContentLength() + 4);
112
        $this->records[] = $record;
113
        $this->records[count($this->records) - 1]->recordNumber = count($this->records);
114
115
        if ($this->boundingBox['xmin'] == 0.0 || ($this->boundingBox['xmin'] > $record->SHPData['xmin'])) {
116
            $this->boundingBox['xmin'] = $record->SHPData['xmin'];
117
        }
118
        if ($this->boundingBox['xmax'] == 0.0 || ($this->boundingBox['xmax'] < $record->SHPData['xmax'])) {
119
            $this->boundingBox['xmax'] = $record->SHPData['xmax'];
120
        }
121
122
        if ($this->boundingBox['ymin'] == 0.0 || ($this->boundingBox['ymin'] > $record->SHPData['ymin'])) {
123
            $this->boundingBox['ymin'] = $record->SHPData['ymin'];
124
        }
125
        if ($this->boundingBox['ymax'] == 0.0 || ($this->boundingBox['ymax'] < $record->SHPData['ymax'])) {
126
            $this->boundingBox['ymax'] = $record->SHPData['ymax'];
127
        }
128
129
        if (in_array($this->shapeType, array(11, 13, 15, 18, 21, 23, 25, 28))) {
130
            if (!isset($this->boundingBox['mmin']) || $this->boundingBox['mmin'] == 0.0 || ($this->boundingBox['mmin'] > $record->SHPData['mmin'])) {
131
                $this->boundingBox['mmin'] = $record->SHPData['mmin'];
132
            }
133
            if (!isset($this->boundingBox['mmax']) || $this->boundingBox['mmax'] == 0.0 || ($this->boundingBox['mmax'] < $record->SHPData['mmax'])) {
134
                $this->boundingBox['mmax'] = $record->SHPData['mmax'];
135
            }
136
        }
137
138
        if (in_array($this->shapeType, array(11, 13, 15, 18))) {
139
            if (!isset($this->boundingBox['zmin']) || $this->boundingBox['zmin'] == 0.0 || ($this->boundingBox['zmin'] > $record->SHPData['zmin'])) {
140
                $this->boundingBox['zmin'] = $record->SHPData['zmin'];
141
            }
142
            if (!isset($this->boundingBox['zmax']) || $this->boundingBox['zmax'] == 0.0 || ($this->boundingBox['zmax'] < $record->SHPData['zmax'])) {
143
                $this->boundingBox['zmax'] = $record->SHPData['zmax'];
144
            }
145
        }
146
147
        return (count($this->records) - 1);
148
    }
149
150
    public function deleteRecord($index) {
151
        if (isset($this->records[$index])) {
152
            $this->fileLength -= ($this->records[$index]->getContentLength() + 4);
153
            $count = count($this->records) - 1;
154
            for ($i = $index; $i < $count; $i++) {
155
                $this->records[$i] = $this->records[$i + 1];
156
            }
157
            unset($this->records[count($this->records) - 1]);
158
            $this->_deleteRecordFromDBF($index);
159
        }
160
    }
161
162
    public function getDBFHeader() {
163
        return $this->DBFHeader;
164
    }
165
166
    public function setDBFHeader($header) {
167
        $this->DBFHeader = $header;
168
169
        $count = count($this->records);
170
        for ($i = 0; $i < $count; $i++) {
171
            $this->records[$i]->updateDBFInfo($header);
172
        }
173
    }
174
175
    public function getIndexFromDBFData($field, $value) {
176
        $result = -1;
177
        $count = count($this->records) - 1;
178
        for ($i = 0; $i < $count; $i++) {
179
            if (isset($this->records[$i]->DBFData[$field]) && (strtoupper($this->records[$i]->DBFData[$field]) == strtoupper($value))) {
180
                $result = $i;
181
            }
182
        }
183
184
        return $result;
185
    }
186
187
    private function _loadDBFHeader() {
188
        $DBFFile = fopen($this->_getFilename('.dbf'), 'r');
189
190
        $result = array();
191
        $i = 1;
192
        $inHeader = true;
193
194
        while ($inHeader) {
195
            if (!feof($DBFFile)) {
196
                $buff32 = fread($DBFFile, 32);
197
                if ($i > 1) {
198
                    if (substr($buff32, 0, 1) == chr(13)) {
199
                        $inHeader = false;
200
                    } else {
201
                        $pos = strpos(substr($buff32, 0, 10), chr(0));
202
                        $pos = ($pos == 0 ? 10 : $pos);
203
204
                        $fieldName = substr($buff32, 0, $pos);
205
                        $fieldType = substr($buff32, 11, 1);
206
                        $fieldLen = ord(substr($buff32, 16, 1));
207
                        $fieldDec = ord(substr($buff32, 17, 1));
208
209
                        array_push($result, array($fieldName, $fieldType, $fieldLen, $fieldDec));
210
                    }
211
                }
212
                $i++;
213
            } else {
214
                $inHeader = false;
215
            }
216
        }
217
218
        fclose($DBFFile);
219
        return($result);
220
    }
221
222
    private function _deleteRecordFromDBF($index) {
223
        if (@dbase_delete_record($this->DBFFile, $index)) {
224
            @dbase_pack($this->DBFFile);
225
        }
226
    }
227
228
    private function _loadHeaders() {
229
        fseek($this->SHPFile, 24, SEEK_SET);
230
        $this->fileLength = Util::loadData('N', fread($this->SHPFile, 4));
231
232
        fseek($this->SHPFile, 32, SEEK_SET);
233
        $this->shapeType = Util::loadData('V', fread($this->SHPFile, 4));
234
235
        $this->boundingBox = array();
236
        $this->boundingBox['xmin'] = Util::loadData('d', fread($this->SHPFile, 8));
237
        $this->boundingBox['ymin'] = Util::loadData('d', fread($this->SHPFile, 8));
238
        $this->boundingBox['xmax'] = Util::loadData('d', fread($this->SHPFile, 8));
239
        $this->boundingBox['ymax'] = Util::loadData('d', fread($this->SHPFile, 8));
240
        $this->boundingBox['zmin'] = Util::loadData('d', fread($this->SHPFile, 8));
241
        $this->boundingBox['zmax'] = Util::loadData('d', fread($this->SHPFile, 8));
242
        $this->boundingBox['mmin'] = Util::loadData('d', fread($this->SHPFile, 8));
243
        $this->boundingBox['mmax'] = Util::loadData('d', fread($this->SHPFile, 8));
244
245
        $this->DBFHeader = $this->_loadDBFHeader();
246
    }
247
248
    private function _saveHeaders() {
249
        fwrite($this->SHPFile, pack('NNNNNN', 9994, 0, 0, 0, 0, 0));
250
        fwrite($this->SHPFile, pack('N', $this->fileLength));
251
        fwrite($this->SHPFile, pack('V', 1000));
252
        fwrite($this->SHPFile, pack('V', $this->shapeType));
253
        fwrite($this->SHPFile, Util::packDouble($this->boundingBox['xmin']));
254
        fwrite($this->SHPFile, Util::packDouble($this->boundingBox['ymin']));
255
        fwrite($this->SHPFile, Util::packDouble($this->boundingBox['xmax']));
256
        fwrite($this->SHPFile, Util::packDouble($this->boundingBox['ymax']));
257
        fwrite($this->SHPFile, Util::packDouble(isset($this->boundingBox['zmin']) ? $this->boundingBox['zmin'] : 0));
258
        fwrite($this->SHPFile, Util::packDouble(isset($this->boundingBox['zmax']) ? $this->boundingBox['zmax'] : 0));
259
        fwrite($this->SHPFile, Util::packDouble(isset($this->boundingBox['mmin']) ? $this->boundingBox['mmin'] : 0));
260
        fwrite($this->SHPFile, Util::packDouble(isset($this->boundingBox['mmax']) ? $this->boundingBox['mmax'] : 0));
261
262
        fwrite($this->SHXFile, pack('NNNNNN', 9994, 0, 0, 0, 0, 0));
263
        fwrite($this->SHXFile, pack('N', 50 + 4 * count($this->records)));
264
        fwrite($this->SHXFile, pack('V', 1000));
265
        fwrite($this->SHXFile, pack('V', $this->shapeType));
266
        fwrite($this->SHXFile, Util::packDouble($this->boundingBox['xmin']));
267
        fwrite($this->SHXFile, Util::packDouble($this->boundingBox['ymin']));
268
        fwrite($this->SHXFile, Util::packDouble($this->boundingBox['xmax']));
269
        fwrite($this->SHXFile, Util::packDouble($this->boundingBox['ymax']));
270
        fwrite($this->SHXFile, Util::packDouble(isset($this->boundingBox['zmin']) ? $this->boundingBox['zmin'] : 0));
271
        fwrite($this->SHXFile, Util::packDouble(isset($this->boundingBox['zmax']) ? $this->boundingBox['zmax'] : 0));
272
        fwrite($this->SHXFile, Util::packDouble(isset($this->boundingBox['mmin']) ? $this->boundingBox['mmin'] : 0));
273
        fwrite($this->SHXFile, Util::packDouble(isset($this->boundingBox['mmax']) ? $this->boundingBox['mmax'] : 0));
274
    }
275
276
    private function _loadRecords() {
277
        fseek($this->SHPFile, 100);
278
        while (!feof($this->SHPFile)) {
279
            $bByte = ftell($this->SHPFile);
280
            $record = new ShapeRecord(-1);
281
            $record->loadFromFile($this->SHPFile, $this->DBFFile);
282
            $eByte = ftell($this->SHPFile);
283
            if (($eByte <= $bByte) || ($record->lastError != '')) {
284
                return false;
285
            }
286
287
            $this->records[] = $record;
288
        }
289
    }
290
291
    private function _saveRecords() {
292
        $dbf_name = $this->_getFilename('.dbf');
293
        if (file_exists($dbf_name)) {
294
            @unlink($dbf_name);
295
        }
296
        if (!($this->DBFFile = @dbase_create($dbf_name, $this->DBFHeader))) {
297
            return $this->setError(sprintf('It wasn\'t possible to create the DBase file '%s'', $dbf_name));
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_CONSTANT_ENCAPSED_STRING, expecting ',' or ')'
Loading history...
298
        }
299
300
        $offset = 50;
301
        if (is_array($this->records) && (count($this->records) > 0)) {
302
            foreach ($this->records as $index => $record) {
303
                //Save the record to the .shp file
304
                $record->saveToFile($this->SHPFile, $this->DBFFile, $index + 1);
305
306
                //Save the record to the .shx file
307
                fwrite($this->SHXFile, pack('N', $offset));
308
                fwrite($this->SHXFile, pack('N', $record->getContentLength()));
309
                $offset += (4 + $record->getContentLength());
310
            }
311
        }
312
        @dbase_pack($this->DBFFile);
313
    }
314
315
    private function _openSHPFile($toWrite = false) {
316
        $shp_name = $this->_getFilename('.shp');
317
        $this->SHPFile = @fopen($shp_name, ($toWrite ? 'wb+' : 'rb'));
318
        if (!$this->SHPFile) {
319
            return $this->setError(sprintf('It wasn\'t possible to open the Shape file '%s'', $shp_name));
320
        }
321
322
        return true;
323
    }
324
325
    private function _closeSHPFile() {
326
        if ($this->SHPFile) {
327
            fclose($this->SHPFile);
328
            $this->SHPFile = null;
329
        }
330
    }
331
332
    private function _openSHXFile($toWrite = false) {
333
        $shx_name = $this->_getFilename('.shx');
334
        $this->SHXFile = @fopen($shx_name, ($toWrite ? 'wb+' : 'rb'));
335
        if (!$this->SHXFile) {
336
            return $this->setError(sprintf('It wasn\'t possible to open the Index file '%s'', $shx_name));
337
        }
338
339
        return true;
340
    }
341
342
    private function _closeSHXFile() {
343
        if ($this->SHXFile) {
344
            fclose($this->SHXFile);
345
            $this->SHXFile = null;
346
        }
347
    }
348
349
    private function _openDBFFile($toWrite = false) {
350
        $dbf_name = $this->_getFilename('.dbf');
351
        $checkFunction = $toWrite ? 'is_writable' : 'is_readable';
352
        if (($toWrite) && (!file_exists($dbf_name))) {
353
            if (!@dbase_create($dbf_name, $this->DBFHeader)) {
354
                return $this->setError(sprintf('It wasn\'t possible to create the DBase file '%s'', $dbf_name));
355
            }
356
        }
357
        if ($checkFunction($dbf_name)) {
358
            $this->DBFFile = @dbase_open($dbf_name, ($toWrite ? 2 : 0));
359
            if (!$this->DBFFile) {
360
                return $this->setError(sprintf('It wasn\'t possible to open the DBase file '%s'', $dbf_name));
361
            }
362
        } else {
363
            return $this->setError(sprintf('It wasn\'t possible to find the DBase file '%s'', $dbf_name));
364
        }
365
        return true;
366
    }
367
368
    private function _closeDBFFile() {
369
        if ($this->DBFFile) {
370
            dbase_close($this->DBFFile);
371
            $this->DBFFile = null;
372
        }
373
    }
374
375
    /**
376
     * @param string $error
377
     */
378
    public function setError($error) {
379
        $this->lastError = $error;
380
        return false;
381
    }
382
}
383
384