@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);
      }
    }
  }
  @Override
  /**
   * @param inputData a HashMap of the input data: inputObservations: the observations
   *     inputAuthoritativeData: the authoritative points inputDistance: the distance threshold
   *     minNumber: the minimum number of named features to pass fieldName: the name of the field
   *     within the observations to match
   * @result results a HashpMap of the results: result: the input data with the polygon attributes
   *     attached, null values for no match qual_result: the matched input only data with polygon
   *     attributes attached
   */
  public Map<String, IData> run(Map<String, List<IData>> inputData) throws ExceptionReport {

    List obsList = inputData.get("inputObservations");
    List authList = inputData.get("inputAuthoritativeData");
    List distList = inputData.get("inputDistance");
    List minList = inputData.get("minNumber");
    List fieldList = inputData.get("fieldName");

    FeatureCollection obsFc = ((GTVectorDataBinding) obsList.get(0)).getPayload();
    FeatureCollection authFc = ((GTVectorDataBinding) authList.get(0)).getPayload();
    double dist = ((LiteralDoubleBinding) distList.get(0)).getPayload();
    int minNum = ((LiteralIntBinding) minList.get(0)).getPayload();
    String fieldName = ((LiteralStringBinding) fieldList.get(0)).getPayload();

    ArrayList<SimpleFeature> resultFeatures = new ArrayList<SimpleFeature>();
    ArrayList<SimpleFeature> returnFeatures = new ArrayList<SimpleFeature>();

    SimpleFeatureIterator obsIt = (SimpleFeatureIterator) obsFc.features();
    SimpleFeatureIterator authIt = (SimpleFeatureIterator) authFc.features();

    SimpleFeatureIterator sfi = (SimpleFeatureIterator) obsFc.features();
    SimpleFeatureType fType = null;
    SimpleFeature tempPropFeature = sfi.next();
    CoordinateReferenceSystem inputObsCrs = obsFc.getSchema().getCoordinateReferenceSystem();

    Collection<Property> obsProp = tempPropFeature.getProperties();

    SimpleFeatureTypeBuilder resultTypeBuilder = new SimpleFeatureTypeBuilder();
    resultTypeBuilder.setName("typeBuilder");
    resultTypeBuilder.setCRS(inputObsCrs);

    Iterator<Property> pItObs = obsProp.iterator();

    sfi.close();
    while (pItObs.hasNext() == true) {

      try {

        Property tempProp = pItObs.next();
        PropertyType type = tempProp.getDescriptor().getType();
        String name = type.getName().getLocalPart();
        Class<String> valueClass = (Class<String>) tempProp.getType().getBinding();

        resultTypeBuilder.add(name, valueClass);

        // LOG.warn ("Obs property " + name + " " + valueClass + " " +type.toString());
      } catch (Exception e) {
        LOG.error("property error " + e);
      }
    }

    // add DQ_Field

    resultTypeBuilder.add("DQ_SA_SimilarF", Double.class);
    SimpleFeatureType typeF = resultTypeBuilder.buildFeatureType();
    // LOG.warn("Get Spatial Accuracy Feature Type " + typeF.toString());

    SimpleFeatureBuilder resultFeatureBuilder = new SimpleFeatureBuilder(typeF);

    obsIt.close();

    SimpleFeatureIterator obsIt2 = (SimpleFeatureIterator) obsFc.features();

    while (obsIt2.hasNext()) {
      SimpleFeature tempFeature = obsIt2.next();

      fType = tempFeature.getType();

      // LOG.warn("fieldName " + fieldName + " featureName " + featureN + " tempFeature Type " +
      // fType);
      // LOG.warn("TableFeatureName " + tempFeature.getProperty(fieldName).getValue());

      String tempFeatureName = (String) tempFeature.getProperty(fieldName).getValue();

      Geometry geom = (Geometry) tempFeature.getDefaultGeometry();
      for (Property obsProperty : tempFeature.getProperties()) {

        String name = obsProperty.getName().toString();
        Object value = obsProperty.getValue();

        resultFeatureBuilder.set(name, value);
      }

      Geometry bufferGeom = geom.buffer(dist);

      SimpleFeatureIterator authIt2 = (SimpleFeatureIterator) authFc.features();

      int count = 0;
      int within = 0;

      while (authIt2.hasNext() && count <= minNum) {
        SimpleFeature tempAuth = authIt2.next();
        String featureN = (String) tempAuth.getProperty(fieldName).getValue();

        Geometry tempGeom = (Geometry) tempAuth.getDefaultGeometry();

        String authProperty = (String) tempAuth.getProperty(fieldName).getValue();

        if (tempGeom.within(bufferGeom) && authProperty.equalsIgnoreCase(featureN)) {

          count++;

          if (count >= minNum) {
            within = 1;
          }
        }
      }
      LOG.warn("HERE " + within);
      resultFeatureBuilder.set("DQ_SA_SimilarF", within);

      SimpleFeature result = resultFeatureBuilder.buildFeature(tempFeature.getID());
      result.setDefaultGeometry(geom);
      LOG.warn("HERE " + result);
      returnFeatures.add(result);

      if (within == 1) {
        resultFeatures.add(result);
      }

      authIt2.close();
    }
    obsIt2.close();

    ListFeatureCollection qualResult = new ListFeatureCollection(fType, resultFeatures);
    ListFeatureCollection returns = new ListFeatureCollection(fType, returnFeatures);

    LOG.warn("HERE 2 " + qualResult.size() + " " + returns.size());

    Map<String, IData> results = new HashMap<String, IData>();
    results.put("qual_result", new GTVectorDataBinding(qualResult));
    results.put("result", new GTVectorDataBinding(returns));

    return results;
  }
  /**
   * 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;
  }
  @Override
  /**
   * inputData a HashMap of the input data:
   *
   * @param inputObservations: the observations
   * @param inputAuthoritativeData: the polygons
   * @param bufferSize: the size of the buffer around the polygons results a HashpMap of the
   *     results:
   * @result result: the input data with the polygon attributes attached, null values for no match
   * @result qual_result: the matched input only data with polygon attributes attached
   */
  public Map<String, IData> run(Map<String, List<IData>> inputData) throws ExceptionReport {

    HashMap<String, Object> metadataMap = new HashMap<String, Object>();
    ArrayList<SimpleFeature> list = new ArrayList<SimpleFeature>();
    List<IData> inputObs = inputData.get("inputObservations");
    List<IData> inputAuth = inputData.get("inputAuthoritativeData");
    List<IData> inputLit = inputData.get("bufferSize");
    IData observations = inputObs.get(0);
    IData authoritative = inputAuth.get(0);

    IData buffersize = inputLit.get(0);

    double doubleB = (Double) buffersize.getPayload();

    FeatureCollection obsFC = ((GTVectorDataBinding) observations).getPayload();
    FeatureCollection authFC = ((GTVectorDataBinding) authoritative).getPayload();

    SimpleFeatureIterator obsIt = (SimpleFeatureIterator) obsFC.features();

    SimpleFeatureIterator authIt = (SimpleFeatureIterator) authFC.features();

    // setup result feature

    SimpleFeature obsItFeat = obsIt.next();

    SimpleFeature obsItAuth = authIt.next();

    Collection<Property> property = obsItFeat.getProperties();
    Collection<Property> authProperty = obsItAuth.getProperties();

    // setup result type builder
    SimpleFeatureTypeBuilder resultTypeBuilder = new SimpleFeatureTypeBuilder();
    resultTypeBuilder.setName("typeBuilder");

    Iterator<Property> pItObs = property.iterator();
    Iterator<Property> pItAuth = authProperty.iterator();

    metadataMap.put("element", "elementBufferedMetadata");
    File metadataFile = createXMLMetadata(metadataMap);

    while (pItObs.hasNext() == true) {

      try {
        Property tempProp = pItObs.next();

        PropertyType type = tempProp.getDescriptor().getType();
        String name = type.getName().getLocalPart();
        Class<String> valueClass = (Class<String>) tempProp.getType().getBinding();

        resultTypeBuilder.add(name, valueClass);

      } catch (Exception e) {
        LOGGER.error("property error " + e);
      }
    }
    int i = 0;
    while (pItAuth.hasNext() == true) {
      try {
        Property tempProp = pItAuth.next();

        PropertyType type = tempProp.getDescriptor().getType();
        String name = type.getName().getLocalPart();
        Class<String> valueClass = (Class<String>) tempProp.getType().getBinding();

        if (i > 3) {

          resultTypeBuilder.add(name, valueClass);
        }

        i++;

      } catch (Exception e) {
        LOGGER.error("property error " + e);
      }
    }

    obsIt.close();
    authIt.close();
    resultTypeBuilder.add("withinBuffer", Integer.class);

    // set up result feature builder

    SimpleFeatureType type = resultTypeBuilder.buildFeatureType();
    SimpleFeatureBuilder resultFeatureBuilder = new SimpleFeatureBuilder(type);

    // process data here:

    SimpleFeatureIterator obsIt2 = (SimpleFeatureIterator) obsFC.features();

    int within = 0;

    FeatureCollection resultFeatureCollection = DefaultFeatureCollections.newCollection();

    while (obsIt2.hasNext() == true) {
      within = 0;
      SimpleFeature tempObs = obsIt2.next();
      Geometry obsGeom = (Geometry) tempObs.getDefaultGeometry();

      for (Property obsProperty : tempObs.getProperties()) {

        String name = obsProperty.getName().getLocalPart();
        Object value = obsProperty.getValue();

        resultFeatureBuilder.set(name, value);
        // LOGGER.warn("obs Property set " + name);
      }

      double bufferSizeDouble = doubleB;

      Geometry bufferGeom = obsGeom.buffer(bufferSizeDouble);

      int j = 0;
      SimpleFeatureIterator authIt2 = (SimpleFeatureIterator) authFC.features();
      while (authIt2.hasNext() == true) {

        SimpleFeature tempAuth = authIt2.next();
        Geometry authGeom = (Geometry) tempAuth.getDefaultGeometry();

        if (bufferGeom.intersects(authGeom) == true) {
          within = 1;
          j = 0;

          LOGGER.warn("Intersection = true");
          for (Property authProperty1 : tempAuth.getProperties()) {

            String name = authProperty1.getName().getLocalPart();
            Object value = authProperty1.getValue();
            // Class valueClass = (Class<String>)authProperty1.getType().getBinding();

            //		LOGGER.warn("Auth property " + name);
            if (j > 3) {
              resultFeatureBuilder.set(name, value);
              //	LOGGER.warn("Auth property set " + name);

            }

            j++;
          }
        }
      }
      resultFeatureBuilder.set("withinBuffer", within);

      SimpleFeature resultFeature = resultFeatureBuilder.buildFeature(tempObs.getName().toString());
      Geometry geom = (Geometry) tempObs.getDefaultGeometry();
      resultFeature.setDefaultGeometry(geom);

      list.add(resultFeature);

      // resultFeatureCollection.add(resultFeature);
      // LOGGER.warn("RESULT FEATURE " + resultFeatureCollection.getSchema().toString());
      // resultFeatureCollection = obsFC;
    }

    ListFeatureCollection listFeatureCollection = new ListFeatureCollection(type, list);
    LOGGER.warn("Result Feature Size " + listFeatureCollection.size());

    // sort HashMap
    GenericFileData gf = null;
    try {
      gf = new GenericFileData(metadataFile, "text/xml");
    } catch (IOException e) {
      LOGGER.error("GenericFileData " + e);
    }

    HashMap<String, IData> results = new HashMap<String, IData>();
    results.put("result", new GTVectorDataBinding((FeatureCollection) obsFC));
    results.put("qual_result", new GTVectorDataBinding((FeatureCollection) listFeatureCollection));
    results.put("metadata", new GenericFileDataBinding(gf));

    return results;
  }
  private SimpleFeatureCollection calculateRisk(
      SimpleFeatureCollection features,
      JDBCDataStore dataStore,
      String storeName,
      Integer precision,
      String connectionParams,
      int processing,
      int formula,
      int target,
      String materials,
      String scenarios,
      String entities,
      String severeness,
      String fpfield,
      int batch,
      boolean simulation,
      Geometry damageArea,
      Map<Integer, Double> damageValues,
      List<TargetInfo> changedTargets,
      Map<Integer, Map<Integer, Double>> cffs,
      List<String> psc,
      Map<Integer, Map<Integer, Double>> padrs,
      Map<Integer, Double> piss,
      List<Integer> distances,
      boolean extendedSchema)
      throws IOException, SQLException {

    if (precision == null) {
      precision = 3;
    }

    DefaultTransaction transaction = new DefaultTransaction();
    Connection conn = null;
    try {
      conn = dataStore.getConnection(transaction);

      // output FeatureType (risk)
      //  - id_geo_arco
      //  - geometria
      //  - rischio1
      //  - rischio2
      SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder();
      tb.add(
          "id_geo_arco", features.getSchema().getDescriptor("id_geo_arco").getType().getBinding());
      tb.add(
          "geometria",
          MultiLineString.class,
          features.getSchema().getGeometryDescriptor().getCoordinateReferenceSystem());

      if (extendedSchema) {
        tb.add("rischio_sociale", Double.class);
        tb.add("rischio_ambientale", Double.class);
        tb.add("nr_corsie", Integer.class);
        tb.add("lunghezza", Integer.class);
        tb.add("nr_incidenti", Integer.class);
      } else {
        tb.add("rischio1", Double.class);
        tb.add("rischio2", Double.class);
      }
      // fake layer name (risk) used for WPS output. Layer risk must be defined in GeoServer
      // catalog
      tb.setName(new NameImpl(features.getSchema().getName().getNamespaceURI(), "risk"));
      SimpleFeatureType ft = tb.buildFeatureType();

      // feature level (1, 2, 3)
      int level = FormulaUtils.getLevel(features);

      LOGGER.info("Doing calculus for level " + level);

      Formula formulaDescriptor = Formula.load(conn, processing, formula, target);

      if (formulaDescriptor == null) {
        throw new ProcessException("Unable to load formula " + formula);
      }

      if (((!formulaDescriptor.hasGrid() && level == 3)
              || (!formulaDescriptor.hasNoGrid() && level < 3))
          && !extendedSchema) {
        LOGGER.info("Formula not supported on this level, returning empty collection");
        return new EmptyFeatureCollection(ft);
      } else {
        // iterator on source
        SimpleFeatureIterator iter = features.features();

        // result builder
        SimpleFeatureBuilder fb = new SimpleFeatureBuilder(ft);
        ListFeatureCollection result = new ListFeatureCollection(ft);
        int count = 0;
        Double[] risk = new Double[] {0.0, 0.0};

        // iterate source features
        try {
          // we will calculate risk in batch of arcs
          // we store each feature of the batch in a map
          // indexed by id
          Map<Number, SimpleFeature> temp = new HashMap<Number, SimpleFeature>();
          StringBuilder ids = new StringBuilder();
          String fk_partner = null;

          while (iter.hasNext()) {
            SimpleFeature feature = iter.next();
            Number id = (Number) feature.getAttribute("id_geo_arco");
            fk_partner = (String) feature.getAttribute("fk_partner");
            fb.add(id);
            fb.add(feature.getDefaultGeometry());
            if (formulaDescriptor.takeFromSource()) {
              risk[0] = ((Number) feature.getAttribute("rischio1")).doubleValue();
              risk[1] = ((Number) feature.getAttribute("rischio2")).doubleValue();
            }
            fb.add(risk[0]);
            fb.add(risk[1]);
            if (extendedSchema) {
              fb.add((Number) feature.getAttribute("nr_corsie"));
              fb.add((Number) feature.getAttribute("lunghezza"));
              fb.add((Number) feature.getAttribute("nr_incidenti"));
            }
            temp.put(id.intValue(), fb.buildFeature(id + ""));

            if (simulation) {
              Double pis = piss.get(id.intValue());
              Map<Integer, Double> padr = padrs.get(id.intValue());
              Map<Integer, Double> cff = cffs.get(id.intValue());

              Map<Integer, Map<Integer, Double>> simulationTargets =
                  new HashMap<Integer, Map<Integer, Double>>();

              if (!changedTargets.isEmpty()) {
                for (int distance : distances) {
                  Geometry buffer =
                      BufferUtils.iterativeBuffer(
                          (Geometry) feature.getDefaultGeometry(), (double) distance, 100);
                  for (TargetInfo targetInfo : changedTargets) {
                    if (targetInfo.getGeometry() != null) {
                      Geometry intersection = buffer.intersection(targetInfo.getGeometry());
                      if (intersection != null && intersection.getArea() > 0) {
                        Map<Integer, Double> distancesMap =
                            simulationTargets.get(targetInfo.getType());
                        if (distancesMap == null) {
                          distancesMap = new HashMap<Integer, Double>();
                          simulationTargets.put(targetInfo.getType(), distancesMap);
                        }
                        double value = 0.0;
                        if (targetInfo.isHuman()) {
                          value =
                              intersection.getArea()
                                  / targetInfo.getGeometry().getArea()
                                  * targetInfo.getValue();
                        } else {
                          value = intersection.getArea();
                        }
                        if (targetInfo.isRemoved()) {
                          value = -value;
                        }
                        distancesMap.put(distance, value);
                      }
                    }
                  }
                }
              }

              FormulaUtils.calculateSimulationFormulaValuesOnSingleArc(
                  conn,
                  level,
                  processing,
                  formulaDescriptor,
                  id.intValue(),
                  fk_partner,
                  materials,
                  scenarios,
                  entities,
                  severeness,
                  fpfield,
                  target,
                  simulationTargets,
                  temp,
                  precision,
                  cff,
                  psc,
                  padr,
                  pis,
                  null,
                  extendedSchema);

              result.addAll(temp.values());
              temp = new HashMap<Number, SimpleFeature>();
            } else if (damageArea != null) {
              Geometry arcGeometry = (Geometry) feature.getDefaultGeometry();
              if (arcGeometry != null && arcGeometry.intersects(damageArea)) {
                FormulaUtils.calculateSimulationFormulaValuesOnSingleArc(
                    conn,
                    level,
                    processing,
                    formulaDescriptor,
                    id.intValue(),
                    fk_partner,
                    materials,
                    scenarios,
                    entities,
                    severeness,
                    fpfield,
                    target,
                    null,
                    temp,
                    precision,
                    null,
                    null,
                    null,
                    null,
                    damageValues,
                    extendedSchema);
                result.addAll(temp.values());
              }
              temp = new HashMap<Number, SimpleFeature>();
            } else {
              ids.append("," + id);
              count++;
              // calculate batch items a time
              if (count % batch == 0) {
                LOGGER.info("Calculated " + count + " values");
                FormulaUtils.calculateFormulaValues(
                    conn,
                    level,
                    processing,
                    formulaDescriptor,
                    ids.toString().substring(1),
                    fk_partner,
                    materials,
                    scenarios,
                    entities,
                    severeness,
                    fpfield,
                    target,
                    temp,
                    precision,
                    extendedSchema);
                result.addAll(temp.values());
                ids = new StringBuilder();
                temp = new HashMap<Number, SimpleFeature>();
              }
            }
          }

          // final calculus for remaining items not in batch size
          LOGGER.info("Calculating remaining items");
          if (ids.length() > 0) {
            FormulaUtils.calculateFormulaValues(
                conn,
                level,
                processing,
                formulaDescriptor,
                ids.toString().substring(1),
                fk_partner,
                materials,
                scenarios,
                entities,
                severeness,
                fpfield,
                target,
                temp,
                precision,
                extendedSchema);
          }
          result.addAll(temp.values());

          LOGGER.info("Calculated " + result.size() + " values");

        } finally {
          iter.close();
        }
        return result;
      }

    } finally {
      transaction.close();
      if (conn != null) {
        conn.close();
      }
    }
  }