Statistics
| Revision:

gvsig-raster / org.gvsig.raster / branches / org.gvsig.raster.2.4 / org.gvsig.raster / org.gvsig.raster.lib / org.gvsig.raster.lib.buffer / org.gvsig.raster.lib.buffer.impl / src / main / java / org / gvsig / raster / lib / buffer / impl / AbstractBuffer.java @ 6498

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

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

    
46
import org.slf4j.Logger;
47
import org.slf4j.LoggerFactory;
48

    
49

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

    
56
    protected static final Logger logger = LoggerFactory.getLogger(AbstractBuffer.class);
57

    
58
    protected List<Band> bands;
59
    protected int rows;
60
    protected int columns;
61
    protected FilterList filters;
62
    protected Envelope envelope;
63
    protected IProjection projection;
64
    protected Statistics statistics;
65
    private Double pixelSizeX = null;
66
    private Double pixelSizeY = null;
67

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

    
72
    public AbstractBuffer() {
73
        logger.info("CONSTRUCTOR rows = "+rows+" columns = "+columns+ " hashCode = "+this.hashCode()+ " className = "+this.getClass().getSimpleName());
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(bands);
85
            // this.addObserver(statistics);
86
        }
87
        if (!statistics.isCalculated()) {
88
            statistics.calculate(status); // scale ???
89
        }
90
        return statistics;
91
    }
92

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

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

    
113
    @Override
114
    public void filter(FilterList filterList) {
115
        this.filters = filterList;
116
    }
117

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

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

    
129
    @Override
130
    public int getColumns() {
131
        return this.columns;
132
    }
133

    
134
    @Override
135
    public int getRows() {
136
        return this.rows;
137
    }
138

    
139
    @Override
140
    public Envelope getEnvelope() {
141
        return this.envelope;
142
    }
143

    
144
    @Override
145
    public IProjection getProjection() {
146
        return this.projection;
147
    }
148

    
149
    @Override
150
    public boolean isInside(int cellX, int cellY) {
151
        return (cellX >= 0 && cellX < this.columns && cellY >= 0 && cellY < this.rows);
152
    }
153

    
154
    @Override
155
    public boolean isInside(Point point) {
156

    
157
        if (getEnvelope() == null) {
158
            return false;
159
        }
160

    
161
        try {
162
            return getEnvelope().getGeometry().contains(point);
163
        } catch (GeometryOperationNotSupportedException | GeometryOperationException e) {
164
            logger.warn("It could not determine if the point is on the envelope", e);
165
            return false;
166
        }
167
    }
168

    
169
    @Override
170
    public void addBand(Band band) {
171

    
172
        if (band.getColumns() != this.getColumns() || band.getRows() != this.getRows()) {
173
            throw new IllegalArgumentException(
174
                "Can not add band to buffer. Band must have the same rows and columns as buffer.");
175
        }
176

    
177
        this.bands.add(band);
178
        band.addObserver(this);
179
        this.statistics = null;
180
        this.notifyObservers(new DefaultBufferNotification(BufferNotification.ADD_BAND, new Object[] { band }));
181
    }
182

    
183
    @Override
184
    public void setBand(int pos, Band band) throws BandException {
185
        this.bands.get(pos).copyFrom(band);
186
        this.statistics = null;
187
        this.notifyObservers(new DefaultBufferNotification(BufferNotification.SET_BAND, new Object[] { pos, band }));
188
    }
189

    
190
    @Override
191
    public void removeBand(int pos) {
192
        Band band = this.bands.get(pos);
193
        band.deleteObserver(this);
194
        this.bands.remove(pos);
195
        this.statistics = null;
196
        this.notifyObservers(new DefaultBufferNotification(BufferNotification.REMOVE_BAND, new Object[] { pos }));
197
    }
198

    
199
    @Override
200
    public Band getBand(int pos) {
201
        return this.bands.get(pos);
202
    }
203

    
204
    @Override
205
    public BandByte getBandByte(int pos) {
206
        if (this.bands.get(pos).getDataType() != BufferManager.TYPE_BYTE) {
207
            throw new IllegalArgumentException(String.format("Band in position %1s is type of %2s", pos, this.bands
208
                .get(pos).getDataType()));
209
        }
210
        return (BandByte) this.bands.get(pos);
211
    }
212

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

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

    
230
    }
231

    
232
    @Override
233
    public BandFloat getBandFloat(int pos) {
234
        if (this.bands.get(pos).getDataType() != BufferManager.TYPE_FLOAT) {
235
            throw new IllegalArgumentException(String.format("Band in position %1s is type of %2s", pos, this.bands
236
                .get(pos).getDataType()));
237
        }
238
        return (BandFloat) this.bands.get(pos);
239

    
240
    }
241

    
242
    @Override
243
    public BandDouble getBandDouble(int pos) {
244
        if (this.bands.get(pos).getDataType() != BufferManager.TYPE_DOUBLE) {
245
            throw new IllegalArgumentException(String.format("Band in position %1s is type of %2s", pos, this.bands
246
                .get(pos).getDataType()));
247
        }
248
        return (BandDouble) this.bands.get(pos);
249

    
250
    }
251

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

    
267
        List<Band> auxBands = new ArrayList<Band>(bands.size());
268
        for (int i = 0; i < visited.size(); i++) {
269
            auxBands.add(bands.get(visited.get(i)));
270
        }
271
        bands = auxBands;
272
        this.statistics = null;
273
        this.notifyObservers(new DefaultBufferNotification(BufferNotification.SWITCH_BAND, new Object[] { positions }));
274
    }
275

    
276
    @Override
277
    public void switchBands(int pos1, int pos2) {
278
        Band auxBand = bands.get(pos1);
279
        bands.set(pos1, bands.get(pos2));
280
        bands.set(pos2, auxBand);
281
        this.statistics = null;
282
        this.notifyObservers(new DefaultBufferNotification(BufferNotification.SWITCH_BAND, new Object[] { pos1, pos2 }));
283
    }
284

    
285
    @Override
286
    public Buffer createInterpolated(int rows, int columns, int interpolationMode, SimpleTaskStatus status)
287
        throws BufferException {
288
        if(rows==0 || columns==0){
289
            return null;
290
        }
291

    
292
        try {
293
//            if (rows == getRows() && columns == getColumns()) {
294
//                return (Buffer) this.clone();
295
//            }
296

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

    
325
    @Override
326
    public Buffer convert(ICoordTrans ct, SimpleTaskStatus status) throws BufferException {
327

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

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

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

    
350
        int[] bandDataTypes = this.getBandTypes();
351
        NoData[] bandNoData = this.getBandNoData();
352

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

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

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

    
373
        status.setRangeOfValues(0,
374
            projectedBuffer.getRows() * projectedBuffer.getColumns() * projectedBuffer.getBandCount());
375
        status.message("Projecting buffer");
376

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

    
387
            if (status.isCancellationRequested()) {
388
                status.cancel();
389
                return projectedBuffer;
390
            }
391

    
392
            for (int col = 0; col < projectedBuffer.getColumns(); col++) {
393
                point.setX(col);
394
                point.setY(row);
395

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

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

    
412
                for (int k = 0; k < this.getBandCount(); k++) {
413

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

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

    
448
            }
449
        }
450

    
451
        if (isMyStatus) {
452
            status.terminate();
453
        } else {
454
            status.pop();
455
        }
456
        return projectedBuffer;
457
    }
458

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

    
473
    private AffineTransform calculateAffineTransform(Envelope projectedBufferEnvelope, int projectedBufferRows,
474
        int projectedBufferColumns) {
475

    
476
        double cellSizeX = projectedBufferEnvelope.getLength(0) / projectedBufferColumns;
477
        double cellSizeY = projectedBufferEnvelope.getLength(1) / projectedBufferRows;
478

    
479
        return new AffineTransform(cellSizeX, 0d, 0d, -cellSizeY, projectedBufferEnvelope.getMinimum(0),
480
            projectedBufferEnvelope.getMaximum(1));
481
    }
482

    
483

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

    
492
    public NoData[] getBandNoData() {
493
        NoData[] bandNoData = new NoData[this.getBandCount()];
494
        for (int i = 0; i < this.bands.size(); i++) {
495
            bandNoData[i] = this.bands.get(i).getNoData();
496
        }
497
        return bandNoData;
498
    }
499

    
500
    @Override
501
    public Buffer clip(Envelope envelope) throws BufferException {
502
        if(!this.envelope.intersects(envelope)){
503
            return null;
504
        }
505
        try {
506
            return BufferLocator.getBufferManager().createClippedBuffer(this, envelope);
507
        } catch (LocatorException | BufferException e) {
508
            throw new BufferException(e);
509
        }
510
    }
511

    
512
    public double getPixelSizeX(){
513
        if(pixelSizeX == null){
514
            pixelSizeX = this.getEnvelope().getLength(0)/this.getColumns();
515
        }
516
        return pixelSizeX.doubleValue();
517
    }
518

    
519
    public double getPixelSizeY(){
520
        if(pixelSizeY == null){
521
            pixelSizeY = this.getEnvelope().getLength(1)/this.getRows();
522
        }
523
        return pixelSizeY.doubleValue();
524
    }
525

    
526
    public final void dispose() {
527
        synchronized (lock) {
528
            // Check if we have already been disposed, and don't do it again
529
            if (!disposed) {
530
                logger.info("DISPOSE rows = "+rows+" columns = "+columns+" hashCode = "+this.hashCode()+" className = "+this.getClass().getSimpleName());
531
                if ( ToolsLocator.getDisposableManager().release(this) ) {
532
                    try {
533
                        doDispose();
534
                    } catch (BaseException ex) {
535
                        logger.error("Error performing dispose", ex);
536
                    }
537
                    disposed = true;
538
                }
539
            }
540
        }
541
    }
542

    
543
    /**
544
     * Internal implementation for the {@link #dispose()} method.
545
     *
546
     * @see #dispose()
547
     */
548
    public void doDispose() throws BaseException {
549
        logger.info("DO DISPOSE rows = "+rows+" columns = "+columns+" hashCode = "+this.hashCode());
550
        for (Iterator iterator = bands.iterator(); iterator.hasNext();) {
551
            Band band = (Band) iterator.next();
552
            DisposeUtils.dispose(band);
553
        }
554
        bands.removeAll(bands);
555
        envelope = null;
556
        projection = null;
557
        //TODO: ?FilterList Disposable?
558
        filters = null;
559
        //TODO: ?Statistics Disposable?
560
        statistics = null;
561

    
562
        pixelSizeX = null;
563
        pixelSizeY = null;
564
    }
565

    
566
    @Override
567
    protected void finalize() throws Throwable {
568
        super.finalize();
569
        logger.info("CLEANED rows = "+rows+" columns = "+columns+" hashCode = "+this.hashCode());
570
    }
571
}