@Override public void actionPerformed(CommandEvent event) { JTextArea textArea = new JTextArea(16, 32); textArea.setEditable(true); JPanel contentPanel = new JPanel(new BorderLayout(4, 4)); contentPanel.add(new JLabel("Geometry Well-Known-Text (WKT):"), BorderLayout.NORTH); contentPanel.add(new JScrollPane(textArea), BorderLayout.CENTER); VisatApp visatApp = VisatApp.getApp(); ModalDialog modalDialog = new ModalDialog(visatApp.getApplicationWindow(), DLG_TITLE, ModalDialog.ID_OK_CANCEL, null); modalDialog.setContent(contentPanel); modalDialog.center(); if (modalDialog.show() == ModalDialog.ID_OK) { String wellKnownText = textArea.getText(); if (wellKnownText == null || wellKnownText.isEmpty()) { return; } ProductSceneView sceneView = visatApp.getSelectedProductSceneView(); VectorDataLayer vectorDataLayer = InsertFigureInteractorInterceptor.getActiveVectorDataLayer(sceneView); if (vectorDataLayer == null) { return; } SimpleFeatureType wktFeatureType = PlainFeatureFactory.createDefaultFeatureType(DefaultGeographicCRS.WGS84); ListFeatureCollection newCollection = new ListFeatureCollection(wktFeatureType); SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(wktFeatureType); SimpleFeature wktFeature = featureBuilder.buildFeature("ID" + Long.toHexString(currentFeatureId++)); Geometry geometry; try { geometry = new WKTReader().read(wellKnownText); } catch (ParseException e) { visatApp.handleError("Failed to convert WKT into geometry", e); return; } wktFeature.setDefaultGeometry(geometry); newCollection.add(wktFeature); FeatureCollection<SimpleFeatureType, SimpleFeature> productFeatures = FeatureUtils.clipFeatureCollectionToProductBounds( newCollection, sceneView.getProduct(), null, ProgressMonitor.NULL); if (productFeatures.isEmpty()) { visatApp.showErrorDialog(DLG_TITLE, "The geometry is not contained in the product."); } else { vectorDataLayer.getVectorDataNode().getFeatureCollection().addAll(productFeatures); } } }
/** * Executes the raster to vector process. * * @param coverage the input grid coverage * @param band the coverage band to process; defaults to 0 if {@code null} * @param insideEdges whether boundaries between raster regions with data values (ie. not NODATA) * should be returned; defaults to {@code true} if {@code null} * @param roi optional polygonal {@code Geometry} to define a sub-area within which vectorizing * will be done * @param noDataValues optional list of values to treat as NODATA; regions with these values will * not be represented in the returned features; if {@code null}, 0 is used as the single * NODATA value; ignored if {@code classificationRanges} is provided * @param classificationRanges optional list of {@code Range} objects to pre-classify the input * coverage prior to vectorizing; values not included in the list will be treated as NODATA; * values in the first {@code Range} are classified to 1, those in the second {@code Range} to * 2 etc. * @param progressListener an optional listener * @return a feature collection where each feature has a {@code Polygon} ("the_geom") and an * attribute "value" with value of the corresponding region in either {@code coverage} or the * classified coverage (when {@code classificationRanges} is used) * @throws ProcessException */ @DescribeResult(name = "result", description = "The polygon feature collection") public SimpleFeatureCollection execute( @DescribeParameter(name = "data", description = "The raster to be used as the source") GridCoverage2D coverage, @DescribeParameter( name = "band", description = "(Integer, default=0) the source image band to process", min = 0) Integer band, @DescribeParameter( name = "insideEdges", description = "(Boolean, default=true) whether to vectorize boundaries between adjacent regions with non-outside values", min = 0) Boolean insideEdges, @DescribeParameter( name = "roi", description = "The geometry used to delineate the area of interest in model space", min = 0) Geometry roi, @DescribeParameter( name = "nodata", description = "Collection<Number>, default={0}) values to treat as NODATA", collectionType = Number.class, min = 0) Collection<Number> noDataValues, @DescribeParameter( name = "ranges", description = "The list of ranges to be applied. \n" + "Each range is expressed as 'OPEN START ; END CLOSE'\n" + "where 'OPEN:=(|[, CLOSE=)|]',\n " + "START is the low value, or nothing to imply -INF,\n" + "CLOSE is the biggest value, or nothing to imply +INF", collectionType = Range.class, min = 0) List<Range> classificationRanges, ProgressListener progressListener) throws ProcessException { // // initial checks // if (coverage == null) { throw new ProcessException("Invalid input, source grid coverage should be not null"); } if (band == null) { band = 0; } else if (band < 0 || band >= coverage.getNumSampleDimensions()) { throw new ProcessException("Invalid input, invalid band number:" + band); } // do we have classification ranges? boolean hasClassificationRanges = classificationRanges != null && classificationRanges.size() > 0; // apply the classification by setting 0 as the default value and using 1, ..., numClasses for // the other classes. // we use 0 also as the noData for the resulting coverage. if (hasClassificationRanges) { final RangeLookupProcess lookup = new RangeLookupProcess(); coverage = lookup.execute(coverage, band, classificationRanges, progressListener); } // Use noDataValues to set the "outsideValues" parameter of the Vectorize // operation unless classificationRanges are in use, in which case the // noDataValues arg is ignored. List<Number> outsideValues = new ArrayList<Number>(); if (noDataValues != null && !hasClassificationRanges) { outsideValues.addAll(noDataValues); } else { outsideValues.add(0); } // // GRID TO WORLD preparation // final AffineTransform mt2D = (AffineTransform) coverage.getGridGeometry().getGridToCRS2D(PixelOrientation.UPPER_LEFT); // get the rendered image final RenderedImage raster = coverage.getRenderedImage(); // perform jai operation ParameterBlockJAI pb = new ParameterBlockJAI("Vectorize"); pb.setSource("source0", raster); if (roi != null) { pb.setParameter("roi", CoverageUtilities.prepareROI(roi, mt2D)); } pb.setParameter("band", band); pb.setParameter("outsideValues", outsideValues); if (insideEdges != null) { pb.setParameter("insideEdges", insideEdges); } // pb.setParameter("removeCollinear", false); final RenderedOp dest = JAI.create("Vectorize", pb); @SuppressWarnings("unchecked") final Collection<Polygon> prop = (Collection<Polygon>) dest.getProperty(VectorizeDescriptor.VECTOR_PROPERTY_NAME); // wrap as a feature collection and return final SimpleFeatureType featureType = CoverageUtilities.createFeatureType(coverage, Polygon.class); final SimpleFeatureBuilder builder = new SimpleFeatureBuilder(featureType); int i = 0; final ListFeatureCollection featureCollection = new ListFeatureCollection(featureType); final AffineTransformation jtsTransformation = new AffineTransformation( mt2D.getScaleX(), mt2D.getShearX(), mt2D.getTranslateX(), mt2D.getShearY(), mt2D.getScaleY(), mt2D.getTranslateY()); for (Polygon polygon : prop) { // get value Double value = (Double) polygon.getUserData(); polygon.setUserData(null); // filter coordinates in place polygon.apply(jtsTransformation); // create feature and add to list builder.set("the_geom", polygon); builder.set("value", value); featureCollection.add(builder.buildFeature(String.valueOf(i++))); } // return value return featureCollection; }
@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<Object> noDataList = new ArrayList<Object>(); for (GridSampleDimension sd : gc2d.getSampleDimensions()) { // grab all the explicit nodata final double[] sdNoData = sd.getNoDataValues(); if (sdNoData != null) { for (double nodata : sdNoData) { noDataList.add(nodata); } } // handle also readers setting up nodata in a category with a specific name if (sd.getCategories() != null) { for (Category cat : sd.getCategories()) { if (cat.getName().equals(NO_DATA)) { final NumberRange<? extends Number> catRange = cat.getRange(); if (catRange.getMinimum() == catRange.getMaximum()) { noDataList.add(catRange.getMinimum()); } else { Range<Double> noData = new Range<Double>( catRange.getMinimum(), catRange.isMinIncluded(), catRange.getMaximum(), catRange.isMaxIncluded()); noDataList.add(noData); } } } } } // get the rendered image final RenderedImage raster = gc2d.getRenderedImage(); // perform jai operation ParameterBlockJAI pb = new ParameterBlockJAI("Contour"); pb.setSource("source0", raster); if (roi != null) { pb.setParameter("roi", CoverageUtilities.prepareROI(roi, mt2D)); } if (band != null) { pb.setParameter("band", band); } if (interval != null) { pb.setParameter("interval", interval); } else { final ArrayList<Double> elements = new ArrayList<Double>(levels.length); for (double level : levels) elements.add(level); pb.setParameter("levels", elements); } if (simplify != null) { pb.setParameter("simplify", simplify); } if (smooth != null) { pb.setParameter("smooth", smooth); } if (noDataList != null) { pb.setParameter("nodata", noDataList); } final RenderedOp dest = JAI.create("Contour", pb); @SuppressWarnings("unchecked") final Collection<LineString> prop = (Collection<LineString>) dest.getProperty(ContourDescriptor.CONTOUR_PROPERTY_NAME); // wrap as a feature collection and return final SimpleFeatureType schema = CoverageUtilities.createFeatureType(gc2d, LineString.class); final SimpleFeatureBuilder builder = new SimpleFeatureBuilder(schema); int i = 0; final ListFeatureCollection featureCollection = new ListFeatureCollection(schema); final AffineTransformation jtsTransformation = new AffineTransformation( mt2D.getScaleX(), mt2D.getShearX(), mt2D.getTranslateX(), mt2D.getShearY(), mt2D.getScaleY(), mt2D.getTranslateY()); for (LineString line : prop) { // get value Double value = (Double) line.getUserData(); line.setUserData(null); // filter coordinates in place line.apply(jtsTransformation); // create feature and add to list builder.set("the_geom", line); builder.set("value", value); featureCollection.add(builder.buildFeature(String.valueOf(i++))); } // return value return featureCollection; }