BarnesSurfaceProcess implements VectorProcess { @DescribeResult(name = "result", description = "Output raster") public GridCoverage2D execute( @DescribeParameter(name = "data", description = "Input features") SimpleFeatureCollection obsFeatures, @DescribeParameter(name = "valueAttr", description = "Name of attribute containing the data value to be interpolated") String valueAttr, @DescribeParameter(name = "dataLimit", description = "Limit for the number of input features processed", min=0, max=1) Integer argDataLimit, @DescribeParameter(name = "scale", description = "Length scale for the interpolation, in units of the source data CRS", min=1, max=1) Double argScale, @DescribeParameter(name = "convergence", description = "Convergence factor for refinement (between 0 and 1, default 0.3)", min=0, max=1, defaultValue="0.3") Double argConvergence, @DescribeParameter(name = "passes", description = "Number of passes to compute (default = 2)", min=0, max=1) Integer argPasses, @DescribeParameter(name = "minObservations", description = "Minimum number of observations required to support a grid cell (default = 2)", min=0, max=1, defaultValue="2") Integer argMinObsCount, @DescribeParameter(name = "maxObservationDistance", description = "Maximum distance to an observation for it to support a grid cell, in units of the source CRS (default = 0, meaning all observations used)", defaultValue="0", min=0, max=1) Double argMaxObsDistance, @DescribeParameter(name = "noDataValue", description = "Value to use for NO_DATA cells (default = -999)", defaultValue="-999", min=0, max=1) Double argNoDataValue, @DescribeParameter(name = "pixelsPerCell", description = "Resolution of the computed grid in pixels per grid cell (default = 1)", defaultValue="1", min=0, max=1) Integer argPixelsPerCell, @DescribeParameter(name = "queryBuffer", description = "Distance to expand the query envelope by, in units of the source CRS (larger values provide a more stable surface)", min=0, max=1) Double argQueryBuffer, @DescribeParameter(name = "outputBBOX", description = "Bounding box for output") ReferencedEnvelope outputEnv, @DescribeParameter(name = "outputWidth", description = "Width of the output raster in pixels") Integer outputWidth, @DescribeParameter(name = "outputHeight", description = "Height of the output raster in pixels") Integer outputHeight, ProgressListener monitor) throws ProcessException { if (valueAttr == null || valueAttr.length() <= 0) { throw new IllegalArgumentException("Value attribute must be specified"); } int dataLimit = 0; if (argDataLimit != null) dataLimit = argDataLimit; double lengthScale = argScale; double convergenceFactor = argConvergence != null ? argConvergence : 0.3; int passes = argPasses != null ? argPasses : 2; int minObsCount = argMinObsCount != null ? argMinObsCount : 2; double maxObsDistance = argMaxObsDistance != null ? argMaxObsDistance : 0.0; float noDataValue = (float) (argNoDataValue != null ? argNoDataValue : -999); int pixelsPerCell = 1; if (argPixelsPerCell != null && argPixelsPerCell > 1) { pixelsPerCell = argPixelsPerCell; } int gridWidth = outputWidth; int gridHeight = outputHeight; if (pixelsPerCell > 1) { gridWidth = outputWidth / pixelsPerCell; gridHeight = outputHeight / pixelsPerCell; } CoordinateReferenceSystem srcCRS = obsFeatures.getSchema().getCoordinateReferenceSystem(); CoordinateReferenceSystem dstCRS = outputEnv.getCoordinateReferenceSystem(); MathTransform trans = null; try { trans = CRS.findMathTransform(srcCRS, dstCRS); } catch (FactoryException e) { throw new ProcessException(e); } double distanceConversionFactor = distanceConversionFactor(srcCRS, dstCRS); double dstLengthScale = lengthScale * distanceConversionFactor; double dstMaxObsDistance = maxObsDistance * distanceConversionFactor; Coordinate[] pts = null; try { pts = extractPoints(obsFeatures, valueAttr, trans, dataLimit); } catch (CQLException e) { throw new ProcessException(e); } float[][] barnesGrid = createBarnesGrid(pts, dstLengthScale, convergenceFactor, passes, minObsCount, dstMaxObsDistance, noDataValue, outputEnv, gridWidth, gridHeight); barnesGrid = flipXY(barnesGrid); float[][] outGrid = barnesGrid; if (pixelsPerCell > 1) outGrid = upsample(barnesGrid, noDataValue, outputWidth, outputHeight); GridCoverageFactory gcf = CoverageFactoryFinder.getGridCoverageFactory(GeoTools.getDefaultHints()); GridCoverage2D gridCov = gcf.create("values", outGrid, outputEnv); return gridCov; }  @DescribeResult(name = "result", description = "Output raster") GridCoverage2D execute(
            
            // process data
            @DescribeParameter(name = "data", description = "Input features") SimpleFeatureCollection obsFeatures,
            @DescribeParameter(name = "valueAttr", description = "Name of attribute containing the data value to be interpolated") String valueAttr,
            @DescribeParameter(name = "dataLimit", description = "Limit for the number of input features processed", min=0, max=1) Integer argDataLimit,
            
            // process parameters
            @DescribeParameter(name = "scale", description = "Length scale for the interpolation, in units of the source data CRS", min=1, max=1) Double argScale,
            @DescribeParameter(name = "convergence", description = "Convergence factor for refinement (between 0 and 1, default 0.3)", min=0, max=1, defaultValue="0.3") Double argConvergence,
            @DescribeParameter(name = "passes", description = "Number of passes to compute (default = 2)", min=0, max=1) Integer argPasses,
            @DescribeParameter(name = "minObservations", description = "Minimum number of observations required to support a grid cell (default = 2)", min=0, max=1, defaultValue="2") Integer argMinObsCount,
            @DescribeParameter(name = "maxObservationDistance", description = "Maximum distance to an observation for it to support a grid cell, in units of the source CRS (default = 0, meaning all observations used)", defaultValue="0", min=0, max=1) Double argMaxObsDistance,
            @DescribeParameter(name = "noDataValue", description = "Value to use for NO_DATA cells (default = -999)", defaultValue="-999", min=0, max=1) Double argNoDataValue,
            @DescribeParameter(name = "pixelsPerCell", description = "Resolution of the computed grid in pixels per grid cell (default = 1)", defaultValue="1", min=0, max=1) Integer argPixelsPerCell,
            
            // query modification parameters
            @DescribeParameter(name = "queryBuffer", description = "Distance to expand the query envelope by, in units of the source CRS (larger values provide a more stable surface)", min=0, max=1) Double argQueryBuffer,

            // output image parameters
            @DescribeParameter(name = "outputBBOX", description = "Bounding box for output") ReferencedEnvelope outputEnv,
            @DescribeParameter(name = "outputWidth", description = "Width of the output raster in pixels") Integer outputWidth,
            @DescribeParameter(name = "outputHeight", description = "Height of the output raster in pixels") Integer outputHeight,
            
            ProgressListener monitor); Query invertQuery(
            @DescribeParameter(name = "queryBuffer", description = "The distance by which to expand the query window", min=0, max=1) Double argQueryBuffer,
            Query targetQuery, GridGeometry targetGridGeometry); static Coordinate[] extractPoints(SimpleFeatureCollection obsPoints, String attrName, MathTransform trans, int dataLimit);  }
@Test public void testSimpleSurface() { ReferencedEnvelope bounds = new ReferencedEnvelope(0, 30, 0, 30, DefaultGeographicCRS.WGS84); Coordinate[] data = new Coordinate[] { new Coordinate(10, 10, 100), new Coordinate(10, 20, 20), new Coordinate(20, 10, 0), new Coordinate(20, 20, 80) }; SimpleFeatureCollection fc = createPoints(data, bounds); ProgressListener monitor = null; BarnesSurfaceProcess process = new BarnesSurfaceProcess(); GridCoverage2D cov = process.execute(fc, "value", 1000, 10.0, (Double) null, (Integer) 2, (Integer) null, (Double) null, -999.0, 1, 0.0, bounds, 100, 100, monitor ); double ERROR_TOL = 10; for (Coordinate p : data) { float covval = coverageValue(cov, p.x, p.y); double error = Math.abs(p.z - covval); assertTrue(error < ERROR_TOL); } assertEquals(1, cov.getSampleDimensions().length); assertEquals("values", cov.getSampleDimensions()[0].getDescription().toString()); }