@Override
 public Schema clone() {
   Schema copy;
   try {
     copy = ((Schema) super.clone());
   } catch (CloneNotSupportedException _x) {
     throw new InternalError((_x.toString()));
   }
   copy.simpleField = new ArrayList<SimpleField>((getSimpleField().size()));
   for (SimpleField iter : simpleField) {
     copy.simpleField.add(iter.clone());
   }
   copy.schemaExtension = new ArrayList<Object>((getSchemaExtension().size()));
   for (Object iter : schemaExtension) {
     copy.schemaExtension.add(iter);
   }
   return copy;
 }
  public void writeFeatures(List<Feature> features) throws KettleException {

    Kml kml = new Kml();
    Document document = kml.createAndSetDocument();

    if (this.documentName != null) {
      document.setName(documentName);
    }

    if (this.documentDescription != null) {
      document.setDescription(documentDescription);
    }

    // Si export des attributs
    if (exportWithAttributs) {

      Schema schema = document.createAndAddSchema();
      schema.setId("dataSchema");
      schema.setName("");

      Iterator<Field> fieldIt = this.fields.iterator();
      while (fieldIt.hasNext()) {

        Field field = fieldIt.next();

        // Pas pris en compte ici : une seule géométrie
        if (!field.getType().equals(FieldType.GEOMETRY)) {

          SimpleField simpleField = schema.createAndAddSimpleField();
          simpleField.setName(field.getName());

          // Texte
          if (field.getType().equals(FieldType.STRING)) {

            simpleField.setType("string");

            // Date
          } else if (field.getType().equals(FieldType.DATE)) {

            simpleField.setType("date");

            // Entier
          } else if (field.getType().equals(FieldType.LONG)) {

            simpleField.setType("int");

            // Double
          } else if (field.getType().equals(FieldType.DOUBLE)) {

            simpleField.setType("float");

            // Booléen
          } else if (field.getType().equals(FieldType.BOOLEAN)) {

            simpleField.setType("bool");

            // Autres types
          } else {
            simpleField.setType("string");
          }
        }
      }
    }

    // Récupération des champs utilisés
    Field geometryField = null;
    Field nameField = null;
    Field descriptionField = null;

    Iterator<Feature> featureIt = features.iterator();
    boolean first = true;
    while (featureIt.hasNext()) {

      Feature feature = featureIt.next();
      if (first) {

        geometryField = feature.getField(this.geometryFieldName);

        if (featureNameField != null) {
          nameField = feature.getField(this.featureNameField);
        }

        if (featureDescriptionField != null) {
          descriptionField = feature.getField(this.featureDescriptionField);
        }

        first = false;
      }

      Geometry geometry = (Geometry) feature.getValue(geometryField);
      Envelope envelope = geometry.getEnvelopeInternal();

      // Vérification de l'emprise : doit être en WGS 84
      if (envelope.getMaxX() > 180
          || envelope.getMinX() < -180
          || envelope.getMaxY() > 90
          || envelope.getMinY() < -90) {

        throw new KettleException("Bad coordinates for WGS84 system");
      }

      Placemark placemark = document.createAndAddPlacemark();

      // Nom de feature
      if (featureNameField != null) {
        String name = (String) feature.getValue(nameField);
        if (name != null) {
          placemark.setName(name);
        }
      }

      // Description de feature
      if (featureDescriptionField != null) {
        String description = (String) feature.getValue(descriptionField);
        if (description != null) {
          placemark.setDescription(description);
        }
      }

      // Attributs
      if (exportWithAttributs) {
        ExtendedData extendedData = placemark.createAndSetExtendedData();
        SchemaData schemaData = extendedData.createAndAddSchemaData();
        schemaData.setSchemaUrl("dataSchema");

        Iterator<Field> colIt = this.fields.iterator();
        while (colIt.hasNext()) {

          Field field = colIt.next();
          if (!field.getType().equals(FieldType.GEOMETRY)) {

            Object value = feature.getValue(field);
            SimpleData simpleData = schemaData.createAndAddSimpleData(field.getName());
            simpleData.setValue(String.valueOf(value));
          }
        }
      }

      // En fonction dy type de géométrie Jts, appel
      // aux fonctions de conversion en géométries Kml

      // POINT
      if (geometry instanceof Point) {

        placemark.setGeometry(getAsKmlPoint((Point) geometry));

        // LINESTRING
      } else if (geometry instanceof LineString) {

        placemark.setGeometry(getAsKmlLineString((LineString) geometry));

        // POLYGON
      } else if (geometry instanceof Polygon) {

        placemark.setGeometry(getAsKmlPolygon((Polygon) geometry));

        // MULTIPOINT
      } else if (geometry instanceof MultiPoint) {

        de.micromata.opengis.kml.v_2_2_0.MultiGeometry kmlMultiGeometry =
            placemark.createAndSetMultiGeometry();
        for (int i = 0; i < geometry.getNumGeometries(); i++) {

          kmlMultiGeometry.addToGeometry(getAsKmlPoint((Point) ((Point) geometry).getGeometryN(i)));
        }
        // MULTILINESTRING
      } else if (geometry instanceof MultiLineString) {

        de.micromata.opengis.kml.v_2_2_0.MultiGeometry kmlMultiGeometry =
            placemark.createAndSetMultiGeometry();
        for (int i = 0; i < geometry.getNumGeometries(); i++) {

          kmlMultiGeometry.addToGeometry(
              getAsKmlLineString((LineString) ((MultiLineString) geometry).getGeometryN(i)));
        }
        // MULTIPOLYGON
      } else if (geometry instanceof MultiPolygon) {

        de.micromata.opengis.kml.v_2_2_0.MultiGeometry kmlMultiGeometry =
            placemark.createAndSetMultiGeometry();
        for (int i = 0; i < geometry.getNumGeometries(); i++) {

          kmlMultiGeometry.addToGeometry(
              getAsKmlPolygon((Polygon) ((MultiPolygon) geometry).getGeometryN(i)));
        }
        // GEOMETRYCOLLECTION
      } else if (geometry instanceof GeometryCollection) {

        de.micromata.opengis.kml.v_2_2_0.MultiGeometry kmlMultiGeometry =
            placemark.createAndSetMultiGeometry();
        for (int i = 0; i < geometry.getNumGeometries(); i++) {

          Geometry currentGeometry = geometry.getGeometryN(i);

          if (currentGeometry instanceof Point) {
            kmlMultiGeometry.addToGeometry(getAsKmlPoint((Point) currentGeometry));
          } else if (currentGeometry instanceof LineString) {
            kmlMultiGeometry.addToGeometry(getAsKmlLineString((LineString) currentGeometry));
          } else if (currentGeometry instanceof Polygon) {
            kmlMultiGeometry.addToGeometry(getAsKmlPolygon((Polygon) currentGeometry));
          } else if (currentGeometry instanceof MultiPoint) {

            for (int j = 0; j < currentGeometry.getNumGeometries(); j++) {
              kmlMultiGeometry.addToGeometry(
                  getAsKmlPoint((Point) ((Point) currentGeometry).getGeometryN(j)));
            }

          } else if (currentGeometry instanceof MultiLineString) {

            for (int j = 0; j < currentGeometry.getNumGeometries(); j++) {
              kmlMultiGeometry.addToGeometry(
                  getAsKmlLineString((LineString) ((LineString) currentGeometry).getGeometryN(j)));
            }

          } else if (currentGeometry instanceof MultiPolygon) {

            for (int j = 0; j < currentGeometry.getNumGeometries(); j++) {
              kmlMultiGeometry.addToGeometry(
                  getAsKmlPolygon((Polygon) ((Polygon) currentGeometry).getGeometryN(j)));
            }
          }
        }
      }
    }

    if (isServletOutput) {

      if (features.size() > 0) {
        kml.marshal();
        kml.marshal(writer);
      }

    } else {

      try {

        FileOutputStream fileOutputStream = new FileOutputStream(this.kmlFileName);
        kml.marshal();
        kml.marshal(fileOutputStream);
        fileOutputStream.close();

      } catch (FileNotFoundException e) {
        throw new KettleException("Error writing features to " + this.kmlFileName, e);
      } catch (IOException e) {
        throw new KettleException("Error writing features to " + this.kmlFileName, e);
      }
    }
  }