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

History | View | Annotate | Download (21.3 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
            } else if (bandNotification.getType().equals(BandNotification.LOADED_PAGE)){
108
                this.notifyObservers(new DefaultBufferNotification(BufferNotification.LOADED_PAGE,
109
                    new Object[] { this }));
110
            }
111
        }
112
    }
113

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
236
    }
237

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

    
246
    }
247

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

    
256
    }
257

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
449
            }
450
        }
451

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

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

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

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

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

    
484

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

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

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

    
521
    public double getPixelSizeX(){
522
        return this.dimensions.getPixelSizeX();
523
    }
524

    
525
    public double getPixelSizeY(){
526
        return this.dimensions.getPixelSizeY();
527
    }
528

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

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

    
564
    @Override
565
    protected void finalize() throws Throwable {
566
        super.finalize();
567
    }
568

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