Statistics
| Revision:

gvsig-projects-pool / org.gvsig.lidar.prov / org.gvsig.lidar.prov.jgrass / src / main / java / org / gvsig / lidar / prov / jgrasstools / JGrassLASDataStoreProvider.java @ 281

History | View | Annotate | Download (20.6 KB)

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

    
26
import java.io.File;
27
import java.io.IOException;
28
import java.lang.reflect.Constructor;
29
import java.lang.reflect.InvocationTargetException;
30
import java.util.ArrayList;
31
import java.util.Collection;
32
import java.util.Date;
33
import java.util.Iterator;
34
import java.util.List;
35
import java.util.Set;
36

    
37
import org.apache.commons.io.FileUtils;
38
import org.cresques.cts.ICRSFactory;
39
import org.cresques.cts.IProjection;
40
import org.gvsig.fmap.dal.DALLocator;
41
import org.gvsig.fmap.dal.DataStore;
42
import org.gvsig.fmap.dal.DataStoreParameters;
43
import org.gvsig.fmap.dal.DataTypes;
44
import org.gvsig.fmap.dal.FileHelper;
45
import org.gvsig.fmap.dal.exception.DataException;
46
import org.gvsig.fmap.dal.exception.InitializeException;
47
import org.gvsig.fmap.dal.exception.OpenException;
48
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
49
import org.gvsig.fmap.dal.feature.EditableFeatureType;
50
import org.gvsig.fmap.dal.feature.Feature;
51
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
52
import org.gvsig.fmap.dal.feature.FeatureSet;
53
import org.gvsig.fmap.dal.feature.FeatureType;
54
import org.gvsig.fmap.dal.feature.exception.FeatureIndexException;
55
import org.gvsig.fmap.dal.feature.exception.PerformEditingException;
56
import org.gvsig.fmap.dal.feature.spi.DefaultFeatureProvider;
57
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
58
import org.gvsig.fmap.dal.resource.ResourceAction;
59
import org.gvsig.fmap.dal.resource.exception.ResourceException;
60
import org.gvsig.fmap.dal.resource.spi.ResourceProvider;
61
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
62
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
63
import org.gvsig.fmap.geom.Geometry.TYPES;
64
import org.gvsig.fmap.geom.GeometryLocator;
65
import org.gvsig.fmap.geom.GeometryManager;
66
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
67
import org.gvsig.fmap.geom.exception.CreateGeometryException;
68
import org.gvsig.fmap.geom.primitive.Envelope;
69
import org.gvsig.fmap.geom.primitive.Point;
70
import org.gvsig.fmap.geom.type.GeometryType;
71
import org.gvsig.fmap.geom.type.GeometryTypeNotSupportedException;
72
import org.gvsig.fmap.geom.type.GeometryTypeNotValidException;
73
import org.gvsig.lidar.prov.LASDataStoreParameters;
74
import org.gvsig.lidar.prov.LASDataStoreProvider;
75
import org.gvsig.lidar.prov.ConversionUtils;
76
import org.gvsig.lidar.prov.LASUnsupportedFormatException;
77
import org.gvsig.tools.dispose.DisposableIterator;
78
import org.gvsig.tools.dispose.DisposeUtils;
79
import org.gvsig.tools.dynobject.DynObject;
80
import org.gvsig.tools.exception.BaseException;
81
import org.jgrasstools.gears.io.las.core.ALasReader;
82
import org.jgrasstools.gears.io.las.core.ILasHeader;
83
import org.jgrasstools.gears.io.las.core.LasRecord;
84
import org.jgrasstools.gears.io.las.core.liblas.LiblasReader;
85
import org.jgrasstools.gears.io.las.core.v_1_0.LasReader;
86
import org.jgrasstools.gears.io.las.core.v_1_0.LasWriter;
87
import org.opengis.metadata.Identifier;
88
import org.opengis.metadata.extent.Extent;
89
import org.opengis.referencing.crs.CRSFactory;
90
import org.opengis.referencing.crs.CoordinateReferenceSystem;
91
import org.opengis.referencing.cs.CoordinateSystem;
92
import org.opengis.util.GenericName;
93
import org.opengis.util.InternationalString;
94
import org.slf4j.Logger;
95
import org.slf4j.LoggerFactory;
96

    
97
import com.sun.jna.Native;
98
import com.sun.jna.NativeLibrary;
99

    
100
/**
101
 * 
102
 * @author <a href="mailto:cmartinez@scolab.es">Cesar Martinez Izquierdo</a>
103
 *
104
 */
105
public class JGrassLASDataStoreProvider extends LASDataStoreProvider {
106

    
107
    private static final Logger LOG = LoggerFactory.getLogger(JGrassLASDataStoreProvider.class);
108

    
109
    public static final String NAME = "JGrassLASDataStoreProvider";
110

    
111
    public static final String DESCRIPTION = "LIDAR LAS file (jgrasstools)";
112
    
113
    public static final String METADATA_DEFINITION_NAME = "JGrassLASDataStoreProvider";
114

    
115
    protected ALasReader reader = null;
116

    
117
    protected JGrassLASDataStoreProvider(DataStoreParameters dataParameters,
118
        DataStoreProviderServices storeServices, DynObject metadata) throws InitializeException {
119
        super(dataParameters, storeServices, metadata);
120
    }
121

    
122
    protected JGrassLASDataStoreProvider(DataStoreParameters dataParameters,
123
        DataStoreProviderServices storeServices) throws InitializeException {
124
            
125
            super(dataParameters, storeServices, FileHelper
126
                    .newMetadataContainer(METADATA_DEFINITION_NAME));
127
    }
128
    
129
    @Override
130
    public String getFullName() {
131
            return getName();
132
    }
133

    
134
    @Override
135
    public String getName() {
136
        return getLASParameters().getFile().getName();
137
    }
138

    
139
    @Override
140
    public String getProviderName() {
141
        return NAME;
142
    }
143

    
144
    @Override
145
    public boolean allowWrite() {
146
            return true;
147
    }
148

    
149
    protected boolean loadFeatureType() throws LASUnsupportedFormatException,
150
        GeometryTypeNotSupportedException, GeometryTypeNotValidException, DataException {
151

    
152
        return (boolean) getResource().execute(new ResourceAction() {
153

    
154
            @Override
155
            public Object run() throws Exception {
156
                    EditableFeatureType editableFeatureType = DALLocator.getDataManager().createFeatureType();
157
                editableFeatureType.setHasOID(true);
158
                EditableFeatureAttributeDescriptor attributeDescriptor = editableFeatureType.add("GEOM",  DataTypes.GEOMETRY);
159
                
160
                GeometryManager geometryManager = GeometryLocator.getGeometryManager();
161
                GeometryType geometryType =
162
                        geometryManager.getGeometryType(
163
                                        TYPES.POINT, SUBTYPES.GEOM3D);
164
                attributeDescriptor.setGeometryType(geometryType);
165
                if (getLASParameters().getCRS()!=null) {
166
                        attributeDescriptor.setSRS(getLASParameters().getCRS());
167
                }
168
                editableFeatureType.setDefaultGeometryAttributeName(JGrassLASDataStoreProvider.GEOM_FIELD);
169
                attributeDescriptor =
170
                        editableFeatureType.add(CLASS_FIELD, DataTypes.BYTE);
171
                attributeDescriptor =
172
                        editableFeatureType.add(POINTX_FIELD, DataTypes.DOUBLE);
173
                attributeDescriptor =
174
                        editableFeatureType.add(POINTY_FIELD, DataTypes.DOUBLE);
175
                attributeDescriptor =
176
                        editableFeatureType.add(POINTZ_FIELD, DataTypes.DOUBLE);
177
                attributeDescriptor =
178
                        editableFeatureType.add(INTENSITY_FIELD, DataTypes.INT);
179
                attributeDescriptor =
180
                        editableFeatureType.add(RETURNNUM_FIELD, DataTypes.BYTE);
181
                attributeDescriptor =
182
                        editableFeatureType.add(NUMRETURNS_FIELD, DataTypes.BYTE);
183
                
184
                ILasHeader header = getReader().getHeader();
185
                if (header.hasRGB()) {
186
                        attributeDescriptor =
187
                            editableFeatureType.add(COLOR_FIELD, DataTypes.INT);
188
                }
189
                if (header.hasGpsTime()) {
190
                        if (header.getGpsTimeType()==1) {
191
                                // Show adjusted standard GPS time as a Java Date
192
                                attributeDescriptor = editableFeatureType.add(TIME_FIELD, DataTypes.DATE);
193
                        }
194
                        else {
195
                                // Show GPS week time as a plain long, as we don't have enough context to translate it to a real date
196
                                attributeDescriptor = editableFeatureType.add(WEEKTIME_FIELD, DataTypes.LONG);
197
                        }
198
                }
199

    
200
                FeatureType featureType = editableFeatureType.getNotEditableCopy();
201
                if (featureType.getDefaultSRS() != null) {
202
                    setDynValue(DataStore.METADATA_CRS, featureType.getDefaultSRS());
203
                }
204

    
205
                List<FeatureType> featureTypes = new ArrayList<FeatureType>(1);
206
                featureTypes.add(featureType);
207

    
208
                getStoreServices().setFeatureTypes(featureTypes, featureType);
209
                
210
                return true;
211
            }
212
        });
213
    }
214

    
215
    public FeatureProvider internalGetFeatureProviderByIndex(
216
                    long index, FeatureType featureType)
217
                                    throws DataException {
218
            
219
            long decimatedIndex = (long) Math.floor(index * decimation);
220
            FeatureProvider featureProvider = this.createFeatureProvider(featureType);
221
            featureProvider.setOID(new Long(decimatedIndex));
222
            LasRecord record;
223
                try {
224
                        record = getReader().getPointAt(decimatedIndex);
225
                } catch (IOException e1) {
226
                        throw new FeatureIndexException("Error accessing record", e1,
227
                                        "Error accessing record", -1);
228
                }
229
                if (featureType.getAttributeDescriptor(GEOM_FIELD)!=null) {
230
                        try {
231
                                Point p = (Point) this.gm.create(TYPES.POINT, SUBTYPES.GEOM3D);
232
                                p.setCoordinateAt(0, record.x);
233
                                p.setCoordinateAt(1, record.y);
234
                                p.setCoordinateAt(2, record.z);
235
                                featureProvider.set(GEOM_FIELD, p);
236
                        } catch (CreateGeometryException e) {
237
                                // TODO Auto-generated catch block
238
                                e.printStackTrace();
239
                        }
240
                }
241
            if (featureType.getAttributeDescriptor(POINTX_FIELD)!=null) {
242
                    featureProvider.set(POINTX_FIELD, record.x);
243
            }
244
            if (featureType.getAttributeDescriptor(POINTY_FIELD)!=null) {
245
                    featureProvider.set(POINTY_FIELD, record.y);
246
            }
247
            if (featureType.getAttributeDescriptor(POINTZ_FIELD)!=null) {
248
                    featureProvider.set(POINTZ_FIELD, record.z);
249
            }
250
            if (featureType.getAttributeDescriptor(CLASS_FIELD)!=null) {
251
                    featureProvider.set(CLASS_FIELD, record.classification);
252
            }
253
            if (featureType.getAttributeDescriptor(INTENSITY_FIELD)!=null) {
254
                    featureProvider.set(INTENSITY_FIELD, (int)record.intensity);
255
            }
256
            if (featureType.getAttributeDescriptor(RETURNNUM_FIELD)!=null) {
257
                    featureProvider.set(RETURNNUM_FIELD, (byte)record.returnNumber);
258
            }
259
            if (featureType.getAttributeDescriptor(NUMRETURNS_FIELD)!=null) {
260
                    featureProvider.set(NUMRETURNS_FIELD, (byte) record.numberOfReturns);
261
            }
262
            if (featureType.getAttributeDescriptor(TIME_FIELD)!=null) {
263
                    long las_time  = (long)record.gpsTime;
264
                    // file uses LAS 1.2/1.3/1.4 adjusted GPS Time
265
                    // convert to unix time and build a Date object
266
                    Date unix_time = new Date(las_time + ConversionUtils.LAS_TIME_TO_UNIX_TIME);
267
                        featureProvider.set(TIME_FIELD, unix_time);
268
            }
269
            if (featureType.getAttributeDescriptor(WEEKTIME_FIELD)!=null) {
270
                    // LAS 1.0, 1.1 gps week time, we handle it as a plain long.
271
                    // It could be translated to Unix time using the following formulas:
272
                    // seconds_per_week = 604800
273
                    // las_gps_time = las_week_time + ( week_number_from_6_1_1980 * seconds_per_week)
274
                    // unix_time = las_gps_time + 315964800
275
                    long las_time  = (long)record.gpsTime;
276
                    featureProvider.set(WEEKTIME_FIELD, new Long(las_time));
277
            }
278
            if (featureType.getAttributeDescriptor(COLOR_FIELD)!=null) {
279

    
280
                    int red = record.color[0];
281
                    int green = record.color[1];
282
                    int blue = record.color[2];
283
                    // RGB value representing the color in the default sRGB ColorModel.
284
                    // (Bits 24-31 are alpha, 16-23 are red, 8-15 are green, 0-7 are blue).
285
                    featureProvider.set(COLOR_FIELD, ConversionUtils.rgbToInt(red, green, blue));
286
            }
287
            return featureProvider;
288
    }
289
    
290
    protected synchronized ALasReader getReader() throws LASUnsupportedFormatException, OpenException {
291
        if (this.reader == null) {
292
                /**
293
                 * We create a fake geoapi CRS, because providing a null CRS to JGrasstools readers
294
                 * causes errors if the .prj file exists and the dependences are not satisfied
295
                 */
296
                CoordinateReferenceSystemFakeImpl crs = new CoordinateReferenceSystemFakeImpl();
297
                try {
298
                        if (ALasReader.supportsNative()) {
299
                                reader = new LiblasReader(file, crs);
300
                                LOG.info("JGrass LiblasReader loaded");
301
                        }
302
                } catch (Exception exc) {
303
                        LOG.debug("Error loading native liblas reader", exc);
304
                }
305
                if (reader==null) {
306
                        try {
307
                                reader = new LasReader(file, crs);
308
                                LOG.info("JGrass LasReader loaded");
309
                        }
310
                        catch (Exception e) {
311
                                throw new OpenException("Error loading Java JGrass LAS reader", e);
312
                        }
313
                }
314
                try {
315
                        reader.open();
316
                } catch (Exception e) {
317
                        throw new OpenException("Error opening JGrass LAS reader", e);
318
                }
319
        }
320
        return this.reader;
321
    }
322
    
323
    /*
324
     * Lazy envelope initialization
325
     */
326
    @Override
327
    public Envelope getEnvelope() throws DataException {
328
            
329
        open();
330
        if (this.envelope == null) {
331
                Envelope env;
332
                        try {
333
                                env = this.gm.createEnvelope(SUBTYPES.GEOM3D);
334
                        Point p1 = (Point) this.gm.create(TYPES.POINT, SUBTYPES.GEOM3D);
335
                        Point p2 = (Point) this.gm.create(TYPES.POINT, SUBTYPES.GEOM3D);
336
                        // FIXME: do we need to consider projection for upper & lower corner?
337
                        ILasHeader header = getReader().getHeader();
338
                        double[] env3d = header.getRawDataEnvelope();
339
                        p1.setCoordinateAt(0, env3d[0]);
340
                        p1.setCoordinateAt(1, env3d[1]);
341
                        p1.setCoordinateAt(2, env3d[2]);
342
                        p2.setCoordinateAt(0, env3d[3]);
343
                        p2.setCoordinateAt(1, env3d[4]);
344
                        p2.setCoordinateAt(2, env3d[5]);
345
                        env.setLowerCorner(p1);
346
                        env.setUpperCorner(p2);
347
                        this.envelope = env;
348
                        } catch (CreateEnvelopeException e) {
349
                                // TODO Auto-generated catch block
350
                                e.printStackTrace();
351
                        } catch (CreateGeometryException e) {
352
                                // TODO Auto-generated catch block
353
                                e.printStackTrace();
354
                        }
355

    
356
        }
357
        return this.envelope;
358
    }
359

    
360

    
361
    @Override
362
    public long getFeatureCount() throws DataException {
363
        open();
364
        return ((Number) getResource().execute(new ResourceAction() {
365

    
366
            @Override
367
            public Object run() throws Exception {
368
                    long count = getReader().getHeader().getRecordsCount();
369
                    if (decimation>1.0d) {
370
                            count = (long) Math.floor((count/decimation));
371
                    }
372
                    return count;
373
            }
374
        })).longValue();
375
    }
376
    
377
    @Override
378
    protected void doDispose() throws BaseException {
379
        super.doDispose();
380
        getResource().removeConsumer(this);
381
        this.resourceProvider = null;
382
        this.gm = null;
383
        this.envelope = null;
384
        this.opened = false;
385
        try { this.reader.close(); } catch (Exception e) {}
386
        this.reader = null;
387
    }
388

    
389
    @Override
390
    public boolean closeResourceRequested(ResourceProvider resource) {
391
        this.envelope = null;
392
        this.opened = false;
393
        try { this.reader.close(); } catch (Exception e) {}
394
        this.reader = null;
395
        return true;
396
    }
397

    
398
    @Override
399
    public void resourceChanged(ResourceProvider resource) {
400
        this.envelope = null;
401
        this.opened = false;
402
        try { this.reader.close(); } catch (Exception e) {}
403
        this.reader = null;
404
    }
405

    
406
    @SuppressWarnings("rawtypes")
407
    public void performChanges(Iterator deleteds, Iterator inserteds, Iterator updateds,
408
        Iterator originalFeatureTypesUpdated) throws PerformEditingException {
409

    
410
        final FeatureType fType;
411
        try {
412
            fType = this.getStoreServices().getDefaultFeatureType();
413
        } catch (DataException e) {
414
            throw new PerformEditingException(this.getProviderName(), e);
415
        }
416
        final FeatureAttributeDescriptor geomAttrib = fType.getDefaultGeometryAttribute();
417
        
418
        if (geomAttrib.getGeomType().getType()!=TYPES.POINT) {
419
                throw new PerformEditingException(getProviderName(), new RuntimeException("LAS files can only store points"));
420
        }
421

    
422
        try {
423
            resourceCloseRequest();
424

    
425
            getResource().execute(new ResourceAction() {
426

    
427
                public Object run() throws Exception {
428
                    FeatureSet set = null;
429
                    DisposableIterator iter = null;
430
                    try {
431
                        set = getFeatureStore().getFeatureSet();
432
                        
433

    
434
                        LASDataStoreParameters lasParams = getLASParameters();
435
                        LASDataStoreParameters tmpParams = (LASDataStoreParameters) lasParams.getCopy();
436

    
437
                        File tmp_file = File.createTempFile("tmp_" + System.currentTimeMillis(), ".las");
438
                        tmpParams.setFile(tmp_file);
439
                        
440
                        JGrassLASWriter writer = new JGrassLASWriter(tmpParams, fType, getFeatureStore().getEnvelope());                        
441
                        ILasHeader h = getReader().getHeader();
442
                        writer.setOffset(h.getXYZOffset()[0], h.getXYZOffset()[1], h.getXYZOffset()[2]);
443
                        writer.setScale(h.getXYZScale()[0], h.getXYZScale()[1], h.getXYZScale()[2]);
444
                        writer.setPointFormat(h.getPointDataFormat());
445
                        writer.open();
446

    
447
                        iter = set.fastIterator();
448
                        while (iter.hasNext()) {
449
                            Feature feature = (Feature) iter.next();
450
                            writer.write(feature);
451
                        }
452

    
453
                        writer.close();
454
                        close();
455

    
456

    
457
                        if (!lasParams.getFile().delete()) {
458
                            LOG.debug("Can't delete las file '" + lasParams.getFile() + "'.");
459
                            throw new IOException("Can't delete las '"
460
                                + lasParams.getFile().getAbsolutePath()
461
                                + "' file to replace with the new las.\nThe new las is in temporary file '" + tmp_file.getCanonicalPath()
462
                                + "'");
463
                        }
464
                        FileUtils.moveFile(tmpParams.getFile(), lasParams.getFile());
465
                        return null;
466
                    } finally {
467
                        dispose(set);
468
                        dispose(iter);
469
                        getResource().notifyChanges();
470
                    }
471
                }
472
            });
473

    
474
        } catch (Exception e) {
475
            throw new PerformEditingException(this.getProviderName(), e);
476
        }
477
    }
478

    
479
    protected void resourceCloseRequest() throws ResourceException {
480
        getResource().closeRequest();
481
    }
482
    
483
    public FeatureProvider createFeatureProvider(FeatureType type) throws DataException {
484
            return new DefaultFeatureProvider(type);
485
    }
486
    
487
    /**
488
     * A fake implementation of GeoAPI CoordinateReferenceSystem. It must
489
     * only be used to fake JGrass LAS readers, as they are happy if a
490
     * CRS fake object is provided (they won't do anything special with it)
491
     */
492
    public class CoordinateReferenceSystemFakeImpl implements CoordinateReferenceSystem {
493

    
494
            public CoordinateReferenceSystemFakeImpl() {
495
            }
496

    
497
                public Extent getDomainOfValidity() {
498
                        // TODO Auto-generated method stub
499
                        return null;
500
                }
501

    
502
                public InternationalString getScope() {
503
                        // TODO Auto-generated method stub
504
                        return null;
505
                }
506

    
507
                public Collection<GenericName> getAlias() {
508
                        // TODO Auto-generated method stub
509
                        return null;
510
                }
511

    
512
                /**
513
                 * Identifier must be replaced by ReferenceIdentifier when
514
                 * using Geoapi >= 3.0
515
                 */
516
                public Set<Identifier> getIdentifiers() {
517
                        
518
                        // TODO Auto-generated method stub
519
                        return null;
520
                }
521

    
522
                /**
523
                 * Identifier must be replaced by ReferenceIdentifier when
524
                 * using Geoapi >= 3.0
525
                 */
526
                public Identifier getName() {
527
                        // TODO Auto-generated method stub
528
                        return null;
529
                }
530

    
531
                public InternationalString getRemarks() {
532
                        // TODO Auto-generated method stub
533
                        return null;
534
                }
535

    
536
                public String toWKT() throws UnsupportedOperationException {
537
                        return null;
538
                }
539

    
540
                public CoordinateSystem getCoordinateSystem() {
541
                        // TODO Auto-generated method stub
542
                        return null;
543
                }
544
                
545
                public Extent getValidArea() {
546
                        return null;
547
                }
548
            
549
    }
550

    
551
}