/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2014, 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.process.raster;
import java.util.List;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
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.raster.MarchingSquaresVectorizer.ImageLoadingType;
import org.geotools.util.Range;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.util.ProgressListener;
import com.vividsolutions.jts.geom.Geometry;
/**
* A process to extract footprint from a raster.
* Result is returned as a {@linkplain SimpleFeatureCollection}
* containing the Geometry of the footprint as first element of the
* collection.
*
* Optionally, in case the {@code computeSimplifiedFootprint} parameter has been
* set to {@code true}, the simplified footprint will be provided as second element
* of the collection.
*
* <p>
* By default, the footprint is computed by looking for not-zero pixels.
* Luminance is computed on the input dataset and not-zero luminance pixels will be used.
*
* <p>
* An optional {@code exclusionRanges} parameter is supported to define which luminance values
* should be excluded from the search. This allows you, as an instance, to exclude
* "Dark pixels / Almost black pixels / White pixels" from the results.
*
* <p>
* An optional {@code thresholdArea} parameter is supported to exclude small polygons from
* the final result. In case a polygon has an area (in pixels) smaller than the provided value
* it will not be included in the footprint.
* Default Threshold Area is {@link MarchingSquaresVectorizer#DEFAULT_THRESHOLD_AREA}
*
* <p>
* An optional {@code computeSimplifiedFootprint} parameter is supported to return a simplified
* version of the biggest polygon as second element of the feature collection.
*
* <p>
* An optional {@code simplifierFactor} parameter is supported to specify the simplifier factor
* to be applied to get the simplified version of the Footprint.
*
* <p>
* An optional {@code removeCollinear} parameter is supported to specify whether the collinear
* vertices of the retrieved polygon should be removed (Default is true)
*
* <p>
* An optional {@code forceValid} parameter is supported to specify whether polygons should be
* forced to be valid (also removing holes)
*
* <p>
* An optional {@code loadingType} parameter is supported to specify the type of imageLoading
* (DEFERRED vs IMMEDIATE). Default is {@link ImageLoadingType#getDefault()}.
*
* @author Daniele Romagnoli, GeoSolutions SAS
*/
@DescribeProcess(title = "Footprint Extraction", description = "Extract footprint from a raster")
public class FootprintExtractionProcess implements RasterProcess {
/**
* Executes the raster to vector process.
*
* @param coverage the data coverage
*
* @param exclusionRanges An optional {@code exclusionRanges} parameter is supported to define
* which luminance values should be excluded from the search. This allows you, as an
* instance, to exclude "Dark pixels / Almost black pixels" from the results.
*
* @param thresholdArea An optional {@code thresholdArea} parameter is supported to exclude
* small polygons from the final result. In case a polygon has an area (in pixels)
* smaller than the provided value it will not be included in the footprint. Default
* Threshold Area is {@link MarchingSquaresVectorizer#DEFAULT_THRESHOLD_AREA}
*
* @param computeSimplifiedFootprint An optional {@code computeSimplifiedFootprint}
* parameter is supported to return a simplified version of the biggest polygon.
*
* @param simplifierFactor the simplifier factor to be applied to compute the simplified
* version of the biggest polygon.
*
* @param imageLoadingType the type of imageLoading (DEFERRED vs IMMEDIATE).
*
* @param removeCollinear specifies whether the collinear vertices of the retrieved
* polygon should be removed (Default is true)
*
* @param forceValid specifies whether polygons should be forced to be valid (also removing holes)
*
* @param imageLoadingType specifies the type of imageLoading (DEFERRED vs IMMEDIATE).
* Default is {@link ImageLoadingType#getDefault()}.
*
* @param progressListener
* @return
* @throws ProcessException
*/
@DescribeResult(name = "result", description = "The compute footprint geometry")
public SimpleFeatureCollection execute(
@DescribeParameter(name = "data", description = "Source raster") GridCoverage2D coverage,
@DescribeParameter(name = "exclusionRanges", description = "the ranges of luminance values to be excluded by the computation.", min = 0) List<Range<Integer>> exclusionRanges,
@DescribeParameter(name = "thresholdArea", description = "Indicates the minimum area of a polygon to be included in the final result", min = 0) Double thresholdArea,
@DescribeParameter(name = "computeSimplifiedFootprint", description = "Indicates whether the simplified footprint should be computed", min = 0) Boolean computeSimplifiedFootprint,
@DescribeParameter(name = "simplifierFactor", description = "Indicates the simplifier factor to be applied when computing the simplified footprint", min = 0) Double simplifierFactor,
@DescribeParameter(name = "removeCollinear", description = "Indicates whether remove collinear point should be applied", min = 0) Boolean removeCollinear,
@DescribeParameter(name = "forceValid", description = "Indicates whether polygon should be forced to be valid, also removing holes", min = 0) Boolean forceValid,
@DescribeParameter(name = "loadingType", description = "Indicates which type of imageLoading should be performed (DEFERRED vs IMMEDIATE)", min = 0) ImageLoadingType imageLoadingType,
ProgressListener progressListener)
throws ProcessException {
//
// initial checks
//
if (coverage == null) {
throw new ProcessException("Invalid input, source grid coverage should be not null");
}
// Checking for defaults
if (exclusionRanges == null) {
exclusionRanges = MarchingSquaresVectorizer.DEFAULT_RANGES;
}
if (computeSimplifiedFootprint == null) {
computeSimplifiedFootprint = false;
}
if (simplifierFactor == null) {
simplifierFactor = MarchingSquaresVectorizer.DEFAULT_SIMPLIFIER_FACTOR;
}
if (forceValid == null) {
forceValid = true;
}
if (removeCollinear == null) {
removeCollinear = true;
}
if (imageLoadingType == null) {
imageLoadingType = ImageLoadingType.getDefault();
}
if (thresholdArea == null) {
thresholdArea = MarchingSquaresVectorizer.DEFAULT_THRESHOLD_AREA;
}
MarchingSquaresVectorizer vectorizer = new MarchingSquaresVectorizer(coverage, null, thresholdArea, simplifierFactor, imageLoadingType, exclusionRanges);
vectorizer.setComputeSimplifiedFootprint(computeSimplifiedFootprint);
vectorizer.setForceValid(forceValid);
vectorizer.setRemoveCollinear(removeCollinear);
try {
vectorizer.process();
Geometry geometry = vectorizer.getFootprint();
// wrap as a feature collection and return
final SimpleFeatureType featureType = CoverageUtilities.createFeatureType(coverage,
Geometry.class);
final SimpleFeatureBuilder builder = new SimpleFeatureBuilder(featureType);
int i = 0;
final ListFeatureCollection featureCollection = new ListFeatureCollection(featureType);
// create feature and add to list
builder.set("the_geom", geometry);
featureCollection.add(builder.buildFeature(String.valueOf(i++)));
if (computeSimplifiedFootprint) {
builder.set("the_geom", vectorizer.getSimplifiedFootprint());
featureCollection.add(builder.buildFeature(String.valueOf(i++)));
}
return featureCollection;
} catch (Exception e) {
throw new ProcessException("Exception occurred while computing the footprint", e);
} finally {
vectorizer.dispose();
}
}
}