/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2016, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.processing.jai; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.util.LinkedList; import java.util.ListIterator; import javax.media.jai.PixelAccessor; import javax.media.jai.ROI; import javax.media.jai.StatisticsOpImage; import javax.media.jai.UnpackedImageData; import org.geotools.process.raster.classify.Classification; /** * Abstract base class for various operations corresponding to classification method. */ public abstract class ClassBreaksOpImage extends StatisticsOpImage { /* number of classes */ protected Integer numClasses; /* range of values to calculate per band */ protected Double[][] extrema; /* bands to process */ protected Integer[] bands; /* no data value */ protected Double noData; public ClassBreaksOpImage(RenderedImage image, Integer numClasses, Double[][] extrema, ROI roi, Integer[] bands, Integer xStart, Integer yStart, Integer xPeriod, Integer yPeriod, Double noData) { super(image, roi, xStart, yStart, xPeriod, yPeriod); this.numClasses = numClasses; this.extrema = extrema; this.bands = bands; this.noData = noData; } @Override protected String[] getStatisticsNames() { return new String[]{ClassBreaksDescriptor.CLASSIFICATION_PROPERTY}; } @Override public Object getProperty(String name) { Object obj = properties.getProperty(ClassBreaksDescriptor.CLASSIFICATION_PROPERTY); if (obj == java.awt.Image.UndefinedProperty) { // not calculated yet, give subclass a chance to optimize in cases where enough // parameters are specified that the image does not have to be scanned Classification c = preCalculate(); if (c != null) { properties.setProperty(ClassBreaksDescriptor.CLASSIFICATION_PROPERTY, c); } } return super.getProperty(name); } @Override public void setProperty(String name, Object value) { if (value instanceof Classification) { //calculation over, calculate the breaks Classification c = (Classification) value; for (int b = 0; b < bands.length; b++) { postCalculate(c, b); } } super.setProperty(name, value); } @Override protected Object createStatistics(String name) { if (ClassBreaksDescriptor.CLASSIFICATION_PROPERTY.equals(name)) { return createClassification(); } return null; } @Override protected void accumulateStatistics(String name, Raster raster, Object obj) { if (!ClassBreaksDescriptor.CLASSIFICATION_PROPERTY.equals(name)) { return; } Classification c = (Classification) obj; //ClassifiedStats2 stats = (ClassifiedStats2) obj; SampleModel sampleModel = raster.getSampleModel(); Rectangle bounds = raster.getBounds(); LinkedList rectList; if (roi == null) { // ROI is the whole Raster rectList = new LinkedList(); rectList.addLast(bounds); } else { rectList = roi.getAsRectangleList(bounds.x, bounds.y, bounds.width, bounds.height); if (rectList == null) { return; // ROI does not intersect with Raster boundary. } } PixelAccessor accessor = new PixelAccessor(sampleModel, null); ListIterator iterator = rectList.listIterator(0); while (iterator.hasNext()) { Rectangle r = (Rectangle)iterator.next(); int tx = r.x; int ty = r.y; // Find the actual ROI based on start and period. r.x = startPosition(tx, xStart, xPeriod); r.y = startPosition(ty, yStart, yPeriod); r.width = tx + r.width - r.x; r.height = ty + r.height - r.y; if (r.width <= 0 || r.height <= 0) { continue; // no pixel to count in this rectangle } switch (accessor.sampleType) { case PixelAccessor.TYPE_BIT: case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: //countPixelsInt(accessor, raster, r, xPeriod, yPeriod, breaks); //break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: default: calculate(accessor, raster, r, xPeriod, yPeriod, c); break; } } } void calculate(PixelAccessor accessor, Raster raster, Rectangle rect, int xPeriod, int yPeriod, Classification c) { UnpackedImageData uid = accessor.getPixels(raster, rect, DataBuffer.TYPE_DOUBLE, false); double[][] doubleData = uid.getDoubleData(); int pixelStride = uid.pixelStride * xPeriod; int lineStride = uid.lineStride * yPeriod; int[] offsets = uid.bandOffsets; for (int i = 0; i < bands.length; i++) { int b = bands[i]; double[] data = doubleData[b]; int lineOffset = offsets[b]; // line offset for (int h = 0; h < rect.height; h += yPeriod) { int pixelOffset = lineOffset; // pixel offset lineOffset += lineStride; for (int w = 0; w < rect.width; w += xPeriod) { double d = data[pixelOffset]; pixelOffset += pixelStride; // skip no data if (noData != null && noData.equals(d)) { continue; } handleValue(d, c, i); } } } } protected abstract void handleValue(double d, Classification c, int band); protected abstract Classification createClassification(); protected Classification preCalculate() { return null; } protected abstract void postCalculate(Classification c, int band); /** Finds the first pixel at or after <code>pos</code> to be counted. */ private int startPosition(int pos, int start, int Period) { int t = (pos - start) % Period; return t == 0 ? pos : pos + (Period - t); } }