/* * 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.image.RenderedImage; import java.util.Collections; import java.util.List; import javax.media.jai.ROI; import org.geotools.process.raster.classify.Classification; import org.geotools.process.raster.classify.NaturalClassification; /** * Classification op for the natural breaks method. */ public class NaturalBreaksOpImage extends ClassBreaksOpImage { public NaturalBreaksOpImage(RenderedImage image, Integer numClasses, Double[][] extrema, ROI roi, Integer[] bands, Integer xStart, Integer yStart, Integer xPeriod, Integer yPeriod, Double noData) { super(image, numClasses, extrema, roi, bands, xStart, yStart, xPeriod, yPeriod, noData); } @Override protected Classification createClassification() { return new NaturalClassification(bands.length); } @Override protected void handleValue(double d, Classification c, int band) { if (extrema != null) { double min = extrema[0][band]; double max = extrema[1][band]; if (d < min || d > max) { return; } } ((NaturalClassification)c).count(d, band); } @Override protected void postCalculate(Classification c, int band) { NaturalClassification nc = (NaturalClassification) c; List<Double> data = nc.getValues(band); Collections.sort(data); final int k = numClasses; final int m = data.size(); if (k >= m) { //just return all the values c.setBreaks(band, data.toArray(new Double[data.size()])); return; } int[][] iwork = new int[m + 1][k + 1]; double[][] work = new double[m + 1][k + 1]; for (int j = 1; j <= k; j++) { // the first item is always in the first class! iwork[0][j] = 1; iwork[1][j] = 1; // initialize work matirix work[1][j] = 0; for (int i = 2; i <= m; i++) { work[i][j] = Double.MAX_VALUE; } } // calculate the class for each data item for (int i = 1; i <= m; i++) { // sum of data values double s1 = 0; // sum of squares of data values double s2 = 0; double var = 0.0; // consider all the previous values for (int ii = 1; ii <= i; ii++) { // index in to sorted data array int i3 = i - ii + 1; // remember to allow for 0 index double val = data.get(i3 - 1); // update running totals s2 = s2 + (val * val); s1 += val; double s0 = (double) ii; // calculate (square of) the variance // (http://secure.wikimedia.org/wikipedia/en/wiki/Standard_deviation#Rapid_calculation_methods) var = s2 - ((s1 * s1) / s0); // System.out.println(s0+" "+s1+" "+s2); // System.out.println(i+","+ii+" var "+var); int ik = i3 - 1; if (ik != 0) { // not the last value for (int j = 2; j <= k; j++) { // for each class compare current value to var + previous value // System.out.println("\tis "+work[i][j]+" >= "+(var + work[ik][j - 1])); if (work[i][j] >= (var + work[ik][j - 1])) { // if it is greater or equal update classification iwork[i][j] = i3 - 1; // System.out.println("\t\tiwork["+i+"]["+j+"] = "+i3); work[i][j] = var + work[ik][j - 1]; } } } } // store the latest variance! iwork[i][1] = 1; work[i][1] = var; } Double[] breaks = new Double[k+1]; // go through matrix and extract class breaks int ik = m - 1; breaks[k] = data.get(ik); for (int j = k; j >= 2; j--) { int id = (int) iwork[ik][j] - 1; // subtract one as we want inclusive breaks on the // left? breaks[j-1] = data.get(id); ik = (int) iwork[ik][j] - 1; } breaks[0] = data.get(0); nc.setBreaks(band, breaks); } }