Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.file / org.gvsig.fmap.dal.file.shp / src / main / java / org / gvsig / fmap / dal / store / shp / utils / SHPFile2.java @ 42811

History | View | Annotate | Download (20 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License as published by the Free Software
8
 * Foundation; either version 3 of the License, or (at your option) any later
9
 * version.
10
 *
11
 * This program is distributed in the hope that it will be useful, but WITHOUT
12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14
 * details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with
17
 * this program; if not, write to the Free Software Foundation, Inc., 51
18
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
 *
20
 * For any additional information, do not hesitate to contact us at info AT
21
 * gvsig.com, or visit our website www.gvsig.com.
22
 */
23
package org.gvsig.fmap.dal.store.shp.utils;
24

    
25
import java.io.BufferedReader;
26
import java.io.File;
27
import java.io.FileInputStream;
28
import java.io.FileReader;
29
import java.io.IOException;
30
import java.nio.ByteOrder;
31
import java.nio.channels.FileChannel;
32

    
33
import org.slf4j.Logger;
34
import org.slf4j.LoggerFactory;
35

    
36
import org.gvsig.fmap.dal.exception.CloseException;
37
import org.gvsig.fmap.dal.exception.DataException;
38
import org.gvsig.fmap.dal.exception.FileNotFoundException;
39
import org.gvsig.fmap.dal.exception.ReadException;
40
import org.gvsig.fmap.dal.store.shp.SHPStoreParameters;
41
import org.gvsig.fmap.geom.Geometry;
42
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
43
import org.gvsig.fmap.geom.Geometry.TYPES;
44
import org.gvsig.fmap.geom.GeometryLocator;
45
import org.gvsig.fmap.geom.GeometryManager;
46
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
47
import org.gvsig.fmap.geom.exception.CreateGeometryException;
48
import org.gvsig.fmap.geom.primitive.Envelope;
49
import org.gvsig.fmap.geom.primitive.Point;
50
import org.gvsig.fmap.geom.primitive.PointGeometryType;
51
import org.gvsig.fmap.geom.type.GeometryType;
52
import org.gvsig.fmap.geom.type.GeometryTypeNotSupportedException;
53
import org.gvsig.fmap.geom.type.GeometryTypeNotValidException;
54
import org.gvsig.tools.exception.BaseException;
55
import org.gvsig.utils.bigfile.BigByteBuffer2;
56

    
57
/**
58
 * @author jmvivo
59
 *
60
 */
61
public class SHPFile2 implements ISHPFile {
62

    
63
    private static final Logger logger = LoggerFactory.getLogger(SHPFile2.class);
64
    private Envelope extent;
65
    private int type;
66
    private int subType;
67
    private String srsParameters;
68

    
69
    private FileInputStream fin;
70
    private FileChannel channel;
71
    private BigByteBuffer2 bb;
72
    private FileInputStream finShx;
73
    private FileChannel channelShx;
74
    private BigByteBuffer2 bbShx;
75

    
76
    private SHPStoreParameters params;
77

    
78
    private int[] supportedGeometryTypes;
79
    private final GeometryManager gManager = GeometryLocator
80
        .getGeometryManager();
81

    
82
    private GeometryType gtypeNull;
83
    private PointGeometryType gtypePoint2D;
84
    private GeometryType gtypeCurve2D;
85
    private GeometryType gtypeSurface2D;
86
    private GeometryType gtypeMultiPoint2D;
87

    
88
    private boolean useNullGeometry = false;
89

    
90
    private SHPReader reader;
91

    
92
    public SHPFile2(SHPStoreParameters params) {
93
        this.params = params;
94
        try {
95
            gtypeNull = gManager.getGeometryType(TYPES.NULL, SUBTYPES.GEOM2D);
96
            gtypePoint2D
97
                    = (PointGeometryType) gManager.getGeometryType(TYPES.POINT,
98
                    SUBTYPES.GEOM2D);
99
            gtypeCurve2D
100
                    = gManager.getGeometryType(TYPES.CURVE, SUBTYPES.GEOM2D);
101
            gtypeSurface2D
102
                    = gManager.getGeometryType(TYPES.SURFACE, SUBTYPES.GEOM2D);
103
            gtypeMultiPoint2D
104
                    = gManager.getGeometryType(TYPES.MULTIPOINT, SUBTYPES.GEOM2D);
105

    
106
        } catch (GeometryTypeNotSupportedException e) {
107
            throw new RuntimeException(
108
                "Unable to get the 2D geometry types to use", e);
109
        } catch (GeometryTypeNotValidException e) {
110
            throw new RuntimeException(
111
                "Unable to get the 2D geometry types to use", e);
112
        }
113
    }
114

    
115
    public void setUseNullGeometry(boolean useNullGeometry) {
116
        this.useNullGeometry = useNullGeometry;
117
    }
118

    
119
    /*
120
     * (non-Javadoc)
121
     *
122
     * @see org.gvsig.fmap.dal.Resource#doClose()
123
     */
124
    public void close() throws CloseException {
125
        CloseException ret = null;
126

    
127
        logger.debug("Closing shp/shx file '"+this.params.getSHPFileName()+"'");
128

    
129
        // FIXME: Arreglar esto para que se acumulen los errores
130
        try {
131
            channel.close();
132
            channelShx.close();
133
        } catch (IOException e) {
134
            ret = new CloseException("SHPFile.close", e);
135
        } finally {
136
            try {
137
                fin.close();
138
                finShx.close();
139
            } catch (IOException e1) {
140
                ret = new CloseException("SHPFile.close", e1);
141
            }
142
        }
143

    
144
        if (ret != null) {
145
            throw ret;
146
        }
147
        bb = null;
148
        bbShx = null;
149
        fin = null;
150
        finShx = null;
151
        channel = null;
152
        channelShx = null;
153
        srsParameters = null;
154
    }
155

    
156
    public boolean isOpen() {
157
        return this.fin != null;
158
    }
159

    
160
    public synchronized void open() throws DataException {
161
        try {
162
            fin = new FileInputStream(this.params.getSHPFile());
163
        } catch (java.io.FileNotFoundException e) {
164
            throw new FileNotFoundException(this.params.getSHPFileName());
165
        }
166

    
167
        // Open the file and then get a channel from the stream
168
        channel = fin.getChannel();
169

    
170
        // long size = channel.size();
171
        // Get the file's size and then map it into memory
172
        // bb = channel.map(FileChannel.MapMode.READ_ONLY, 0, size);
173
        try {
174
            bb = new BigByteBuffer2(channel, FileChannel.MapMode.READ_ONLY);
175
        } catch (IOException e) {
176
            throw new ReadException(this.params.getSHPFileName(), e);
177

    
178
        }
179
        try {
180
            finShx = new FileInputStream(this.params.getSHXFile());
181
        } catch (java.io.FileNotFoundException e) {
182
            throw new FileNotFoundException(this.params.getSHXFileName());
183
        }
184

    
185
        // Open the file and then get a channel from the stream
186
        channelShx = finShx.getChannel();
187

    
188
        // long sizeShx = channelShx.size();
189
        // Get the file's size and then map it into memory
190
        // bb = channel.map(FileChannel.MapMode.READ_ONLY, 0, size);
191
        // bbShx = channelShx.map(FileChannel.MapMode.READ_ONLY, 0, sizeShx);
192
        try {
193
            bbShx
194
                    = new BigByteBuffer2(channelShx, FileChannel.MapMode.READ_ONLY);
195
        } catch (IOException e) {
196
            throw new ReadException(this.params.getSHXFileName(), e);
197

    
198
        }
199
        bbShx.order(ByteOrder.BIG_ENDIAN);
200

    
201
        // create a new header.
202
        ShapeFileHeader2 myHeader = new ShapeFileHeader2();
203

    
204
        bb.position(0);
205

    
206
        // read the header
207
        myHeader.readHeader(bb);
208

    
209
        double[] min = new double[2];
210
        min[0] = myHeader.myXmin;
211
        min[1] = myHeader.myYmin;
212
        double[] max = new double[2];
213
        max[0] = myHeader.myXmax;
214
        max[1] = myHeader.myYmax;
215

    
216
        try {
217
            extent =
218
                gManager.createEnvelope(min[0], min[1], max[0], max[1],
219
                    SUBTYPES.GEOM2D);
220
        } catch (CreateEnvelopeException e1) {
221
            logger.warn("Error creating the envelope", e1);
222
        }
223
        // extent = new Rectangle2D.Double(myHeader.myXmin, myHeader.myYmin,
224
        // myHeader.myXmax - myHeader.myXmin,
225
        // myHeader.myYmax - myHeader.myYmin);
226

    
227
        type = myHeader.myShapeType;
228
        subType = getGeometrySubType();
229

    
230
        switch (subType) {
231
        case SUBTYPES.GEOM2D:
232
            reader = new SHPReader2D(this.params);
233
            break;
234
        case SUBTYPES.GEOM2DM:
235
            reader = new SHPReader2DM(this.params);
236
            break;
237
        case SUBTYPES.GEOM3DM:
238
            reader = new SHPReader3DM(this.params);
239
            break;
240

    
241
        default:
242
            break;
243
        }
244

    
245

    
246
        this.initSupportedGeometryTypes();
247

    
248
        double x = myHeader.myXmin;
249
        double y = myHeader.myYmin;
250
        double w = myHeader.myXmax - myHeader.myXmin;
251
        double h = myHeader.myYmax - myHeader.myYmin;
252

    
253
        if (w == 0) {
254
            x -= 0.1;
255
            w = 0.2;
256
        }
257

    
258
        if (h == 0) {
259
            y -= 0.1;
260
            h = 0.2;
261
        }
262

    
263
        // TODO: SRS
264
        File prjFile = SHP.getPrjFile(this.params.getSHPFile());
265
        if (prjFile.exists()) {
266
            BufferedReader input = null;
267
            try {
268
                input = new BufferedReader(new FileReader(prjFile));
269
            } catch (java.io.FileNotFoundException e) {
270
                throw new FileNotFoundException(prjFile.getAbsolutePath());
271
            }
272

    
273
            try {
274
                this.srsParameters = input.readLine();
275
            } catch (IOException e) {
276
                throw new ReadException("SHPFile.open prj", e);
277
            } finally {
278
                try {
279
                    input.close();
280
                } catch (IOException e) {
281
                    // TODO ???
282
                }
283
            }
284

    
285
        } else {
286
            this.srsParameters = null;
287
        }
288
    }
289

    
290
    public Envelope getFullExtent() throws ReadException {
291
        return this.extent;
292
    }
293

    
294
    public boolean isEditable() {
295
        return this.params.getDBFFile().canWrite()
296
            && this.params.getSHPFile().canWrite()
297
            && this.params.getSHXFile().canWrite();
298
    }
299

    
300
    public int getGeometryType() throws ReadException {
301
        int auxType = 0;
302

    
303
        switch (type) {
304
        case (SHP.POINT2D):
305
        case (SHP.POINT3D):
306
        case (SHP.POINTM):
307
            auxType = auxType | Geometry.TYPES.POINT;
308

    
309
            break;
310

    
311
        case (SHP.POLYLINE2D):
312
        case (SHP.POLYLINE3D):
313
        case (SHP.POLYLINEM):
314
            auxType = auxType | Geometry.TYPES.MULTICURVE;
315

    
316
            break;
317

    
318
        case (SHP.POLYGON2D):
319
        case (SHP.POLYGON3D):
320
        case (SHP.POLYGONM):
321
            auxType = auxType | Geometry.TYPES.MULTISURFACE;
322

    
323
            break;
324
        case (SHP.MULTIPOINT2D):
325
        case (SHP.MULTIPOINT3D):
326
        case (SHP.MULTIPOINTM):
327
            auxType = auxType | Geometry.TYPES.MULTIPOINT;
328

    
329
            break;
330
        }
331

    
332
        return auxType;
333
    }
334

    
335
    public int getGeometrySubType() throws ReadException {
336
        switch (type) {
337
        case (SHP.POINT2D):
338
        case (SHP.POLYLINE2D):
339
        case (SHP.POLYGON2D):
340
        case (SHP.MULTIPOINT2D):
341
            return SUBTYPES.GEOM2D;
342
        case (SHP.POINT3D):
343
        case (SHP.POLYLINE3D):
344
        case (SHP.POLYGON3D):
345
        case (SHP.MULTIPOINT3D):
346
            return SUBTYPES.GEOM3DM;
347
        case (SHP.POINTM):
348
        case (SHP.POLYLINEM):
349
        case (SHP.POLYGONM):
350
        case (SHP.MULTIPOINTM):
351
            return SUBTYPES.GEOM2DM;
352
        }
353

    
354
        return SUBTYPES.UNKNOWN;
355
    }
356

    
357
    public Geometry getNullGeometry() throws CreateGeometryException {
358
        if (this.useNullGeometry) {
359
            return gtypeNull.create();
360
        }
361
        return null;
362
    }
363

    
364
    /**
365
     * Gets the geometry with the index provided. Set to synchronized to prevent
366
     * concurrent threads issue (?)
367
     *
368
     * @param position
369
     * @return
370
     * @throws ReadException
371
     * @throws CreateGeometryException
372
     */
373
    public synchronized Geometry getGeometry(long position)
374
        throws ReadException, CreateGeometryException {
375

    
376
        int shapeType;
377
        bb.position(getPositionForRecord(position));
378
        bb.order(ByteOrder.LITTLE_ENDIAN);
379
        shapeType = bb.getInt();
380

    
381
        if (shapeType == SHP.NULL) {
382
            return getNullGeometry();
383
        }
384

    
385
        /*
386
         * Inconsistency: this particular shape is not
387
         * of the expected type. This can be because the SHP file
388
         * is corrupt and it can cause "out of memory error"
389
         * because the code will possibly try to instantiate a
390
         * huge (absurd) array, so it's safer to return a null geometry
391
         */
392
        if (shapeType != type) {
393
            if( ! this.params.getAllowInconsistenciesInGeometryType() ) {
394
                throw new InconsistenciesInGeometryTypeException(shapeType);
395
            }
396
        }
397

    
398
        // retrieve that shape.
399
        switch (type) {
400
        case (SHP.POINT2D):
401
        case (SHP.POINT3D):
402
        case (SHP.POINTM):
403
            return reader.readPoint(bb);
404

    
405
        case (SHP.POLYLINE2D):
406
        case (SHP.POLYLINE3D):
407
        case (SHP.POLYLINEM):
408
            return reader.readPoLyline(bb);
409

    
410
        case (SHP.POLYGON2D):
411
        case (SHP.POLYGON3D):
412
        case (SHP.POLYGONM):
413
            try {
414
                return reader.readPoLygon(bb);
415
            } catch (BaseException e) {
416
                throw new CreateGeometryException(Geometry.TYPES.POLYGON, getGeometrySubType(), e);
417
            }
418

    
419
        case (SHP.MULTIPOINT2D):
420
        case (SHP.MULTIPOINTM):
421

    
422
        case (SHP.MULTIPOINT3D):
423
            return reader.readMultiPoint(bb);
424
        }
425
        return null;
426
    }
427
    
428
    private class InconsistenciesInGeometryTypeException extends ReadException {
429

    
430
        private final static String MESSAGE_FORMAT = "The geometry type  (%(geomTypeOfHeader)) of the shape file (%(fname)) does not match the type of the current geometry (%(geomTypeOfGeometry)).\nCheck 'Allow inconsistencies in geometry type' in the shape's properties of the add layer dialog to ignore it.";
431
        private final static String MESSAGE_KEY = "_InconsistenciesInGeometryTypeException";
432
        private static final long serialVersionUID = 8991813814092628916L;
433

    
434
        public InconsistenciesInGeometryTypeException(int geomTypeOfGeometry) {
435
            super(MESSAGE_FORMAT, null, MESSAGE_KEY, serialVersionUID);
436
            setValue("geomTypeOfHeader", SHP.getTypeName(type));
437
            setValue("geomTypeOfGeometry", SHP.getTypeName(geomTypeOfGeometry));
438
            setValue("fname", params.getSHPFile().getName());
439
            
440
        }
441
    }
442

    
443
    private long getPositionForRecord(long numRec) {
444
        // shx file has a 100 bytes header, then, records
445
        // 8 bytes length, one for each entity.
446
        // first 4 bytes are the offset
447
        // next 4 bytes are length
448

    
449
        int posIndex = 100 + ((int) numRec * 8);
450
        // bbShx.position(posIndex);
451
        long pos = 8 + 2 * bbShx.getInt(posIndex);
452

    
453
        return pos;
454
    }
455

    
456
    /**
457
     * Reads the Point from the shape file.
458
     *
459
     * @param in ByteBuffer.
460
     *
461
     * @return Point2D.
462
     * @throws ReadException
463
     * @throws CreateGeometryException
464
     */
465
    private Point readPoint(BigByteBuffer2 in)
466
        throws CreateGeometryException, ReadException {
467
        return readPoint(subType, in);
468
    }
469

    
470
    private Point readPoint(int subtype, BigByteBuffer2 in)
471
            throws CreateGeometryException, ReadException {
472
        // bytes 1 to 4 are the type and have already been read.
473
        // bytes 4 to 12 are the X coordinate
474
        in.order(ByteOrder.LITTLE_ENDIAN);
475
        double x = in.getDouble();
476
        double y = in.getDouble();
477
        Point p = gManager.createPoint(x, y, subtype);
478
        return p;
479
    }
480

    
481
    /**
482
     * Lee un rect?ngulo del fichero.
483
     *
484
     * @param in ByteBuffer.
485
     *
486
     * @return Rect?ngulo.
487
     * @throws CreateEnvelopeException
488
     *
489
     * @throws IOException
490
     */
491
    private Envelope readRectangle(BigByteBuffer2 in)
492
        throws CreateEnvelopeException {
493
        in.order(ByteOrder.LITTLE_ENDIAN);
494
        double x = in.getDouble();
495
        double y = in.getDouble();
496

    
497
        double x2 = in.getDouble();
498

    
499
        if (x2 - x == 0) {
500
            x2 += 0.2;
501
            x -= 0.1;
502
        }
503

    
504
        double y2 = in.getDouble();
505

    
506
        if (y2 - y == 0) {
507
            y2 += 0.2;
508
            y -= 0.1;
509
        }
510
        Envelope tempEnvelope
511
                = gManager.createEnvelope(x, y, x2, y2, SUBTYPES.GEOM2D);
512
        return tempEnvelope;
513
    }
514

    
515
    /**
516
     * Gets the geometry bbox with the index provided. Set to synchronized to
517
     * prevent concurrent threads issue (?)
518
     *
519
     * @param featureIndex
520
     * @return
521
     * @throws ReadException
522
     * @throws CreateEnvelopeException
523
     * @throws CreateGeometryException
524
     */
525
    public synchronized Envelope getBoundingBox(long featureIndex)
526
        throws ReadException, CreateEnvelopeException, CreateGeometryException {
527
        Point p;
528
        Envelope BoundingBox = null;
529
        try {
530
            bb.position(getPositionForRecord(featureIndex));
531
        } catch (Exception e) {
532
            throw new ReadException("getBondingBox (" + featureIndex + ")", e);
533
            // logger.error(" Shapefile is corrupted. Drawing aborted. ="+e+
534
            // "  "+"index = "+index);
535
        }
536
        bb.order(ByteOrder.LITTLE_ENDIAN);
537

    
538
        int tipoShape = bb.getInt();
539

    
540
        // AZABALA: si tipoShape viene con valores erroneos deja de funcionar
541
        // el metodo getShape(i)
542
        // if (tipoShape != SHP.NULL) {
543
        // type = tipoShape;
544
        // }
545
        // retrieve that shape.
546
        // tempRecord.setShape(readShape(tempShapeType, tempContentLength, in));
547
        switch (tipoShape) {
548
        case (SHP.POINT2D):
549
        case (SHP.POINT3D):
550
            p = readPoint(bb);
551
                BoundingBox
552
                        = gManager.createEnvelope(p.getX() - 0.1, p.getY() - 0.1,
553
                    p.getX() + 0.2, p.getY() + 0.2, SUBTYPES.GEOM2D);
554
            // new Rectangle2D.Double(p.getX() - 0.1,
555
            // p.getY() - 0.1, 0.2, 0.2);
556

    
557
            break;
558

    
559
        case (SHP.POLYLINE2D):
560
        case (SHP.POLYLINEM):
561
        case (SHP.POLYGON2D):
562
        case (SHP.POLYGONM):
563
        case (SHP.MULTIPOINT2D):
564
        case (SHP.MULTIPOINTM):
565
        case (SHP.POLYLINE3D):
566
        case (SHP.POLYGON3D):
567
        case (SHP.MULTIPOINT3D):
568

    
569
            // BoundingBox
570
            BoundingBox = readRectangle(bb);
571

    
572
            break;
573
        }
574

    
575
        return BoundingBox;
576
    }
577

    
578
    /**
579
     * @return
580
     */
581
    public String getSRSParameters() {
582
        return this.srsParameters;
583
    }
584

    
585
    private void initSupportedGeometryTypes() throws ReadException {
586
        switch (this.getGeometryType()) {
587
        case Geometry.TYPES.POINT:
588
                supportedGeometryTypes
589
                        = new int[]{Geometry.TYPES.POINT, Geometry.TYPES.NULL};
590
            break;
591
        case Geometry.TYPES.MULTIPOINT:
592
                supportedGeometryTypes
593
                        = new int[]{Geometry.TYPES.MULTIPOINT, Geometry.TYPES.NULL};
594
            break;
595
        case Geometry.TYPES.MULTICURVE:
596
                supportedGeometryTypes
597
                        = new int[]{Geometry.TYPES.CURVE, Geometry.TYPES.ELLIPSE,
598
                    Geometry.TYPES.ARC, Geometry.TYPES.CIRCLE,
599
                    Geometry.TYPES.SURFACE, Geometry.TYPES.NULL,
600
                            Geometry.TYPES.MULTICURVE};
601
            break;
602
        case Geometry.TYPES.MULTISURFACE:
603
                supportedGeometryTypes
604
                        = new int[]{Geometry.TYPES.ELLIPSE, Geometry.TYPES.CIRCLE,
605
                    Geometry.TYPES.SURFACE, Geometry.TYPES.NULL,
606
                            Geometry.TYPES.MULTISURFACE};
607
            break;
608

    
609
        default:
610
                supportedGeometryTypes = new int[]{};
611
        }
612
    }
613

    
614
    public boolean canWriteGeometry(int gvSIGgeometryType) {
615
        for (int i = 0; i < supportedGeometryTypes.length; i++) {
616
            if (gvSIGgeometryType == supportedGeometryTypes[i]) {
617
                return true;
618
            }
619
        }
620
        return false;
621
    }
622
}