Hi all,

I'm trying to inspect some geotiffs so I can generate color
ramps appropriate to the value range present. but when i retrieve the
extrema for a coverage band the NODATA value is not ignored - so my ramp
ends up being broader than i'd like.  I was hoping to find something like
the existing 
Extrema<http://docs.geotools.org/latest/javadocs/org/geotools/coverage/processing/operation/Extrema.html>
operation,
but with a parameter containing a list of values to ignore (or a list of
such lists per band.)  I don't see such a thing in either GeoTools or JAI
(but I am a JAI newbie so I might have missed something).

In case it's not clear what I'm looking for, find attached a very basic
implementation of what I'm thinking of (see findExtrema at the end of the
file).  I'd be happy to clean it up for inclusion in GeoTools if this is
something that seems desirable, though I might need a bit of help fitting
into the existing Operation system - I  didn't see anything in the docs on
adding new ones to the library.

I also noticed that while GridSampleDimension has a method
getNoDataValues(), it actually returns null for GeoTIFFs, even ones that do
populate the GC_NODATA property.  Is this behavior intentional?

--
David Winslow
OpenGeo - http://opengeo.org/
package org.geonode.rest.statistics;

import java.awt.image.Raster;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Logger;
import net.sf.json.JSONArray;
import net.sf.json.JSONNull;
import net.sf.json.JSONObject;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.rest.RestletException;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.util.logging.Logging;
import org.restlet.Restlet;
import org.restlet.data.MediaType;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.data.Status;

public class GeneralStatisticsRestlet extends Restlet {

    public static final Logger LOGGER = Logging.getLogger("org.geonode.rest.statistics");

    private final Catalog catalog;

    /**
     * @param catalog A reference to the GeoServer catalog
     */
    public GeneralStatisticsRestlet(final Catalog catalog) {
        this.catalog = catalog;
    }

    @Override
    public void handle(Request request, Response response) {
        InputStream data = getClass().getResourceAsStream("dummy.json");
        String workspace = (String)request.getAttributes().get("workspace");
        String layer = (String)request.getAttributes().get("layer");
        CoverageInfo coverage = catalog.getCoverageByName(workspace, layer);
        if (coverage == null) {
            response.setStatus(Status.CLIENT_ERROR_NOT_FOUND);
        } else {
            try {
                GridCoverage2D grid = (GridCoverage2D)coverage.getGridCoverage(null, null);

                double[][] extrema = findExtrema(grid);
                double[] minima = extrema[0], maxima = extrema[1];
                Object nodata = grid.getProperty("GC_NODATA");

                JSONArray container = new JSONArray();
                for (int i = 0; i < minima.length; i++) {
                    JSONObject entry = new JSONObject();
                    entry.put("min", minima[i]);
                    entry.put("max", maxima[i]);
                    entry.put("nodata", (nodata instanceof Number ? nodata : JSONNull.getInstance()));
                    container.add(entry);
                }

                response.setEntity(container.toString(0), MediaType.APPLICATION_JSON);
            } catch (IOException e) {
                throw new RestletException("Failed to access coverage", Status.SERVER_ERROR_INTERNAL, e);
            }
        }
    }

    private static double[][] findExtrema(GridCoverage2D grid) {
        final Object maybeNodata = grid.getProperty("GC_NODATA");
        final Double nodata;
        if (maybeNodata instanceof Number) {
            nodata = ((Number)maybeNodata).doubleValue();
        } else {
            nodata = null;
        }

        Raster raster = grid.getRenderedImage().getData();
        double[] bands = new double[raster.getNumBands()];
        double[] maxima = null;
        double[] minima = null;

        for (int y = raster.getMinY(); y < raster.getMinY() + raster.getHeight(); y++) {
            for (int x = raster.getMinX(); x < raster.getMinX() + raster.getWidth(); x++) {
                raster.getPixel(x, y, bands);
                if (maxima == null && minima == null) {
                    maxima = new double[raster.getNumBands()];
                    System.arraycopy(bands, 0, maxima, 0, bands.length);
                    minima = new double[raster.getNumBands()];
                    System.arraycopy(bands, 0, minima, 0, bands.length);
                } else {
                    for (int i = 0; i < bands.length; i++) {
                        if (nodata == null || nodata != bands[i]) {
                            if (bands[i] > maxima[i] ||
                                    (Math.abs(maxima[i] - nodata) < Math.abs(nodata / 1000d)))
                                maxima[i] = bands[i];
                            if (bands[i] < minima[i] ||
                                    (Math.abs(minima[i] - nodata) < Math.abs(nodata / 1000d)))
                                minima[i] = bands[i];
                        }
                    }
                }
            }
        }
        
        return new double[][] { minima, maxima };
    }
}
------------------------------------------------------------------------------
Xperia(TM) PLAY
It's a major breakthrough. An authentic gaming
smartphone on the nation's most reliable network.
And it wants your games.
http://p.sf.net/sfu/verizon-sfdev
_______________________________________________
Geotools-devel mailing list
Geotools-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/geotools-devel

Reply via email to