Package org.locationtech.udig.style.sld.editor.raster

Source Code of org.locationtech.udig.style.sld.editor.raster.ClassificationEngine

/*
*    uDig - User Friendly Desktop Internet GIS client
*    http://udig.refractions.net
*    (C) 2011, Refractions Research Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* (http://www.eclipse.org/legal/epl-v10.html), and the Refractions BSD
* License v1.0 (http://udig.refractions.net/files/bsd3-v10.html).
*/
package org.locationtech.udig.style.sld.editor.raster;

import java.awt.Rectangle;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.locationtech.udig.style.sld.internal.Messages;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Display;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.processing.OperationJAI;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.parameter.DefaultParameterDescriptor;
import org.geotools.parameter.DefaultParameterDescriptorGroup;
import org.geotools.parameter.ParameterGroup;
import org.jaitools.media.jai.zonalstats.ZonalStats;
import org.jaitools.media.jai.zonalstats.ZonalStatsDescriptor;
import org.jaitools.numeric.Range;
import org.jaitools.numeric.Statistic;
import org.opengis.coverage.grid.GridCoordinates;
import org.opengis.coverage.grid.GridCoverage;
import org.opengis.coverage.grid.GridCoverageReader;
import org.opengis.coverage.grid.GridGeometry;
import org.opengis.parameter.GeneralParameterDescriptor;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterValueGroup;

/**
* Engine for computing classes for raster styling
* @author Emily
*
*/
public class ClassificationEngine {
 
  private String errorMessage = null;
 
  public ClassificationEngine(){
    this.errorMessage = null;
  }
  /*
   * Maximum value for warning users to limit sample size
   */
  public static final Long WARN_VALUE = 1000000l;
 
 
  /**
   * Breaks the given raster into equal interval bins.
   *
   * @param interval size of interval
   * @param valuesToIgnore values to ignore
   * @param layer raster layer
   * @param sampleSize maximum sample size
   * @return list of double values representing the bins or null if cancelled
   * @throws Exception
   */
  public List<Double> computeDefinedInterval(double interval,double[] valuesToIgnore,
      GridCoverageReader layer, Long sampleSize) throws Exception{
    this.errorMessage = null;
    double[] minmax = computeMinMax(valuesToIgnore, layer, sampleSize);
    if (minmax == null){
      return null;
    }
    double min = minmax[0];
    double max = minmax[1];
    ArrayList<Double> breaks = new ArrayList<Double>();
    for (double x = min; x <= max; x += interval){
      breaks.add(x);
    }
    return breaks;
  }
 
  /**
   * Breaks the given raster into equal interval bins.
   *
   * @param numIntervals number of bins
   * @param valuesToIgnore values to ignore
   * @param layer raster layer
   * @param sampleSize maximum sample size
   * @return list of double values representing the bins or null if cancelled
   * @throws Exception
   */
  public List<Double> computeEqualInterval(int numIntervals,double[] valuesToIgnore,
      GridCoverageReader layer, Long sampleSize) throws Exception{
    this.errorMessage = null;
    double[] minmax = computeMinMax(valuesToIgnore, layer, sampleSize);
    if (minmax == null){
      return null;
    }
    double min = minmax[0];
    double max = minmax[1];
    double interval = (max-min) / numIntervals;
   
    ArrayList<Double> breaks = new ArrayList<Double>();
    if (interval == 0){
      breaks.add(min);
    }else{
      double value = min;
      for (int i = 0; i < numIntervals; i ++){
        breaks.add(value);
        value = value + interval;
      }
      breaks.add(max);
    }
    return breaks;
   
  }
 
  /**
   * Prompts the user to ensure they want to continue when
   * using a large sample size;
   * @return true if processing to continue, false otherwise
   */
  private boolean warnLargeSize(){
    final boolean[] ret = {true};
    Display.getDefault().syncExec(new Runnable(){
      @Override
      public void run() {
          if (!MessageDialog.openConfirm(Display.getDefault().getActiveShell(),
              Messages.ClassifyDialog_ConfirmDialogText,
              MessageFormat.format(Messages.ClassifyDialog_RasterCellWaring, new Object[]{WARN_VALUE}))){
            ret[0] = false;
          }
      }});
    return ret[0];
   
  }
 
  /**
   * Computes the minimum and maximum for
   * the only band in a single band raster
   *
   * @param valuesToIgnore values to ignore
   * @param layer raster layer
   * @param sampleSize maximum sample size
   * @return double {minimum, maximum} or null
   * @throws Exception
   */
  public double[] computeMinMax(double[] valuesToIgnore, GridCoverageReader layer, Long sampleSize) throws Exception{
    final Statistic[] stats = new Statistic[] {
        Statistic.MIN,
        Statistic.MAX };
    this.errorMessage = null;
    List<Range<Double>> ignore = new ArrayList<Range<Double>>();
    if (valuesToIgnore != null){
      for (Double no : valuesToIgnore){
        ignore.add(new Range<Double>(no));
      }
    }
   
    GridCoverage gcRaw = layer.read(null);
   
   
    if (sampleSize != null){
      int rSize = (int) Math.ceil(Math.sqrt(sampleSize.doubleValue()));
      GridEnvelope2D gridRange = new GridEnvelope2D(new Rectangle(0,0, rSize, rSize));
      GridGeometry2D world = new GridGeometry2D(gridRange,  new ReferencedEnvelope(gcRaw.getEnvelope()));
      DefaultParameterDescriptor<GridGeometry> gridGeometryDescriptor = new DefaultParameterDescriptor<GridGeometry>(
          AbstractGridFormat.READ_GRIDGEOMETRY2D.getName()
              .toString(), GridGeometry.class, null, world);

      ParameterGroup readParams = new ParameterGroup(
          new DefaultParameterDescriptorGroup(
              "Test", //$NON-NLS-1$
              new GeneralParameterDescriptor[] { gridGeometryDescriptor }));

      List<GeneralParameterValue> list = readParams.values();
      GeneralParameterValue[] values = list
          .toArray(new GeneralParameterValue[0]);
      gcRaw = layer.read(values);
    }
     
    GridCoordinates high = gcRaw.getGridGeometry().getGridRange().getHigh();
    GridCoordinates low = gcRaw.getGridGeometry().getGridRange().getLow();
    int width = high.getCoordinateValue(0) - low.getCoordinateValue(0);
    int height = high.getCoordinateValue(1) - low.getCoordinateValue(1);
    if (width * height > WARN_VALUE){
      if (!warnLargeSize()){
        return null;
      }
     
    }
     
    final OperationJAI op = new OperationJAI("ZonalStats"); //$NON-NLS-1$
    ParameterValueGroup params = op.getParameters();
    params.parameter("dataImage").setValue(gcRaw); //$NON-NLS-1$
    params.parameter("stats").setValue(stats); //$NON-NLS-1$
    params.parameter("bands").setValue(new Integer[] { 0 }); //$NON-NLS-1$
    if (ignore.size() > 0){
      params.parameter("ranges").setValue(ignore); //$NON-NLS-1$
      params.parameter("rangesType").setValue(Range.Type.EXCLUDE); //$NON-NLS-1$
      params.parameter("rangeLocalStats").setValue(false); //$NON-NLS-1$
    }
   

    final GridCoverage2D coverage = (GridCoverage2D) op.doOperation(params,null);
    final ZonalStats zstats = (ZonalStats) coverage
        .getProperty(ZonalStatsDescriptor.ZONAL_STATS_PROPERTY);
    double min = zstats.statistic(Statistic.MIN).results().get(0).getValue();
    double max = zstats.statistic(Statistic.MAX).results().get(0).getValue();
    return new double[]{min,max};
  }
 
  /**
   * Computes quantile breaks for the given layer.
   *
   * @param numBins number of bins
   * @param valuesToIgnore values to ignore
   * @param layer raster layer to sample
   * @param sampleSize maximum sample size
   * @param monitor progress monitor
   * @return list of doubles representing the quantile breaks or null if cancelled
   * @throws Exception
   */
  public List<Double> computeQuantile(int numBins,
      double[] valuesToIgnore,
      GridCoverageReader layer,
      Long sampleSize,
      IProgressMonitor monitor) throws Exception{
    this.errorMessage = null;
    GridCoverage gcRaw = layer.read(null);
    if (sampleSize != null){
      int rSize = (int) Math.ceil(Math.sqrt(sampleSize.doubleValue()));
      GridEnvelope2D gridRange = new GridEnvelope2D(new Rectangle(0,0, rSize, rSize));
      GridGeometry2D world = new GridGeometry2D(gridRange,  new ReferencedEnvelope(gcRaw.getEnvelope()));
      DefaultParameterDescriptor<GridGeometry> gridGeometryDescriptor = new DefaultParameterDescriptor<GridGeometry>(
          AbstractGridFormat.READ_GRIDGEOMETRY2D.getName()
              .toString(), GridGeometry.class, null, world);

      ParameterGroup readParams = new ParameterGroup(
          new DefaultParameterDescriptorGroup(
              "Test", //$NON-NLS-1$
              new GeneralParameterDescriptor[] { gridGeometryDescriptor }));

      List<GeneralParameterValue> list = readParams.values();
      GeneralParameterValue[] values = list
          .toArray(new GeneralParameterValue[0]);
      gcRaw = layer.read(values);
    }
           
    if (monitor.isCanceled()){
      return null;
    }
    GridCoordinates high = gcRaw.getGridGeometry().getGridRange().getHigh();
    GridCoordinates low = gcRaw.getGridGeometry().getGridRange().getLow();
    int width = high.getCoordinateValue(0) - low.getCoordinateValue(0);
    int height = high.getCoordinateValue(1) - low.getCoordinateValue(1);
   
    if (width * height > WARN_VALUE){
      if (!warnLargeSize()){
        return null;
      }
    }
   
    int recSize = 1000;
    HashSet<Double> ignoreset = new HashSet<Double>();
    if (valuesToIgnore != null){
      for (Double d : valuesToIgnore){
        ignoreset.add(d);
      }
    }

    List<Double> data = new ArrayList<Double>(width * height);
    for (int x = 0; x < width; x+=recSize){
      for (int y = 0; y < height; y += recSize){
        int w = recSize;
        int h = recSize;
        if (x + recSize > width){
          w = width - x;
        }
        if (y + recSize > height){
          h = height - y;
        }
        Rectangle r = new Rectangle(x, y, w, h);
        Raster rs = gcRaw.getRenderedImage().getData(r);
        DataBuffer df  = rs.getDataBuffer();
        for (int i = 0; i < df.getSize(); i ++){
          Double v = df.getElemDouble(i);
          if (!ignoreset.contains(v)){
            data.add(v);
          }
        }
      }
      if (monitor.isCanceled()){
        return null;
      }
    }
   
   
     // sort the list
    Collections.sort(data);

 
    if (numBins > data.size()) { //resize
      numBins = data.size();
    }
    double min[] = new double[numBins];
    double max[] = new double[numBins];
    for( int i = 0; i < numBins; i ++){
      min[i] = Double.POSITIVE_INFINITY;
      max[i] = Double.NEGATIVE_INFINITY;
    }
   
    // calculate number of items to put into each of the larger bins
    int binPop = new Double(Math.ceil((double) data.size() / numBins)).intValue();
    // determine index of bin where the next bin has one less item
    int lastBigBin = data.size() % numBins;
    if (lastBigBin == 0) lastBigBin = numBins;
    else lastBigBin--;

    // put the items into their respective bins
    int item = 0;
    for (int binIndex = 0; binIndex < numBins; binIndex++) {
      for (int binMember = 0; binMember < binPop; binMember++) {
        double value = data.get(item++);
        if (min[binIndex] > value){
          min[binIndex] = value;
        }
        if (max[binIndex] < value){
          max[binIndex] = value;
        }
      }
      if (lastBigBin == binIndex)
        binPop--; // decrease the number of items in a bin for the next item
    }
   
    ArrayList<Double> results = new ArrayList<Double>(numBins + 1);
    for (int i = 0 ; i< numBins; i ++){
      results.add(min[i]);
    }
    results.add(max[numBins-1]);
    return results;
   
  }
 
  /**
   *
   * @param layer the raster to read
   * @param sampleSize the maximum number of values to sample
   * @param monitor
   * @return list of unique values found in the raster or null
   * if cancelled
   * @throws IOException
   * @throws IllegalArgumentException
   */
  public Set<Double> computeUniqueValues(GridCoverageReader layer, Long sampleSize, IProgressMonitor monitor) throws IllegalArgumentException, IOException{
    this.errorMessage = null;
    GridCoverage gcRaw = layer.read(null);
    if (sampleSize != null){
      int rSize = (int) Math.ceil(Math.sqrt(sampleSize.doubleValue()));
      GridEnvelope2D gridRange = new GridEnvelope2D(new Rectangle(0,0, rSize, rSize));
      GridGeometry2D world = new GridGeometry2D(gridRange,  new ReferencedEnvelope(gcRaw.getEnvelope()));
      DefaultParameterDescriptor<GridGeometry> gridGeometryDescriptor = new DefaultParameterDescriptor<GridGeometry>(
          AbstractGridFormat.READ_GRIDGEOMETRY2D.getName()
              .toString(), GridGeometry.class, null, world);

      ParameterGroup readParams = new ParameterGroup(
          new DefaultParameterDescriptorGroup(
              "Test", //$NON-NLS-1$
              new GeneralParameterDescriptor[] { gridGeometryDescriptor }));

      List<GeneralParameterValue> list = readParams.values();
      GeneralParameterValue[] values = list
          .toArray(new GeneralParameterValue[0]);
      gcRaw = layer.read(values);
    }
           
    if (monitor.isCanceled()){
      return null;
    }
    GridCoordinates high = gcRaw.getGridGeometry().getGridRange().getHigh();
    GridCoordinates low = gcRaw.getGridGeometry().getGridRange().getLow();
    int width = high.getCoordinateValue(0) - low.getCoordinateValue(0);
    int height = high.getCoordinateValue(1) - low.getCoordinateValue(1);
   
    if (width * height > WARN_VALUE){
      if (!warnLargeSize()){
        return null;
      }
    }
   
    int recSize = 1000;
    boolean maxReached = false;
    HashSet<Double> results = new HashSet<Double>();
    for (int x = 0; x < width; x+=recSize){
      for (int y = 0; y < height; y += recSize){
        int w = recSize;
        int h = recSize;
        if (x + recSize > width){
          w = width - x;
        }
        if (y + recSize > height){
          h = height - y;
        }
        Rectangle r = new Rectangle(x, y, w, h);
       
        Raster rs = gcRaw.getRenderedImage().getData(r);
        DataBuffer df  = rs.getDataBuffer();
        for (int i = 0; i < df.getSize(); i ++){
          results.add(df.getElemDouble(i));
          if (results.size() >= SingleBandEditorPage.MAX_ENTRIES){
            errorMessage = MessageFormat.format(Messages.UniqueValuesDialog_MaxValueError, SingleBandEditorPage.MAX_ENTRIES);
            maxReached = true;
            break;
          }
        }
      }
      if (maxReached){
        break;
      }
      if (monitor.isCanceled()){
        return null;
      }
    }
    return results;
  }
 
  /**
   * Returns the last error message generated by the engine;
   * @return
   */
  public String getLastErrorMessage(){
    return this.errorMessage;
  }
}
TOP

Related Classes of org.locationtech.udig.style.sld.editor.raster.ClassificationEngine

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.