/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2011, Open Source Geospatial Foundation (OSGeo) * (C) 2008-2011 TOPP - www.openplans.org. * * 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.process.raster.gs; import java.awt.geom.AffineTransform; import java.awt.image.RenderedImage; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.media.jai.JAI; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderedOp; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.util.AffineTransformation; import org.jaitools.media.jai.contour.ContourDescriptor; import org.jaitools.media.jai.contour.ContourRIF; import org.jaitools.numeric.Range; import org.geotools.coverage.Category; import org.geotools.coverage.GridSampleDimension; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.grid.ViewType; import org.geotools.data.collection.ListFeatureCollection; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.image.jai.Registry; import org.geotools.process.ProcessException; import org.geotools.process.factory.DescribeParameter; import org.geotools.process.factory.DescribeProcess; import org.geotools.process.factory.DescribeResult; import org.geotools.process.gs.GSProcess; import org.geotools.process.raster.CoverageUtilities; import org.geotools.resources.i18n.Vocabulary; import org.geotools.resources.i18n.VocabularyKeys; import org.geotools.util.NumberRange; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.metadata.spatial.PixelOrientation; import org.opengis.util.InternationalString; import org.opengis.util.ProgressListener; /** * A process to extract contours based on values in a specified band of the * input {@linkplain GridCoverage2D}. This is a geo-spatial wrapper around the JAITools * "Contour" operation (see {@linkplain ContourDescriptor} for details of the underlying * algorithm). *
* You can specify the specific values for which contours will be generated, or alternatively * the interval between contour values. *
* Contours are returned as a feature collection, where each feature has, as its default * geometry, a {@linkplain LineString} for the contour ("the_geom"), and the contour * value as the {@code Double} attribute "value". * * @author Simone Giannecchini, GeoSolutions * @since 8.0 * * @source $URL$ * @version $Id$ */ @DescribeProcess(title = "Contour", description = "Perform the contouring on a provided raster") public class ContourProcess implements GSProcess { private static final InternationalString NO_DATA = Vocabulary .formatInternational(VocabularyKeys.NODATA); static { Registry.registerRIF(JAI.getDefaultInstance(), new ContourDescriptor(), new ContourRIF(), Registry.JAI_TOOLS_PRODUCT); } /** * Perform the contouring on the input {@linkplain GridCoverage2D} and returns * the results as a feature collection. You can control which contours are generated * either by providing a list of values via the {@code levels} argument, or by specifying * the interval between contour values via the {@code interval} argument. In the interval * case, the resulting contour values will be integer multiples of the specified interval. * If both {@code levels} and {@code interval} are supplied the {@code interval} argument * is ignored. * * @param data the input grid coverage * * @param band the coverage band to process; defaults to 0 if {@code null} * * @param levels the values for which contours should be generated * * @param interval the interval between contour values (if {@code levels} is not provided) * * @param simplify whether to simplify contour lines by removing co-linear vertices; * default is to simplify * * @param smooth whether to apply Bezier smooth to the contours; default is no smoothing * * @param roi an optional polygonal {@code Geometry} to define the region of interest * within which contours will be generated * * @return the contours a feature collection where each feature contains a contour * as a {@linkplain LineString} and the contour value as a {@code Double} * * @throws ProcessException */ public static SimpleFeatureCollection process(GridCoverage2D gc2d, Integer band, double[] levels, Double interval, Boolean simplify, Boolean smooth, Geometry roi, ProgressListener progressListener) throws ProcessException { ContourProcess process = new ContourProcess(); return process.execute(gc2d, band, levels, interval, simplify, smooth, roi, progressListener); } @DescribeResult(name = "result", description = "The contours feature collection") public SimpleFeatureCollection execute( @DescribeParameter(name = "data", description = "The raster to be used as the source") GridCoverage2D gc2d, @DescribeParameter(name = "band", description = "The source image band to process", min = 0, max = 1) Integer band, @DescribeParameter(name = "levels", description = "Values for which to generate contours") double[] levels, @DescribeParameter(name = "interval", description = "Interval between contour values (ignored if levels arg is supplied)", min = 0) Double interval, @DescribeParameter(name = "simplify", description = "Values for which to generate contours", min = 0) Boolean simplify, @DescribeParameter(name = "smooth", description = "Values for which to generate contours", min = 0) Boolean smooth, @DescribeParameter(name = "roi", description = "The geometry used to delineate the area of interest in model space", min = 0) Geometry roi, ProgressListener progressListener) throws ProcessException { // // initial checks // if (gc2d == null) { throw new ProcessException("Invalid input, source grid coverage should be not null"); } if (band != null && (band < 0 || band >= gc2d.getNumSampleDimensions())) { throw new ProcessException("Invalid input, invalid band number:" + band); } boolean hasValues = !(levels == null || levels.length == 0); if (!hasValues && interval == null) { throw new ProcessException("One between interval and values must be valid"); } // switch to geophisics if necessary gc2d = gc2d.view(ViewType.GEOPHYSICS); // // GRID TO WORLD preparation // final AffineTransform mt2D = (AffineTransform) gc2d.getGridGeometry().getGridToCRS2D( PixelOrientation.CENTER); // get the list of nodata, if any List