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 @ 8696

History | View | Annotate | Download (21 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.SUBTYPES;
15
import org.gvsig.fmap.geom.Geometry.TYPES;
16
import org.gvsig.fmap.geom.GeometryLocator;
17
import org.gvsig.fmap.geom.exception.CreateGeometryException;
18
import org.gvsig.fmap.geom.operation.GeometryOperationException;
19
import org.gvsig.fmap.geom.operation.GeometryOperationNotSupportedException;
20
import org.gvsig.fmap.geom.primitive.Envelope;
21
import org.gvsig.fmap.geom.primitive.Point;
22
import org.gvsig.raster.lib.buffer.api.Band;
23
import org.gvsig.raster.lib.buffer.api.Band.BandByte;
24
import org.gvsig.raster.lib.buffer.api.Band.BandDouble;
25
import org.gvsig.raster.lib.buffer.api.Band.BandFloat;
26
import org.gvsig.raster.lib.buffer.api.Band.BandInt;
27
import org.gvsig.raster.lib.buffer.api.Band.BandShort;
28
import org.gvsig.raster.lib.buffer.api.BandInfo;
29
import org.gvsig.raster.lib.buffer.api.BandNotification;
30
import org.gvsig.raster.lib.buffer.api.Buffer;
31
import org.gvsig.raster.lib.buffer.api.BufferDimensions;
32
import org.gvsig.raster.lib.buffer.api.BufferLocator;
33
import org.gvsig.raster.lib.buffer.api.BufferManager;
34
import org.gvsig.raster.lib.buffer.api.BufferNotification;
35
import org.gvsig.raster.lib.buffer.api.NoData;
36
import org.gvsig.raster.lib.buffer.api.exceptions.BandException;
37
import org.gvsig.raster.lib.buffer.api.exceptions.BufferException;
38
import org.gvsig.raster.lib.buffer.api.statistics.Statistics;
39
import org.gvsig.raster.lib.buffer.impl.statistics.DefaultStatistics;
40
import org.gvsig.tools.ToolsLocator;
41
import org.gvsig.tools.dispose.DisposeUtils;
42
import org.gvsig.tools.exception.BaseException;
43
import org.gvsig.tools.locator.LocatorException;
44
import org.gvsig.tools.observer.Notification;
45
import org.gvsig.tools.observer.Observable;
46
import org.gvsig.tools.observer.impl.BaseWeakReferencingObservable;
47
import org.gvsig.tools.task.SimpleTaskStatus;
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 FilterList filters;
60
    protected IProjection projection;
61
    protected Statistics statistics;
62
    protected BufferDimensions dimensions = null;
63

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

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

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

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

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

    
111
//    @Override
112
//    public void filter(FilterList filterList) {
113
//        this.filters = filterList;
114
//    }
115

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

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

    
127
    @Override
128
    public int getColumns() {
129
        return this.dimensions.getColumns();
130
    }
131

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

    
137
    @Override
138
    public Envelope getEnvelope() {
139
        return this.dimensions.getEnvelope();
140
    }
141

    
142
    @Override
143
    public BufferDimensions getDimensions() {
144
        return this.dimensions;
145
    }
146

    
147
    @Override
148
    public IProjection getProjection() {
149
        return this.projection;
150
    }
151

    
152
    @Override
153
    public boolean isInside(int cellX, int cellY) {
154
        return (cellX >= 0 && cellX < this.getColumns() && cellY >= 0 && cellY < this.getRows());
155
    }
156

    
157
    @Override
158
    public boolean isInside(Point point) {
159

    
160
        if (getEnvelope() == null) {
161
            return false;
162
        }
163

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

    
172
    @Override
173
    public void addBand(Band band) {
174

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

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

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

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

    
202
    @Override
203
    public Band getBand(int pos) {
204
        return this.bands.get(pos);
205
    }
206

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

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

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

    
233
    }
234

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

    
243
    }
244

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

    
253
    }
254

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

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

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

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

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

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

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

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

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

    
348
        int[] bandDataTypes = this.getBandTypes();
349
        NoData[] bandNoData = this.getBandNoData();
350

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

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

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

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

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

    
385
            if (status.isCancellationRequested()) {
386
                status.cancel();
387
                return projectedBuffer;
388
            }
389

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

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

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

    
410
                for (int k = 0; k < this.getBandCount(); k++) {
411

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

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

    
446
            }
447
        }
448

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

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

    
471
    private AffineTransform calculateAffineTransform(Envelope projectedBufferEnvelope, int projectedBufferRows,
472
        int projectedBufferColumns) {
473

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

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

    
481

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

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

    
498
    @Override
499
    public Buffer clip(Envelope envelope) throws BufferException {
500
        if(!this.getEnvelope().intersects(envelope)) {
501
            return null;
502
        }
503
        try {
504
            if(this.getEnvelope().getGeometry().intersection(envelope.getGeometry())==null) {
505
                return null;
506
            }
507
        } catch (GeometryOperationNotSupportedException | GeometryOperationException e1) {
508
            logger.warn("Can't get intersection between two envelopes", e1);
509
            return null;
510
        }
511
        try {
512
            return BufferLocator.getBufferManager().createClippedBuffer(this, envelope);
513
        } catch (LocatorException | BufferException e) {
514
            throw new BufferException(e);
515
        }
516
    }
517

    
518
    public double getPixelSizeX(){
519
        return this.dimensions.getPixelSizeX();
520
    }
521

    
522
    public double getPixelSizeY(){
523
        return this.dimensions.getPixelSizeY();
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
                if ( ToolsLocator.getDisposableManager().release(this) ) {
531
                    try {
532
                        doDispose();
533
                    } catch (BaseException ex) {
534
                        logger.error("Error performing dispose", ex);
535
                    }
536
                    disposed = true;
537
                }
538
            }
539
        }
540
    }
541

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

    
561
    @Override
562
    protected void finalize() throws Throwable {
563
        super.finalize();
564
    }
565

    
566
    @Override
567
    public BandInfo[] getBandsInfo() {
568
        BandInfo[] bandsInfo = new BandInfo[this.bands.size()];
569
        for (int i = 0; i < this.bands.size(); i++) {
570
            bandsInfo[i] = this.bands.get(i).getBandInfo();
571
        }
572
        return bandsInfo;
573
    }
574
}