/**
   * Extract the coordinate of a FeatureCollection in a HashMap with an ID as a key.
   *
   * @param nStaz
   * @param collection
   * @throws Exception if a fiel of elevation isn't the same of the collection
   */
  private LinkedHashMap<Integer, Coordinate> getCoordinate(
      int nStaz, SimpleFeatureCollection collection, String idField) throws Exception {
    LinkedHashMap<Integer, Coordinate> id2CoordinatesMap = new LinkedHashMap<Integer, Coordinate>();
    FeatureIterator<SimpleFeature> iterator = collection.features();
    Coordinate coordinate = null;
    try {
      while (iterator.hasNext()) {
        SimpleFeature feature = iterator.next();
        int name = ((Number) feature.getAttribute(idField)).intValue();
        coordinate = ((Geometry) feature.getDefaultGeometry()).getCentroid().getCoordinate();
        double z = 0;
        if (fPointZ != null) {
          try {
            z = ((Number) feature.getAttribute(fPointZ)).doubleValue();
          } catch (NullPointerException e) {
            pm.errorMessage(msg.message("kriging.noPointZ"));
            throw new Exception(msg.message("kriging.noPointZ"));
          }
        }
        coordinate.z = z;
        id2CoordinatesMap.put(name, coordinate);
      }
    } finally {
      iterator.close();
    }

    return id2CoordinatesMap;
  }
  @Execute
  public void process() throws Exception {

    // verify if the file name is setted otherwise set it to a default value.
    if (pNetname == null) {
      if (pMode == 0) {
        pNetname = Constants.NETWORK_PROJECT_NAME_SHP;
      } else if (pMode == 1) {
        pNetname = Constants.NETWORK_CALIBRATION_NAME_SHP;
      }
    }

    checkNull(inFolder, pCode);
    CoordinateReferenceSystem crs = CRS.decode(pCode);
    pm.beginTask(msg.message("trentoP.generatefile.project"), -1);
    pm.worked(1);
    // if you want to create an empty file
    if (!doFromold) {
      ITrentoPType[] values = PipesTrentoP.values();
      String file = new File(inFolder, pNetname).getAbsolutePath();
      if (pMode == 0) {
        // project
        OmsShapefileFeatureWriter.writeEmptyShapefile(file, getProjectFeatureType(crs));
      } else if (pMode == 1) {
        // calibration
        OmsShapefileFeatureWriter.writeEmptyShapefile(file, getCalibrationFeatureType(crs));
      }
      file = new File(inFolder, pShapeAreeName).getAbsolutePath();
      makePolygonShp(values, file, crs, pShapeAreeName);
    } else if (doFromold) {
      if (pOldVector == null) {
        throw new IllegalArgumentException(msg.message("trentoP.generatefile.error.noFeature"));
      }
      String file = new File(inFolder, pNetname).getAbsolutePath();
      SimpleFeatureCollection calibrationFC = createNewCollection(getCalibrationFeatureType(crs));
      OmsShapefileFeatureWriter.writeShapefile(file, calibrationFC);
    }
    pm.done();
  }
  /** Verify the input of the model. */
  private void verifyInput() {
    if (inData == null || inStations == null) {
      throw new NullPointerException(msg.message("kriging.stationproblem"));
    }
    if (pMode < 0 || pMode > 1) {
      throw new IllegalArgumentException(msg.message("kriging.defaultMode"));
    }

    if (defaultVariogramMode != 0 && defaultVariogramMode != 1) {
      throw new IllegalArgumentException(msg.message("kriging.variogramMode"));
    }
    if (defaultVariogramMode == 0) {
      if (pVariance == 0
          || pIntegralscale[0] == 0
          || pIntegralscale[1] == 0
          || pIntegralscale[2] == 0) {

        pm.errorMessage(msg.message("kriging.noParam"));
        pm.errorMessage("varianza " + pVariance);
        pm.errorMessage("Integral scale x " + pIntegralscale[0]);
        pm.errorMessage("Integral scale y " + pIntegralscale[1]);
        pm.errorMessage("Integral scale z " + pIntegralscale[2]);
      }
    }
    if (defaultVariogramMode == 1) {
      if (pNug == 0 || pS == 0 || pA == 0) {
        pm.errorMessage(msg.message("kriging.noParam"));
        pm.errorMessage("Nugget " + pNug);
        pm.errorMessage("Sill " + pS);
        pm.errorMessage("Range " + pA);
      }
    }

    if ((pMode == 0) && inInterpolate == null) {
      throw new ModelsIllegalargumentException(msg.message("kriging.noPoint"), this, pm);
    }
    if (pMode == 1 && inInterpolationGrid == null) {
      throw new ModelsIllegalargumentException(
          "The gridded interpolation needs a gridgeometry in input.", this, pm);
    }
  }
  /**
   * Executing ordinary kriging.
   *
   * <p>
   * <li>Verify if the parameters are correct.
   * <li>Calculating the matrix of the covariance (a).
   * <li>For each point to interpolated, evalutate the know term vector (b) and solve the system (a
   *     x)=b where x is the weight.
   *
   * @throws SchemaException
   */
  @Execute
  public void process() throws Exception {
    verifyInput();

    List<Double> xStationList = new ArrayList<Double>();
    List<Double> yStationList = new ArrayList<Double>();
    List<Double> zStationList = new ArrayList<Double>();
    List<Double> hStationList = new ArrayList<Double>();

    /*
     * counter for the number of station with measured value !=0.
     */
    int n1 = 0;
    /*
     * Store the station coordinates and measured data in the array.
     */
    FeatureIterator<SimpleFeature> stationsIter = inStations.features();
    try {
      while (stationsIter.hasNext()) {
        SimpleFeature feature = stationsIter.next();
        Object stationId = feature.getAttribute(fStationsid);
        int id;
        if (stationId instanceof Number) {
          id = ((Number) stationId).intValue();
        } else if (stationId instanceof String) {
          id = (int) Double.parseDouble((String) stationId);
        } else {
          throw new ModelsIllegalargumentException(
              "Unreadable type found for the station id.", this, pm);
        }
        double z = 0;
        if (fStationsZ != null) {
          try {
            z = ((Number) feature.getAttribute(fStationsZ)).doubleValue();
          } catch (NullPointerException e) {
            pm.errorMessage(msg.message("kriging.noStationZ"));
            throw new Exception(msg.message("kriging.noStationZ"));
          }
        }
        Coordinate coordinate =
            ((Geometry) feature.getDefaultGeometry()).getCentroid().getCoordinate();
        double[] h = inData.get(id);
        if (h == null || isNovalue(h[0])) {
          /*
           * skip data for non existing stations, they are allowed.
           * Also skip novalues.
           */
          continue;
        }
        if (defaultVariogramMode == 0) {
          if (doIncludezero) {
            if (Math.abs(h[0]) >= 0.0) { // TOLL
              xStationList.add(coordinate.x);
              yStationList.add(coordinate.y);
              zStationList.add(z);
              hStationList.add(h[0]);
              n1 = n1 + 1;
            }
          } else {
            if (Math.abs(h[0]) > 0.0) { // TOLL
              xStationList.add(coordinate.x);
              yStationList.add(coordinate.y);
              zStationList.add(z);
              hStationList.add(h[0]);
              n1 = n1 + 1;
            }
          }
        } else if (defaultVariogramMode == 1) {
          if (doIncludezero) {
            if (Math.abs(h[0]) >= 0.0) { // TOLL
              xStationList.add(coordinate.x);
              yStationList.add(coordinate.y);
              zStationList.add(z);
              hStationList.add(h[0]);
              n1 = n1 + 1;
            }
          } else {
            if (Math.abs(h[0]) > 0.0) { // TOLL
              xStationList.add(coordinate.x);
              yStationList.add(coordinate.y);
              zStationList.add(z);
              hStationList.add(h[0]);
              n1 = n1 + 1;
            }
          }
        }
      }
    } finally {
      stationsIter.close();
    }

    int nStaz = xStationList.size();
    /*
     * The coordinates of the station points plus in last position a place
     * for the coordinate of the point to interpolate.
     */
    double[] xStation = new double[nStaz + 1];
    double[] yStation = new double[nStaz + 1];
    double[] zStation = new double[nStaz + 1];
    double[] hStation = new double[nStaz + 1];
    boolean areAllEquals = true;
    if (nStaz != 0) {
      xStation[0] = xStationList.get(0);
      yStation[0] = yStationList.get(0);
      zStation[0] = zStationList.get(0);
      hStation[0] = hStationList.get(0);
      double previousValue = hStation[0];

      for (int i = 1; i < nStaz; i++) {

        double xTmp = xStationList.get(i);
        double yTmp = yStationList.get(i);
        double zTmp = zStationList.get(i);
        double hTmp = hStationList.get(i);
        boolean doubleStation =
            ModelsEngine.verifyDoubleStation(
                xStation, yStation, zStation, hStation, xTmp, yTmp, zTmp, hTmp, i, false, pm);
        if (!doubleStation) {
          xStation[i] = xTmp;
          yStation[i] = yTmp;
          zStation[i] = zTmp;
          hStation[i] = hTmp;
          if (areAllEquals && hStation[i] != previousValue) {
            areAllEquals = false;
          }
          previousValue = hStation[i];
        }
      }
    }
    LinkedHashMap<Integer, Coordinate> pointsToInterpolateId2Coordinates = null;
    // vecchio int numPointToInterpolate = getNumPoint(inInterpolate);
    int numPointToInterpolate = 0;

    /*
     * if the isLogarithmic is true then execute the model with log value.
     */
    // vecchio double[] result = new double[numPointToInterpolate];

    if (pMode == 0) {
      pointsToInterpolateId2Coordinates =
          getCoordinate(numPointToInterpolate, inInterpolate, fInterpolateid);
    } else if (pMode == 1) {
      pointsToInterpolateId2Coordinates = getCoordinate(inInterpolationGrid);
      numPointToInterpolate = pointsToInterpolateId2Coordinates.size();
    } else {
      throw new ModelsIllegalargumentException("The parameter pMode can only be 0 or 1.", this, pm);
    }

    Set<Integer> pointsToInterpolateIdSet = pointsToInterpolateId2Coordinates.keySet();
    Iterator<Integer> idIterator = pointsToInterpolateIdSet.iterator();
    int j = 0;
    // vecchio int[] idArray = new int[inInterpolate.size()];
    int[] idArray = new int[pointsToInterpolateId2Coordinates.size()];
    double[] result = new double[pointsToInterpolateId2Coordinates.size()];
    if (n1 != 0) {
      if (doLogarithmic) {
        for (int i = 0; i < nStaz; i++) {
          if (hStation[i] > 0.0) {
            hStation[i] = Math.log(hStation[i]);
          }
        }
      }

      /*
       * calculating the covariance matrix.
       */
      double[][] covarianceMatrix = covMatrixCalculating(xStation, yStation, zStation, n1);
      /*
       * extract the coordinate of the points where interpolated.
       */

      /*
       * initialize the solution and its variance vector.
       */

      if (!areAllEquals && n1 > 1) {
        // pm.beginTask(msg.message("kriging.working"),inInterpolate.size());
        while (idIterator.hasNext()) {
          double sum = 0.;
          int id = idIterator.next();
          idArray[j] = id;
          Coordinate coordinate = (Coordinate) pointsToInterpolateId2Coordinates.get(id);
          xStation[n1] = coordinate.x;
          yStation[n1] = coordinate.y;
          zStation[n1] = coordinate.z;
          /*
           * calculating the right hand side of the kriging linear
           * system.
           */
          double[] knownTerm = knownTermsCalculation(xStation, yStation, zStation, n1);

          /*
           * solve the linear system, where the result is the weight.
           */
          ColumnVector knownTermColumn = new ColumnVector(knownTerm);
          LinearSystem linearSystem = new LinearSystem(covarianceMatrix);
          ColumnVector solution = linearSystem.solve(knownTermColumn, true);
          // Matrix a = new Matrix(covarianceMatrix);
          // Matrix b = new Matrix(knownTerm, knownTerm.length);
          // Matrix x = a.solve(b);
          double[] moltiplicativeFactor = solution.copyValues1D();

          double h0 = 0.0;
          for (int k = 0; k < n1; k++) {
            h0 = h0 + moltiplicativeFactor[k] * hStation[k];
            sum = sum + moltiplicativeFactor[k];
          }

          if (doLogarithmic) {
            h0 = Math.exp(h0);
          }
          result[j] = h0;
          j++;
          if (Math.abs(sum - 1) >= TOLL) {
            throw new ModelsRuntimeException(
                "Error in the coffeicients calculation", this.getClass().getSimpleName());
          }
        }
      } else if (n1 == 1 || areAllEquals) {
        double tmp = hStation[0];
        int k = 0;
        pm.message(msg.message("kriging.setequalsvalue"));
        while (idIterator.hasNext()) {
          int id = idIterator.next();
          result[k] = tmp;
          idArray[k] = id;
          k++;
        }
      }
      if (pMode == 0) {
        storeResult(result, idArray);
      } else {
        storeResult(result, pointsToInterpolateId2Coordinates);
      }
    } else {
      pm.errorMessage("No rain for this time step");
      j = 0;
      double[] value = inData.values().iterator().next();
      while (idIterator.hasNext()) {
        int id = idIterator.next();
        idArray[j] = id;
        result[j] = value[0];
        j++;
      }
      if (pMode == 0) {
        storeResult(result, idArray);
      } else {
        storeResult(result, pointsToInterpolateId2Coordinates);
      }
    }
  }