6 votos

¿Escribir un flujo GeoJSON en un Shapefile utilizando GeoTools?

He estado probando algunas soluciones para convertir de forma eficiente GeoJSON en un archivo shapefile sin tener que almacenar todas las características en la memoria. Estoy utilizando GeoTools 9.2.

El problema no está tanto en cómo transmitir el JSON sino en cómo escribir eficientemente las características en el shapefile. Utilizo FeatureJSON#streamFeatureCollection para obtener un iterador. Después de googling, he encontrado 3 maneras diferentes de escribir un shapefile, a saber:

Opción 1 : Llamar repetidamente a FeatureStore#addFeatures con una colección que contiene digamos 1000 características, dentro de una transacción.

  ListFeatureCollection coll = new ListFeatureCollection(type, features);
  Transaction transaction = new DefaultTransaction("create");
  featureStore.setTransaction(transaction);
  try {
    featureStore.addFeatures(coll);
    transaction.commit();
  } catch (IOException e) {
    transaction.rollback();
    throw new IllegalStateException(
        "Could not write some features to shapefile. Aborting process", e);
  } finally {
    transaction.close();
  }

Esta opción es extremadamente lenta. Haciendo un perfil de algunas ejecuciones, noté que alrededor del 50% del tiempo de CPU se gasta en el método ContentFeatureStore#getWriterAppend, presumiblemente con el fin de alcanzar el final del archivo antes de cada transacción.

Opción 2 : Obtener un escritor append directamente de ShapefileDataStore, y escribir 1000 características a la vez dentro de una transacción.

Esta opción adolece de los mismos problemas que la número uno.

Opción 3 : Obtener un escritor de características de ShapefileDataStore, y escribir una característica a la vez usando Transaction.AUTO_COMMIT.

 FeatureWriter<SimpleFeatureType, SimpleFeature> writer = shpDataStore
    .getFeatureWriter(shpDataStore.getTypeNames()[0],
        Transaction.AUTO_COMMIT);

 while (jsonIt.hasNext()) {

  SimpleFeature feature = jsonIt.next();
  SimpleFeature toWrite = writer.next();
  for (int i = 0; i < toWrite.getType().getAttributeCount(); i++) {
    String name = toWrite.getType().getDescriptor(i).getLocalName();
    toWrite.setAttribute(name, feature.getAttribute(name));
  }
  writer.write();
}
writer.close();

La opción 3 es la más rápida, pero creo que habría una manera de eficientemente añadir un mayor número de características a la vez al shapefile dentro de una transacción.

Por otra parte, un comentario anterior en la lista de correo de GeoTools señalaba:

Lo anterior funcionaría para transferencias de datos medianas, para masivas contra bases de datos es mejor adoptar algún tipo de batching para evitar tener una única transacción con un millón de inserciones, por ejemplo, insertar 1000, confirmar la transacción, insertar otros 1000, y así sucesivamente. Este funcionaría mejor contra bases de datos y contra servidores WFS, pero no contra archivos shape que en cambio funcionan mejor con el masivo inserción... cada uno a lo suyo.

¿Significa esto que la forma más eficiente de escribir en un shapefile es tener todas las características en la memoria, en lugar de ser capaz de añadir características?

2voto

Alan Krueger Puntos 2148

Con la ayuda de la gente en la lista de correo GeoTools, se me ocurrió el método completo que toma un JSON InputStream opcionalmente un SimpleFeatureType y un ShapefileDataStore y devuelve un archivo shapefile SimpleFeatureSource .

public static SimpleFeatureSource geoJSON2Shp(InputStream input,
        SimpleFeatureType schema, ShapefileDataStore shpDataStore)
        throws IOException {

    FeatureJSON fjson = new FeatureJSON(new GeometryJSON(15));

    SimpleFeatureType featureType = schema;

    if (featureType != null) {
        fjson.setFeatureType(featureType);
    }

    FeatureIterator<SimpleFeature> jsonIt = fjson
            .streamFeatureCollection(input);

    if (!jsonIt.hasNext()) {
        throw new IllegalArgumentException(
                "Cannot create shapefile. GeoJSON stream is empty");
    }

    FeatureWriter<SimpleFeatureType, SimpleFeature> writer = null;

    try {

        // use feature type of first feature, if not supplied
        SimpleFeature firstFeature = jsonIt.next();
        if (featureType == null) {
            featureType = firstFeature.getFeatureType();
        }

        shpDataStore.createSchema(featureType);

        writer = shpDataStore.getFeatureWriterAppend(
                shpDataStore.getTypeNames()[0], Transaction.AUTO_COMMIT);

        addFeature(firstFeature, writer);

        while (jsonIt.hasNext()) {
            SimpleFeature feature = jsonIt.next();
            addFeature(feature, writer);                
        }
    } finally {
        if (writer != null) {
            writer.close();
        }
    }

    return shpDataStore.getFeatureSource(shpDataStore.getTypeNames()[0]);
}

private static void addFeature(SimpleFeature feature,
        FeatureWriter<SimpleFeatureType, SimpleFeature> writer)
        throws IOException {

    SimpleFeature toWrite = writer.next();
    for (int i = 0; i < toWrite.getType().getAttributeCount(); i++) {
        String name = toWrite.getType().getDescriptor(i).getLocalName();
        toWrite.setAttribute(name, feature.getAttribute(name));
    }

    // copy over the user data
    if (feature.getUserData().size() > 0) {
        toWrite.getUserData().putAll(feature.getUserData());
    }

    // perform the write
    writer.write();
}

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X