FootprintExtractionProcess implements RasterProcess { @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 { if (coverage == null) { throw new ProcessException("Invalid input, source grid coverage should be not null"); } 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(); final SimpleFeatureType featureType = CoverageUtilities.createFeatureType(coverage, Geometry.class); final SimpleFeatureBuilder builder = new SimpleFeatureBuilder(featureType); int i = 0; final ListFeatureCollection featureCollection = new ListFeatureCollection(featureType); 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(); } }  @DescribeResult(name = "result", description = "The compute footprint geometry") 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);  }
@Test public void cloudExtractionTest() throws Exception { GeoTiffReader reader = null; FeatureIterator<SimpleFeature> iter = null; GridCoverage2D cov = null; try { reader = new GeoTiffReader(cloudFile); cov = reader.read(null); SimpleFeatureCollection fc = process.execute(cov, null, 10d, false, null, true, true, null, null); assertEquals(1, fc.size()); iter = fc.features(); SimpleFeature feature = iter.next(); MultiPolygon geometry = (MultiPolygon) feature.getDefaultGeometry(); assertTrue(referenceGeometry.equalsExact(geometry, TOLERANCE)); } finally { if (iter != null) { iter.close(); } if (reader != null) { try { reader.dispose(); } catch (Throwable t) { } } if (cov != null) { try { cov.dispose(true); } catch (Throwable t) { } } } }
@Test public void cloudExtractionSimplified() throws Exception { GeoTiffReader reader = null; FeatureIterator<SimpleFeature> iter = null; GridCoverage2D cov = null; try { reader = new GeoTiffReader(cloudFile); cov = reader.read(null); SimpleFeatureCollection fc = process.execute(cov, null, 10d, true, 4d, true, true, null, null); assertEquals(2, fc.size()); iter = fc.features(); SimpleFeature feature = iter.next(); Geometry geometry = (Geometry) feature.getDefaultGeometry(); double fullArea = geometry.getArea(); feature = (SimpleFeature) iter.next(); geometry = (Geometry) feature.getDefaultGeometry(); double simplifiedArea = geometry.getArea(); assertTrue(Math.abs(simplifiedArea - fullArea) > 0); assertFalse(referenceGeometry.equalsExact(geometry, TOLERANCE)); } finally { if (iter != null) { iter.close(); } if (reader != null) { try { reader.dispose(); } catch (Throwable t) { } } if (cov != null) { try { cov.dispose(true); } catch (Throwable t) { } } } }
@Test public void cloudExtractionNoRemoveCollinear() throws Exception { GeoTiffReader reader = null; FeatureIterator<SimpleFeature> iter = null; GridCoverage2D cov = null; try { reader = new GeoTiffReader(cloudFile); cov = reader.read(null); SimpleFeatureCollection fc = process.execute(cov, null, 10d, false, null, false, true, null, null); iter = fc.features(); SimpleFeature feature = iter.next(); Geometry geometry = (Geometry) feature.getDefaultGeometry(); final int removeCollinearLength = referenceGeometry.getGeometryN(0).getCoordinates().length; assertEquals(133, removeCollinearLength); final int length = geometry.getGeometryN(0).getCoordinates().length; assertTrue(length > removeCollinearLength); assertFalse(referenceGeometry.equalsExact(geometry, TOLERANCE)); } finally { if (iter != null) { iter.close(); } if (reader != null) { try { reader.dispose(); } catch (Throwable t) { } } if (cov != null) { try { cov.dispose(true); } catch (Throwable t) { } } } }
@Test public void cloudExtractionWithoutDarkPixels() throws Exception { GeoTiffReader reader = null; FeatureIterator<SimpleFeature> iter = null; GridCoverage2D cov = null; try { reader = new GeoTiffReader(cloudFile); cov = reader.read(null); final int referenceLuminance = 10; List<Range<Integer>> exclusionRanges = Collections.singletonList(new Range<Integer>(Integer.class, 0, referenceLuminance)); SimpleFeatureCollection fc = process.execute(cov, exclusionRanges, 10d, false, null, true, true, null, null); iter = fc.features(); SimpleFeature feature = iter.next(); Geometry geometry = (Geometry) feature.getDefaultGeometry(); Raster raster = cov.getRenderedImage().getData(); int[] darkPixel = new int[3]; raster.getPixel(9, 13, darkPixel); double luminance = ImageUtilities.RGB_TO_GRAY_MATRIX[0][0] * darkPixel[0] + ImageUtilities.RGB_TO_GRAY_MATRIX[0][1] * darkPixel[1] + ImageUtilities.RGB_TO_GRAY_MATRIX[0][2] * darkPixel[2]; assertTrue(luminance < referenceLuminance); raster.getPixel(15, 7, darkPixel); luminance = ImageUtilities.RGB_TO_GRAY_MATRIX[0][0] * darkPixel[0] + ImageUtilities.RGB_TO_GRAY_MATRIX[0][1] * darkPixel[1] + ImageUtilities.RGB_TO_GRAY_MATRIX[0][2] * darkPixel[2]; assertTrue(luminance < referenceLuminance); assertFalse(referenceGeometry.equalsExact(geometry, TOLERANCE)); } finally { if (iter != null) { iter.close(); } if (reader != null) { try { reader.dispose(); } catch (Throwable t) { } } if (cov != null) { try { cov.dispose(true); } catch (Throwable t) { } } } }
@Test public void islandPolygonExtractionWithoutDarkPixelsAndWhiteClouds() throws Exception { GeoTiffReader reader = null; FeatureIterator<SimpleFeature> iter = null; GridCoverage2D cov = null; try { reader = new GeoTiffReader(islandFile); cov = reader.read(null); List<Range<Integer>> exclusionRanges = new ArrayList<Range<Integer>>(); exclusionRanges.add(new Range<Integer>(Integer.class, 0, 10)); exclusionRanges.add(new Range<Integer>(Integer.class, 253, 255)); SimpleFeatureCollection fc = process.execute(cov, exclusionRanges, 10d, false, null, true, true, null, null); iter = fc.features(); SimpleFeature feature = iter.next(); Geometry geometry = (Geometry) feature.getDefaultGeometry(); Geometry islandWkt = wktRead(TestData.file(this, "island.wkt")); assertTrue(islandWkt.equalsExact(geometry, TOLERANCE)); } finally { if (iter != null) { iter.close(); } if (reader != null) { try { reader.dispose(); } catch (Throwable t) { } } if (cov != null) { try { cov.dispose(true); } catch (Throwable t) { } } } }
@Test public void cloudExtractionWriteToDisk() throws Exception { GeoTiffReader reader = null; FeatureIterator<SimpleFeature> iter = null; GridCoverage2D cov = null; try { reader = new GeoTiffReader(cloudFile); cov = reader.read(null); SimpleFeatureCollection fc = process.execute(cov, null, 10d, false, null, true, true, null, null); assertEquals(1, fc.size()); iter = fc.features(); SimpleFeature feature = iter.next(); Geometry geometry = (Geometry) feature.getDefaultGeometry(); assertTrue(referenceGeometry.equalsExact(geometry, TOLERANCE)); final File wktFile = TestData.temp(this, "cloudWkt.wkt"); final File wkbFile = TestData.temp(this, "cloudWkb.wkb"); final File shapeFile = TestData.temp(this, "cloudShape.shp"); CoordinateReferenceSystem crs = reader.getCoordinateReferenceSystem(); writeGeometry(WritingFormat.WKT, geometry, wktFile, crs); writeGeometry(WritingFormat.WKB, geometry, wkbFile, crs); writeGeometry(WritingFormat.SHAPEFILE, geometry, shapeFile, crs); assertTrue(referenceGeometry.equalsExact(wktRead(wktFile), TOLERANCE)); assertTrue(referenceGeometry.equalsExact(wkbRead(wkbFile), TOLERANCE)); } finally { if (iter != null) { iter.close(); } if (reader != null) { try { reader.dispose(); } catch (Throwable t) { } } if (cov != null) { try { cov.dispose(true); } catch (Throwable t) { } } } }