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 / SHPStoreProvider.java @ 41837

History | View | Annotate | Download (21 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;
24

    
25
import java.io.File;
26
import java.io.IOException;
27
import java.util.Iterator;
28

    
29
import org.apache.commons.io.FileUtils;
30
import org.cresques.cts.IProjection;
31
import org.slf4j.Logger;
32
import org.slf4j.LoggerFactory;
33

    
34
import org.gvsig.fmap.dal.DataStore;
35
import org.gvsig.fmap.dal.DataTypes;
36
import org.gvsig.fmap.dal.FileHelper;
37
import org.gvsig.fmap.dal.exception.CloseException;
38
import org.gvsig.fmap.dal.exception.DataException;
39
import org.gvsig.fmap.dal.exception.InitializeException;
40
import org.gvsig.fmap.dal.exception.OpenException;
41
import org.gvsig.fmap.dal.exception.ReadException;
42
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
43
import org.gvsig.fmap.dal.feature.EditableFeatureType;
44
import org.gvsig.fmap.dal.feature.Feature;
45
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
46
import org.gvsig.fmap.dal.feature.FeatureSet;
47
import org.gvsig.fmap.dal.feature.FeatureStore;
48
import org.gvsig.fmap.dal.feature.FeatureType;
49
import org.gvsig.fmap.dal.feature.exception.PerformEditingException;
50
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
51
import org.gvsig.fmap.dal.resource.ResourceAction;
52
import org.gvsig.fmap.dal.resource.exception.ResourceException;
53
import org.gvsig.fmap.dal.resource.exception.ResourceExecuteException;
54
import org.gvsig.fmap.dal.resource.exception.ResourceNotifyChangesException;
55
import org.gvsig.fmap.dal.resource.exception.ResourceNotifyCloseException;
56
import org.gvsig.fmap.dal.resource.exception.ResourceNotifyOpenException;
57
import org.gvsig.fmap.dal.resource.file.FileResource;
58
import org.gvsig.fmap.dal.resource.spi.MultiResource;
59
import org.gvsig.fmap.dal.resource.spi.ResourceProvider;
60
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
61
import org.gvsig.fmap.dal.store.dbf.DBFStoreParameters;
62
import org.gvsig.fmap.dal.store.dbf.DBFStoreProvider;
63
import org.gvsig.fmap.dal.store.shp.utils.ISHPFile;
64
import org.gvsig.fmap.dal.store.shp.utils.SHPFile2;
65
import org.gvsig.fmap.geom.Geometry;
66
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
67
import org.gvsig.fmap.geom.GeometryLocator;
68
import org.gvsig.fmap.geom.GeometryManager;
69
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
70
import org.gvsig.fmap.geom.exception.CreateGeometryException;
71
import org.gvsig.fmap.geom.primitive.Envelope;
72
import org.gvsig.tools.dispose.DisposableIterator;
73
import org.gvsig.tools.dynobject.exception.DynFieldNotFoundException;
74
import org.gvsig.tools.exception.BaseException;
75

    
76
public class SHPStoreProvider extends DBFStoreProvider {
77

    
78
    private static final GeometryManager geomManager = GeometryLocator.getGeometryManager();
79
    private static final Logger logger = LoggerFactory.getLogger(GeometryManager.class);
80
    public static String NAME = "Shape";
81
    public static String DESCRIPTION = "Shape file";
82
    private ISHPFile shpFile;
83

    
84
    private MultiResource resource;
85

    
86
    protected static final String GEOMETRY_ATTIBUTE_NAME = "GEOMETRY";
87

    
88
    public static final String METADATA_DEFINITION_NAME = NAME;
89

    
90
    private SHPFeatureWriter writer = null;
91

    
92
    public SHPStoreProvider(SHPStoreParameters params,
93
            DataStoreProviderServices storeServices)
94
            throws InitializeException {
95
        super(
96
                params,
97
                storeServices,
98
                FileHelper.newMetadataContainer(METADATA_DEFINITION_NAME)
99
        );
100
    }
101

    
102
    protected void init(DBFStoreParameters params,
103
            DataStoreProviderServices storeServices) throws InitializeException {
104

    
105
        this.shpFile = new SHPFile2((SHPStoreParameters) params);
106
        super.init(params, storeServices);
107
        this.shpFile.setUseNullGeometry(this.getShpParameters().getUseNullGeometry());
108
    }
109

    
110
    public Object getDynValue(String name) throws DynFieldNotFoundException {
111
        if (DataStore.METADATA_CRS.equalsIgnoreCase(name)) {
112

    
113
            /*
114
             * String srs =  this.shpFile.getSRSParameters();
115
             if (srs != null){
116
             // This can be non null but not sure how to handle
117
             }
118
             */
119
            return this.getShpParameters().getCRS();
120

    
121
        } else if (DataStore.METADATA_ENVELOPE.equalsIgnoreCase(name)) {
122
            try {
123
                return this.shpFile.getFullExtent();
124
            } catch (ReadException e) {
125
                return null;
126
            }
127
        }
128
        return super.getDynValue(name);
129
    }
130

    
131
    protected void initResource(DBFStoreParameters params,
132
            DataStoreProviderServices storeServices) throws InitializeException {
133

    
134
        SHPStoreParameters shpParams = (SHPStoreParameters) params;
135
        resource
136
                = (MultiResource) createResource(MultiResource.TYPE_NAME,
137
                        new Object[]{shpParams.getSHPFileName()});
138

    
139
        resource.addResource(FileResource.NAME,
140
                new Object[]{shpParams.getSHPFileName()}, true);
141
        resource.addResource(FileResource.NAME,
142
                new Object[]{shpParams.getSHXFileName()}, true);
143
        resource.addResource(FileResource.NAME,
144
                new Object[]{shpParams.getDBFFileName()}, true);
145

    
146
        resource.addConsumer(this);
147
    }
148

    
149
    ;
150

    
151
        public ResourceProvider getResource() {
152
        return resource;
153
    }
154

    
155
    /**
156
     *
157
     * @throws ResourceNotifyChangesException
158
     */
159
    protected void resourcesNotifyChanges()
160
            throws ResourceNotifyChangesException {
161
                // super.resourcesNotifyChanges();
162
        // this.shpResource.notifyChanges();
163
        // this.shxResource.notifyChanges();
164
        getResource().notifyChanges();
165
        // TODO .prj
166

    
167
    }
168

    
169
    /**
170
     * @throws ResourceNotifyCloseException
171
     *
172
     */
173
    protected void resourcesNotifyClose() throws ResourceNotifyCloseException {
174
//                super.resourcesNotifyClose();
175
//                this.shpResource.notifyClose();
176
//                this.shxResource.notifyClose();
177
        getResource().notifyClose();
178
        // TODO .prj
179

    
180
    }
181

    
182
    @Override
183
    protected void doDispose() throws BaseException {
184
        super.doDispose();
185
        getResource().removeConsumer(this);
186
        this.writer = null;
187
        this.shpFile = null;
188
    }
189

    
190
    protected void disposeResource() {
191
        getResource().removeConsumer(this);
192
    }
193

    
194
    /**
195
     * @throws ResourceNotifyOpenException
196
     *
197
     */
198
    protected void resourcesOpen() throws ResourceNotifyOpenException {
199
                // super.resourcesOpen();
200
        // this.shpResource.notifyOpen();
201
        // this.shxResource.notifyOpen();
202
        getResource().notifyOpen();
203
    }
204

    
205
    protected static EditableFeatureAttributeDescriptor addGeometryColumn(
206
            EditableFeatureType fType) {
207

    
208
        EditableFeatureAttributeDescriptor attrTmp = null;
209
        EditableFeatureAttributeDescriptor attr = null;
210
        Iterator iter = fType.iterator();
211
        while (iter.hasNext()) {
212
            attrTmp = (EditableFeatureAttributeDescriptor) iter.next();
213
            if (attrTmp.getType() == DataTypes.GEOMETRY) {
214
                if (attr != null) {
215
                    // Two geom fields not allowed
216
                    fType.remove(attrTmp.getName());
217
                } else {
218
                    attr = attrTmp;
219
                    // attr.setName(GEOMETRY_ATTIBUTE_NAME);
220
                }
221
            }
222
        }
223

    
224
        if (attr == null) {
225
            String geofield = createGeometryFieldName(fType);
226
            attr = fType.add(geofield, DataTypes.GEOMETRY);
227
            attr.setDefaultValue(null);
228
        }
229

    
230
        attr.setObjectClass(Geometry.class);
231
        fType.setDefaultGeometryAttributeName(attr.getName());
232
        return attr;
233

    
234
    }
235

    
236
    private static String createGeometryFieldName(FeatureType ft) {
237

    
238
        if (ft.getAttributeDescriptor(GEOMETRY_ATTIBUTE_NAME) == null) {
239
            return GEOMETRY_ATTIBUTE_NAME;
240
        }
241

    
242
        int i = 0;
243
        String candidate = GEOMETRY_ATTIBUTE_NAME + i;
244
        while (ft.getAttributeDescriptor(candidate) != null) {
245
            i++;
246
            candidate = GEOMETRY_ATTIBUTE_NAME + i;
247
        }
248
        return candidate;
249
    }
250

    
251
    protected static FeatureType removeGeometryColumn(
252
            EditableFeatureType fType) {
253
        Iterator iter = fType.iterator();
254
        FeatureAttributeDescriptor attr;
255
        while (iter.hasNext()) {
256
            attr = (FeatureAttributeDescriptor) iter.next();
257
            if (attr.getType() == DataTypes.GEOMETRY) {
258
                iter.remove();
259
            }
260
        }
261
        fType.setDefaultGeometryAttributeName(null);
262
        return fType.getNotEditableCopy();
263
    }
264

    
265
    protected EditableFeatureType getTheFeatureType()
266
            throws InitializeException, OpenException {
267
        final EditableFeatureType fType = super.getTheFeatureType();
268
        this.open();
269
                // try {
270
        // this.resourcesBegin();
271
        // } catch (DataException e) {
272
        // throw new InitializeException(this.getName(), e);
273
        // }
274
        try {
275
            getResource().execute(new ResourceAction() {
276
                public Object run() throws Exception {
277
                    EditableFeatureAttributeDescriptor attr
278
                            = addGeometryColumn(fType);
279
                    attr.setGeometryType(shpFile.getGeometryType());
280
                    attr.setGeometrySubType(shpFile.getGeometrySubType());
281

    
282
                    IProjection srs = getShpParameters().getCRS();
283
                    attr.setSRS(srs);
284

    
285
                    return null;
286
                }
287
            });
288
            return fType;
289
        } catch (ResourceExecuteException e) {
290
            throw new InitializeException(e);
291
                        // } finally {
292
            // this.resourcesEnd();
293
        }
294
    }
295

    
296
//        private String getSRSFromPrj(String srsParameters) {
297
//                // TODO identificar que SRS hay que usar, ya sea
298
//                // el que se recibe de los parametros o el que
299
//                // conicida con el que se ha encontrado en el
300
//                // prg... y si ninguna de las dos que?
301
//                return null;
302
//        }
303
    protected SHPStoreParameters getShpParameters() {
304
        return (SHPStoreParameters) getParameters();
305
    }
306

    
307
    public String getProviderName() {
308
        return NAME;
309
    }
310

    
311
    public boolean allowWrite() {
312
        return this.shpFile.isEditable();
313
    }
314

    
315
    /**
316
     *
317
     * @param index
318
     * @param featureType
319
     * @return
320
     * @throws ReadException
321
     */
322
    protected FeatureProvider getFeatureProviderByIndex(long index,
323
            FeatureType featureType) throws DataException {
324
                // this.open();
325
        // this.resourcesBegin();
326
        try {
327

    
328
            FeatureProvider featureProvider = super.getFeatureProviderByIndex(index,
329
                    featureType);
330
            featureProvider.setDefaultEnvelope(this.shpFile.getBoundingBox(index));
331
            return featureProvider;
332
        } catch (DataException e) {
333
            throw e;
334
        } catch (CreateEnvelopeException e) {
335
            throw new org.gvsig.fmap.dal.feature.exception.CreateGeometryException(e);
336
        } catch (CreateGeometryException e) {
337
            throw new org.gvsig.fmap.dal.feature.exception.CreateGeometryException(e);
338
        }
339

    
340
    }
341

    
342
    protected void initFeatureProviderByIndex(FeatureProvider featureProvider,
343
            long index, FeatureType featureType) throws DataException {
344
                // this.open();
345
        // this.resourcesBegin();
346
        try {
347
            super.initFeatureProviderByIndex(featureProvider, index, featureType);
348
            featureProvider.setDefaultEnvelope(this.shpFile.getBoundingBox(index));
349
        } catch (CreateEnvelopeException e) {
350
            throw new org.gvsig.fmap.dal.feature.exception.CreateGeometryException(e);
351
        } catch (CreateGeometryException e) {
352
            throw new org.gvsig.fmap.dal.feature.exception.CreateGeometryException(e);
353
        }
354
    }
355

    
356
    /**
357
     *
358
     * @param featureProvider
359
     * @throws DataException
360
     */
361
    protected void loadFeatureProviderByIndex(FeatureProvider featureProvider)
362
            throws DataException {
363

    
364
        FeatureType featureType = featureProvider.getType();
365
        long index = ((Long) featureProvider.getOID()).longValue();
366
        boolean hasGeometry = false;
367
        int i = featureType.getDefaultGeometryAttributeIndex();
368
        if (i >= 0) {
369
            if (!featureProvider.isReadOnly(i)) {
370
                try {
371
                    Geometry geom = this.shpFile.getGeometry(index);
372
                    featureProvider.set(i, geom);
373
                } catch (CreateGeometryException e) {
374
                    throw new ReadException(getProviderName(), e);
375
                }
376
            }
377
            hasGeometry = true;
378
        }
379
        if (hasDBFAttributes(featureType, hasGeometry)) {
380
            super.loadFeatureProviderByIndex(featureProvider);
381
        }
382

    
383
    }
384

    
385
    private boolean hasDBFAttributes(FeatureType featureType,
386
            boolean hasGeometry) {
387
        FeatureAttributeDescriptor[] attributes
388
                = featureType.getAttributeDescriptors();
389
        // If there aren't any attributes, nor has any DBF attributes
390
        if (attributes == null || attributes.length == 0) {
391
            return false;
392
        }
393
        // If there is only one attribute and it is the geometry one
394
        if (attributes.length == 1 && hasGeometry) {
395
            return false;
396
        }
397
        // In any other case
398
        return true;
399
    }
400

    
401
    protected void loadValue(FeatureProvider featureProvider, int rowIndex,
402
            FeatureAttributeDescriptor descriptor) throws ReadException {
403
        if (descriptor.getType() == DataTypes.GEOMETRY) {
404
            return;
405
        } else {
406
            super.loadValue(featureProvider, rowIndex, descriptor);
407
        }
408
    }
409

    
410
    public FeatureProvider createFeatureProvider(FeatureType type) throws DataException {
411
        FeatureProvider data = new SHPFeatureProvider(this, type);
412
        return data;
413
    }
414

    
415
    protected void openFile() throws IOException, DataException {
416
        super.openFile();
417
        this.shpFile.open();
418

    
419
    }
420

    
421
    protected void closeFile() throws CloseException {
422
        super.closeFile();
423
        if (!this.shpFile.isOpen()) {
424
            return;
425
        }
426
        this.shpFile.close();
427
    }
428

    
429
    public boolean canWriteGeometry(final int geometryType, int geometrySubType)
430
            throws DataException {
431
        this.open();
432
        return ((Boolean) getResource().execute(new ResourceAction() {
433
            public Object run() throws Exception {
434
                boolean value = shpFile.canWriteGeometry(geometryType);
435
                return value ? Boolean.TRUE : Boolean.FALSE;
436
            }
437
        })).booleanValue();
438
//                this.resourcesBegin();
439
//                try {
440
//                        return this.shpFile.canWriteGeometry(geometryType);
441
//
442
//                } finally {
443
//                        this.resourcesEnd();
444
//                }
445
    }
446

    
447
    public void performChanges(Iterator deleteds, Iterator inserteds,
448
            Iterator updateds, Iterator originalFeatureTypesUpdated)
449
            throws PerformEditingException {
450

    
451
        /*
452
         * This will throw an exception if there are new fields
453
         * with names too long
454
         */
455
        checkNewFieldsNameSize(originalFeatureTypesUpdated);
456

    
457
        final FeatureType fType;
458
        try {
459
            fType = this.getStoreServices().getDefaultFeatureType();
460
        } catch (DataException e) {
461
            throw new PerformEditingException(this.getProviderName(), e);
462
        }
463
        // TODO Comprobar el campo de geometria
464

    
465
        final EditableFeatureType dbfFtype = fType.getEditable();
466

    
467
        removeGeometryColumn(dbfFtype);
468

    
469
                // try {
470
        // this.resourcesBegin();
471
        // } catch (ResourceExecuteException e1) {
472
        // throw new PerformEditingException(this.getName(), e1);
473
        // }
474
        try {
475

    
476
            getResource().execute(new ResourceAction() {
477
                public Object run() throws Exception {
478
                    FeatureSet set = null;
479
                    DisposableIterator iter = null;
480
                    try {
481
                        set = getFeatureStore().getFeatureSet();
482
                        writer = new SHPFeatureWriter(getProviderName());
483

    
484
                        SHPStoreParameters shpParams = getShpParameters();
485
                        SHPStoreParameters tmpParams
486
                                = (SHPStoreParameters) shpParams.getCopy();
487

    
488
                        File tmp_base = File.createTempFile(
489
                                "tmp_" + System.currentTimeMillis(), null);
490
                        String str_base = tmp_base.getCanonicalPath();
491

    
492
                        tmpParams.setDBFFile(str_base + ".dbf");
493
                        tmpParams.setSHPFile(str_base + ".shp");
494
                        tmpParams.setSHXFile(str_base + ".shx");
495

    
496
                        writer.begin(tmpParams, fType, dbfFtype, set.getSize());
497

    
498
                        iter = set.fastIterator();
499
                        while (iter.hasNext()) {
500
                            Feature feature = (Feature) iter.next();
501
                            writer.append(feature);
502
                        }
503

    
504
                        writer.end();
505

    
506
                        close();
507
                        resourceCloseRequest();
508

    
509
                        if (!shpParams.getDBFFile().delete()) {
510
                            throw new IOException(shpParams.getDBFFileName());
511
                        }
512
                        if (!shpParams.getSHPFile().delete()) {
513
                            throw new IOException(shpParams.getSHPFileName());
514
                        }
515
                        if (!shpParams.getSHXFile().delete()) {
516
                            throw new IOException(shpParams.getSHXFileName());
517
                        }
518
                        FileUtils.moveFile(
519
                                tmpParams.getDBFFile(),
520
                                shpParams.getDBFFile());
521
                        FileUtils.moveFile(
522
                                tmpParams.getSHPFile(),
523
                                shpParams.getSHPFile());
524
                        FileUtils.moveFile(
525
                                tmpParams.getSHXFile(),
526
                                shpParams.getSHXFile());
527

    
528
                        resourcesNotifyChanges();
529
                        initFeatureType();
530
                        return null;
531
                    } finally {
532
                        dispose(set);
533
                        dispose(iter);
534
                    }
535
                }
536
            });
537

    
538
        } catch (Exception e) {
539
            throw new PerformEditingException(this.getProviderName(), e);
540
        }
541

    
542
    }
543

    
544
    protected void resourceCloseRequest() throws ResourceException {
545
                // super.resourceCloseRequest();
546
        // this.shpResource.closeRequest();
547
        // this.shxResource.closeRequest();
548
        getResource().closeRequest();
549
    }
550

    
551
    public Envelope getEnvelope() throws DataException {
552
        this.open();
553
        return (Envelope) this.getDynValue("Envelope");
554
    }
555

    
556
    public void append(final FeatureProvider featureProvider) throws DataException {
557
//                this.resourcesBegin();
558
//                try {
559

    
560
        getResource().execute(new ResourceAction() {
561
            public Object run() throws Exception {
562
                writer.append(getStoreServices().createFeature(featureProvider));
563
                return null;
564
            }
565
        });
566
//                } finally {
567
//                        this.resourcesEnd();
568
//                }
569

    
570
    }
571

    
572
    public void beginAppend() throws DataException {
573
                // this.resourcesBegin();
574
        // try {
575

    
576
        getResource().execute(new ResourceAction() {
577
            public Object run() throws Exception {
578
                FeatureStore store = getFeatureStore();
579
                FeatureType fType = store.getDefaultFeatureType();
580

    
581
                                // TODO Comprobar el campo de geometria
582
                EditableFeatureType dbfFtype = fType.getEditable();
583

    
584
                removeGeometryColumn(dbfFtype);
585
                FeatureSet set = store.getFeatureSet();
586

    
587
                writer = new SHPFeatureWriter(getProviderName());
588

    
589
                writer.begin(getShpParameters(), fType, dbfFtype, set.getSize());
590
                return null;
591
            }
592
        });
593
                // } finally {
594
        // this.resourcesEnd();
595
        // }
596

    
597
    }
598

    
599
    public void endAppend() throws DataException {
600
//                this.resourcesBegin();
601
//                try {
602
        getResource().execute(new ResourceAction() {
603
            public Object run() throws Exception {
604
                writer.end();
605
                resourcesNotifyChanges();
606
                return null;
607
            }
608
        });
609
//                } finally {
610
//                        this.resourcesEnd();
611
//                }
612

    
613
    }
614

    
615
    public Object getSourceId() {
616
        return this.getShpParameters().getFile();
617
    }
618
}