Statistics
| Revision:

svn-gvsig-desktop / branches / org.gvsig.desktop-2018a / org.gvsig.desktop.library / org.gvsig.raster / org.gvsig.raster.lib / org.gvsig.raster.lib.buffer.impl / src / main / java / org / gvsig / raster / lib / buffer / impl / AbstractBuffer.java @ 43876

History | View | Annotate | Download (21.2 KB)

1
package org.gvsig.raster.lib.buffer.impl;
2

    
3
import java.awt.geom.AffineTransform;
4
import java.awt.geom.NoninvertibleTransformException;
5
import java.util.ArrayList;
6
import java.util.Iterator;
7
import java.util.List;
8

    
9
import org.cresques.cts.ICoordTrans;
10
import org.cresques.cts.IProjection;
11
import org.slf4j.Logger;
12
import org.slf4j.LoggerFactory;
13

    
14
import org.gvsig.fmap.geom.Geometry;
15
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
16
import org.gvsig.fmap.geom.Geometry.TYPES;
17
import org.gvsig.fmap.geom.GeometryLocator;
18
import org.gvsig.fmap.geom.exception.CreateGeometryException;
19
import org.gvsig.fmap.geom.operation.GeometryOperationException;
20
import org.gvsig.fmap.geom.operation.GeometryOperationNotSupportedException;
21
import org.gvsig.fmap.geom.primitive.Envelope;
22
import org.gvsig.fmap.geom.primitive.Point;
23
import org.gvsig.raster.lib.buffer.api.Band;
24
import org.gvsig.raster.lib.buffer.api.Band.BandByte;
25
import org.gvsig.raster.lib.buffer.api.Band.BandDouble;
26
import org.gvsig.raster.lib.buffer.api.Band.BandFloat;
27
import org.gvsig.raster.lib.buffer.api.Band.BandInt;
28
import org.gvsig.raster.lib.buffer.api.Band.BandShort;
29
import org.gvsig.raster.lib.buffer.api.BandInfo;
30
import org.gvsig.raster.lib.buffer.api.BandNotification;
31
import org.gvsig.raster.lib.buffer.api.Buffer;
32
import org.gvsig.raster.lib.buffer.api.BufferDimensions;
33
import org.gvsig.raster.lib.buffer.api.BufferLocator;
34
import org.gvsig.raster.lib.buffer.api.BufferManager;
35
import org.gvsig.raster.lib.buffer.api.BufferNotification;
36
import org.gvsig.raster.lib.buffer.api.NoData;
37
import org.gvsig.raster.lib.buffer.api.exceptions.BandException;
38
import org.gvsig.raster.lib.buffer.api.exceptions.BufferException;
39
import org.gvsig.raster.lib.buffer.api.statistics.Statistics;
40
import org.gvsig.raster.lib.buffer.impl.statistics.DefaultStatistics;
41
import org.gvsig.tools.ToolsLocator;
42
import org.gvsig.tools.dispose.Disposable;
43
import org.gvsig.tools.dispose.DisposeUtils;
44
import org.gvsig.tools.exception.BaseException;
45
import org.gvsig.tools.locator.LocatorException;
46
import org.gvsig.tools.observer.Notification;
47
import org.gvsig.tools.observer.Observable;
48
import org.gvsig.tools.observer.impl.BaseWeakReferencingObservable;
49
import org.gvsig.tools.task.SimpleTaskStatus;
50

    
51

    
52
/**
53
 * @author fdiaz
54
 *
55
 */
56
public abstract class AbstractBuffer extends BaseWeakReferencingObservable implements Buffer {
57

    
58
    protected static final Logger logger = LoggerFactory.getLogger(AbstractBuffer.class);
59

    
60
    protected List<Band> bands;
61
//    protected FilterList filters;
62
    protected IProjection projection;
63
    protected Statistics statistics;
64
    protected BufferDimensions dimensions = null;
65

    
66
    // to make it disposable
67
    private final Object lock = new Object();
68
    private boolean disposed = false;
69

    
70
    /**
71
     *
72
     */
73
    public AbstractBuffer() {
74
        if(ToolsLocator.getDisposableManager() != null) {
75
            ToolsLocator.getDisposableManager().bind(this);
76
        } else {
77
            logger.warn("Can't retrieve the disposable manager,");
78
        }
79
    }
80

    
81
    @Override
82
    public Statistics getStatistics(SimpleTaskStatus status) {
83
        if (statistics == null) {
84
            statistics = new DefaultStatistics();
85
        }
86
        if (!statistics.isCalculated()) {
87
            statistics.calculate(status, bands); // scale ???
88
        }
89
        return statistics;
90
    }
91

    
92
    @Override
93
    public Iterator<Band> iterator() {
94
        return bands.iterator();
95
    }
96

    
97
    @Override
98
    public void update(Observable observable, Object notification) {
99
        if (notification instanceof BandNotification && observable instanceof Band) {
100
            Notification bandNotification = (Notification) notification;
101
            if (bandNotification.getType().equals(BandNotification.COPY_FROM)
102
                || bandNotification.getType().equals(BandNotification.FILL)
103
                || bandNotification.getType().equals(BandNotification.PUT_ROW)
104
                || bandNotification.getType().equals(BandNotification.SET)) {
105
                this.statistics = null;
106
                this.notifyObservers(new DefaultBufferNotification(BufferNotification.CHANGE_BAND,
107
                    new Object[] { observable }));
108
            } else if (bandNotification.getType().equals(BandNotification.LOADED_PAGE)){
109
                this.notifyObservers(new DefaultBufferNotification(BufferNotification.LOADED_PAGE,
110
                    new Object[] { this }));
111
            }
112
        }
113
    }
114

    
115
//    @Override
116
//    public void filter(FilterList filterList) {
117
//        this.filters = filterList;
118
//    }
119

    
120
    @Override
121
    public int getBandCount() {
122
        return bands.size();
123
    }
124

    
125
    @Override
126
    public Band[] getBands() {
127
        Band[] arrayBands = new Band[bands.size()];
128
        return bands.toArray(arrayBands);
129
    }
130

    
131
    @Override
132
    public int getColumns() {
133
        return this.dimensions.getColumns();
134
    }
135

    
136
    @Override
137
    public int getRows() {
138
        return this.dimensions.getRows();
139
    }
140

    
141
    @Override
142
    public Envelope getEnvelope() {
143
        return this.dimensions.getEnvelope();
144
    }
145

    
146
    @Override
147
    public BufferDimensions getDimensions() {
148
        return this.dimensions;
149
    }
150

    
151
    @Override
152
    public IProjection getProjection() {
153
        return this.projection;
154
    }
155

    
156
    @Override
157
    public boolean isInside(int cellX, int cellY) {
158
        return (cellX >= 0 && cellX < this.getColumns() && cellY >= 0 && cellY < this.getRows());
159
    }
160

    
161
    @Override
162
    public boolean isInside(Point point) {
163

    
164
        if (getEnvelope() == null) {
165
            return false;
166
        }
167

    
168
        try {
169
            return getEnvelope().getGeometry().contains(point);
170
        } catch (GeometryOperationNotSupportedException | GeometryOperationException e) {
171
            logger.warn("It could not determine if the point is on the envelope", e);
172
            return false;
173
        }
174
    }
175

    
176
    @Override
177
    public void addBand(Band band) {
178

    
179
        if (band.getColumns() != this.getColumns() || band.getRows() != this.getRows()) {
180
            throw new IllegalArgumentException(
181
                "Can not add band to buffer. Band must have the same rows and columns as buffer.");
182
        }
183

    
184
        this.bands.add(band);
185
        DisposeUtils.bind(band);
186
        band.addObserver(this);
187
        this.statistics = null;
188
        this.notifyObservers(new DefaultBufferNotification(BufferNotification.ADD_BAND, new Object[] { band }));
189
    }
190

    
191
    @Override
192
    public void setBand(int pos, Band band) throws BandException {
193
        this.bands.get(pos).copyFrom(band);
194
        this.statistics = null;
195
        this.notifyObservers(new DefaultBufferNotification(BufferNotification.SET_BAND, new Object[] { pos, band }));
196
    }
197

    
198
    @Override
199
    public void removeBand(int pos) {
200
        Band band = this.bands.get(pos);
201
        band.deleteObserver(this);
202
        this.bands.remove(pos);
203
        this.statistics = null;
204
        this.notifyObservers(new DefaultBufferNotification(BufferNotification.REMOVE_BAND, new Object[] { pos }));
205
    }
206

    
207
    @Override
208
    public Band getBand(int pos) {
209
        return this.bands.get(pos);
210
    }
211

    
212
    @Override
213
    public BandByte getBandByte(int pos) {
214
        if (this.bands.get(pos).getDataType() != BufferManager.TYPE_BYTE) {
215
            throw new IllegalArgumentException(String.format("Band in position %1s is type of %2s, byte expected", pos, this.bands
216
                .get(pos).getDataType()));
217
        }
218
        return (BandByte) this.bands.get(pos);
219
    }
220

    
221
    @Override
222
    public BandShort getBandShort(int pos) {
223
        if (this.bands.get(pos).getDataType() != BufferManager.TYPE_SHORT) {
224
            throw new IllegalArgumentException(String.format("Band in position %1s is type of %2s, short expected", pos, this.bands
225
                .get(pos).getDataType()));
226
        }
227
        return (BandShort) this.bands.get(pos);
228
    }
229

    
230
    @Override
231
    public BandInt getBandInt(int pos) {
232
        if (this.bands.get(pos).getDataType() != BufferManager.TYPE_INT) {
233
            throw new IllegalArgumentException(String.format("Band in position %1s is type of %2s, int expected", pos, this.bands
234
                .get(pos).getDataType()));
235
        }
236
        return (BandInt) this.bands.get(pos);
237

    
238
    }
239

    
240
    @Override
241
    public BandFloat getBandFloat(int pos) {
242
        if (this.bands.get(pos).getDataType() != BufferManager.TYPE_FLOAT) {
243
            throw new IllegalArgumentException(String.format("Band in position %1s is type of %2s, float expected", pos, this.bands
244
                .get(pos).getDataType()));
245
        }
246
        return (BandFloat) this.bands.get(pos);
247

    
248
    }
249

    
250
    @Override
251
    public BandDouble getBandDouble(int pos) {
252
        if (this.bands.get(pos).getDataType() != BufferManager.TYPE_DOUBLE) {
253
            throw new IllegalArgumentException(String.format("Band in position %1s is type of %2s, double expected", pos, this.bands
254
                .get(pos).getDataType()));
255
        }
256
        return (BandDouble) this.bands.get(pos);
257

    
258
    }
259

    
260
    @Override
261
    public void switchBands(int[] positions) {
262
        List<Integer> visited = new ArrayList<Integer>();
263
        if (positions.length != this.getBandCount()) {
264
            throw new IllegalArgumentException("Position array length has to be the same as band count");
265
        }
266
        for (int i = 0; i < positions.length; i++) {
267
            Integer position = new Integer(positions[i]);
268
            if (visited.contains(position)) {
269
                throw new IllegalArgumentException(String.format(
270
                    "Position array can not have duplicated bands. Duplicated value: %1s", position));
271
            }
272
            visited.add(position);
273
        }
274

    
275
        List<Band> auxBands = new ArrayList<Band>(bands.size());
276
        for (int i = 0; i < visited.size(); i++) {
277
            auxBands.add(bands.get(visited.get(i)));
278
        }
279
        bands = auxBands;
280
        this.statistics = null;
281
        this.notifyObservers(new DefaultBufferNotification(BufferNotification.SWITCH_BAND, new Object[] { positions }));
282
    }
283

    
284
    @Override
285
    public void switchBands(int pos1, int pos2) {
286
        Band auxBand = bands.get(pos1);
287
        bands.set(pos1, bands.get(pos2));
288
        bands.set(pos2, auxBand);
289
        this.statistics = null;
290
        this.notifyObservers(new DefaultBufferNotification(BufferNotification.SWITCH_BAND, new Object[] { pos1, pos2 }));
291
    }
292

    
293
    @Override
294
    public Buffer createInterpolated(int rows, int columns, int interpolationMode, SimpleTaskStatus status)
295
        throws BufferException {
296
        if(rows==0 || columns==0){
297
            return null;
298
        }
299

    
300
        try {
301
            Buffer target =
302
                BufferLocator.getBufferManager().createBuffer(rows, columns, this.getBandTypes(),
303
                    this.getBandNoData(), this.getProjection(), this.getEnvelope(), null);
304
            BufferInterpolation interp = new BufferInterpolation();
305
            switch (interpolationMode) {
306
            case Buffer.INTERPOLATION_NearestNeighbour:
307
                interp.nearestNeighbourInterpolation(this, target, status);
308
                break;
309
            case Buffer.INTERPOLATION_Bilinear:
310
                interp.bilinearInterpolation(this, target, status);
311
                break;
312
            case Buffer.INTERPOLATION_InverseDistance:
313
                interp.inverseDistanceInterpolation(this, target, status);
314
                break;
315
            case Buffer.INTERPOLATION_BicubicSpline:
316
                interp.bicubicSplineInterpolation(this, target, status);
317
                break;
318
            case Buffer.INTERPOLATION_BSpline:
319
                interp.bSplineInterpolation(this, target, status);
320
                break;
321
            }
322
            return target;
323
        } catch (LocatorException e) {
324
            throw new BufferException(e);
325
        }
326
    }
327

    
328
    @Override
329
    public Buffer convert(ICoordTrans ct, SimpleTaskStatus status) throws BufferException {
330

    
331
        if (this.getEnvelope() == null) {
332
            throw new IllegalStateException("Buffer envelope is null. A buffer allways has to have envelope");
333
        }
334

    
335
        boolean isMyStatus = false;
336
        if (status == null) {
337
            status =
338
                ToolsLocator.getTaskStatusManager().createDefaultSimpleTaskStatus(
339
                    String.format("Projecting buffer from %1s to %2s", ct.getPOrig().getAbrev(), ct.getPDest()
340
                        .getAbrev()));
341
            status.add();
342
            isMyStatus = true;
343
        } else {
344
            status.push();
345
        }
346

    
347
        Envelope projectedBufferEnvelope = this.getEnvelope().convert(ct);
348
        // Get size of new buffer
349
        double[] newsize = getSize(this.getEnvelope(), projectedBufferEnvelope, this.getColumns(), this.getRows());
350
        int projectedBufferColumns = (int) newsize[0];
351
        int projectedBufferRows = (int) newsize[1];
352

    
353
        int[] bandDataTypes = this.getBandTypes();
354
        NoData[] bandNoData = this.getBandNoData();
355

    
356
        status.message("Creating projected buffer");
357
        Buffer projectedBuffer =
358
            BufferLocator.getBufferManager().createBuffer(projectedBufferRows, projectedBufferColumns, bandDataTypes,
359
                bandNoData, ct.getPDest(), projectedBufferEnvelope);
360

    
361
        // This affine transform allow us to convert pixel to coordinate and inverse
362
        AffineTransform bufferAf = calculateAffineTransform(this.getEnvelope(), this.getRows(), this.getColumns());
363
        AffineTransform projectedBufferAf =
364
            calculateAffineTransform(projectedBufferEnvelope, projectedBufferRows, projectedBufferColumns);
365

    
366
        ICoordTrans invertedCt = ct.getInverted();
367
        AffineTransform inverseBufferAf;
368
        try {
369
            inverseBufferAf = bufferAf.createInverse();
370
        } catch (NoninvertibleTransformException e1) {
371
            status.abort();
372
            logger.error("Can not create inverse transformation of {}", bufferAf);
373
            throw new BufferException(e1);
374
        }
375

    
376
        status.setRangeOfValues(0,
377
            projectedBuffer.getRows() * projectedBuffer.getColumns() * projectedBuffer.getBandCount());
378
        status.message("Projecting buffer");
379

    
380
        Point point;
381
        try {
382
            point = (Point) GeometryLocator.getGeometryManager().create(TYPES.POINT, SUBTYPES.GEOM2D);
383
        } catch (CreateGeometryException | LocatorException e1) {
384
            status.abort();
385
            logger.error("Can not create point geometry to project buffer");
386
           throw new BufferException(e1);
387
        }
388
        for (int row = 0; row < projectedBuffer.getRows(); row++) {
389

    
390
            if (status.isCancellationRequested()) {
391
                status.cancel();
392
                return projectedBuffer;
393
            }
394

    
395
            for (int col = 0; col < projectedBuffer.getColumns(); col++) {
396
                point.setX(col);
397
                point.setY(row);
398

    
399
                point.transform(projectedBufferAf); // projected buffer
400
                                                    // pixel to world point
401
                                                    // (target projection)
402
                point.reProject(invertedCt); // projected world point
403
                                             // to world point (source
404
                                             // projection)
405
                point.transform(inverseBufferAf); // world point to
406
                                                  // pixel source
407
                                                  // buffer
408

    
409
                double floorPointX = Math.floor(point.getX());
410
                double floorPointY = Math.floor(point.getY());
411
                boolean isValidPoint =
412
                    point.getX() >= 0 && floorPointX < this.getColumns() && point.getY() >= 0
413
                        && floorPointY < this.getRows();
414

    
415
                for (int k = 0; k < this.getBandCount(); k++) {
416

    
417
                    status.setCurValue(row * col * k);
418
                    Band band = this.bands.get(k);
419
                    Number value = null;
420
                    if (isValidPoint) {
421
                        value = (Number) band.get((int) floorPointY, (int) floorPointX);
422
                    } else if (band.getNoData().isDefined()) {
423
                        value = band.getNoData().getValue();
424
                    } else {
425
                        continue;
426
                    }
427

    
428
                    Band projectedBand = projectedBuffer.getBand(k);
429
                    int dataType = projectedBand.getDataType();
430
                    switch (dataType) {
431
                    case BufferManager.TYPE_BYTE:
432
                        projectedBand.set(row, col, value.byteValue());
433
                        break;
434
                    case BufferManager.TYPE_SHORT:
435
                        projectedBand.set(row, col, value.shortValue());
436
                        break;
437
                    case BufferManager.TYPE_INT:
438
                        projectedBand.set(row, col, value.intValue());
439
                        break;
440
                    case BufferManager.TYPE_FLOAT:
441
                        projectedBand.set(row, col, value.floatValue());
442
                        break;
443
                    case BufferManager.TYPE_DOUBLE:
444
                        projectedBand.set(row, col, value.doubleValue());
445
                        break;
446
                    default:
447
                        break;
448
                    }
449
                }
450

    
451
            }
452
        }
453

    
454
        if (isMyStatus) {
455
            status.terminate();
456
        } else {
457
            status.pop();
458
        }
459
        return projectedBuffer;
460
    }
461

    
462
    private double[] getSize(Envelope envelope, Envelope projectedEnvelope, int p1x, int p1y) {
463
        double sumSideOldBBox = envelope.getLength(0) + envelope.getLength(1);
464
        double sumSideNewBBox = projectedEnvelope.getLength(0) + projectedEnvelope.getLength(1);
465
        double d1x = (envelope.getLength(0) * 100) / sumSideOldBBox;
466
        double d1y = (envelope.getLength(1) * 100) / sumSideOldBBox;
467
        double d2x = (projectedEnvelope.getLength(0) * 100) / sumSideNewBBox;
468
        double d2y = (projectedEnvelope.getLength(1) * 100) / sumSideNewBBox;
469
        double p2y = (p1y * d2y) / d1y;
470
        double p2x = (p1x * d2x) / d1x;
471
        double newCellSizeX = projectedEnvelope.getLength(0) / p2x;
472
        double newCellSizeY= projectedEnvelope.getLength(1) / p2y;
473
        return new double[] { Math.round(p2x), Math.round(p2y), newCellSizeX, newCellSizeY };
474
    }
475

    
476
    private AffineTransform calculateAffineTransform(Envelope projectedBufferEnvelope, int projectedBufferRows,
477
        int projectedBufferColumns) {
478

    
479
        double cellSizeX = projectedBufferEnvelope.getLength(0) / projectedBufferColumns;
480
        double cellSizeY = projectedBufferEnvelope.getLength(1) / projectedBufferRows;
481

    
482
        return new AffineTransform(cellSizeX, 0d, 0d, -cellSizeY, projectedBufferEnvelope.getMinimum(0),
483
            projectedBufferEnvelope.getMaximum(1));
484
    }
485

    
486

    
487
    public int[] getBandTypes() {
488
        int[] bandDataTypes = new int[this.getBandCount()];
489
        for (int i = 0; i < this.bands.size(); i++) {
490
            bandDataTypes[i] = this.bands.get(i).getDataType();
491
        }
492
        return bandDataTypes;
493
    }
494

    
495
    @Override
496
    public boolean areAllBandsOfTheSameType() {
497
        if( this.bands.isEmpty() ) {
498
            return true;
499
        }
500
        int type = this.bands.get(0).getDataType();
501
        for (int i = 1; i < this.bands.size(); i++) {
502
            if( type != this.bands.get(i).getDataType() ) {
503
                return false;
504
            }
505
        }
506
        return true;
507
    }
508
    
509
    public NoData[] getBandNoData() {
510
        NoData[] bandNoData = new NoData[this.getBandCount()];
511
        for (int i = 0; i < this.bands.size(); i++) {
512
            bandNoData[i] = this.bands.get(i).getNoData();
513
        }
514
        return bandNoData;
515
    }
516

    
517
    @Override
518
    public Buffer clip(Envelope envelope) throws BufferException {
519
        try {
520
            return BufferLocator.getBufferManager().createClippedBuffer(this, envelope);
521
        } catch (LocatorException | BufferException e) {
522
            throw new BufferException(e);
523
        }
524
    }
525

    
526
    public double getPixelSizeX(){
527
        return this.dimensions.getPixelSizeX();
528
    }
529

    
530
    public double getPixelSizeY(){
531
        return this.dimensions.getPixelSizeY();
532
    }
533

    
534
    public final void dispose() {
535
        synchronized (lock) {
536
            // Check if we have already been disposed, and don't do it again
537
            if (!disposed) {
538
                if ( ToolsLocator.getDisposableManager().release(this) ) {
539
                    try {
540
                        doDispose();
541
                    } catch (BaseException ex) {
542
                        logger.error("Error performing dispose", ex);
543
                    }
544
                    disposed = true;
545
                }
546
            }
547
        }
548
    }
549

    
550
    /**
551
     * Internal implementation for the {@link #dispose()} method.
552
     *
553
     * @see #dispose()
554
     */
555
    public void doDispose() throws BaseException {
556
        for (Iterator<Band> iterator = bands.iterator(); iterator.hasNext();) {
557
            Band band = (Band) iterator.next();
558
            DisposeUtils.dispose(band);
559
        }
560
        bands.removeAll(bands);
561
        projection = null;
562
        //TODO: ?FilterList Disposable?
563
//        filters = null;
564
        statistics = null;
565
        dimensions = null;
566
    }
567

    
568
    @Override
569
    protected void finalize() throws Throwable {
570
        super.finalize();
571
    }
572

    
573
    @Override
574
    public BandInfo[] getBandsInfo() {
575
        BandInfo[] bandsInfo = new BandInfo[this.bands.size()];
576
        for (int i = 0; i < this.bands.size(); i++) {
577
            bandsInfo[i] = this.bands.get(i).getBandInfo();
578
        }
579
        return bandsInfo;
580
    }
581
}