Manipuler des données géographiques avec GeoTools

Dans le monde des Systèmes d'Informations Géographique (SIG), il est assez courant de devoir manipuler des données au format Shapefile (ensemble de fichiers standardisés pour la représentation de cartographie) afin de présenter différents types d'informations.

Pour ce faire, le plus simple est encore d'utiliser comme intermédiaire une base Postgre dédiée, autrement nommée PostGIS, spécifique au traitement de ce type de données.

La solution la plus courante est d'utiliser les outils en ligne de commande shp2pgsql ou ogr2ogr qui permettent de créer un fichier SQL à partir des Shapefile et éventuellement de le jouer directement en base. Cependant, le but de ce billet est de présenter comment il est possible en Java d'extraire des informations et/ou ré-organiser un lot de fichiers Shapefile. L'utilisation d'une base de données intermédiaire a pour vocation de résoudre des problèmes de performance liés à la manipulation directe des fichiers.

Voici un exemple de code à utiliser :

public class GeoToPostGISClient {

 private static final String POSTGIS_TABLENAME = "MY_TABLE";

 private static GeoProperties props = GeoProperties.getInstance();

 private static ShapefileDataStoreFactory shpFactory = new ShapefileDataStoreFactory();

 private static FeatureTypeFactoryImpl factory = new FeatureTypeFactoryImpl();

 private static JDBCDataStore pgStore;

 private SimpleFeatureType schema;

 public GeoToPostGISClient() throws IOException {
  // Ouvrir une connexion vers la base PostGIS
  if (pgStore == null) {
   PostgisNGDataStoreFactory pgFactory = new PostgisNGDataStoreFactory();
   Map<String, String> jdbcparams = new HashMap<String, String>();
   jdbcparams.put(PostgisNGDataStoreFactory.DBTYPE.key, "postgis");
   jdbcparams.put(PostgisNGDataStoreFactory.HOST.key, props.getProperty(GeoProperties.DB_HOST));
   jdbcparams.put(PostgisNGDataStoreFactory.PORT.key, props.getProperty(GeoProperties.DB_PORT));
   jdbcparams.put(PostgisNGDataStoreFactory.SCHEMA.key, props.getProperty(GeoProperties.DB_SCHEMA));
   jdbcparams.put(PostgisNGDataStoreFactory.DATABASE.key, props.getProperty(GeoProperties.DB_NAME));
   jdbcparams.put(PostgisNGDataStoreFactory.USER.key, props.getProperty(GeoProperties.DB_USER));
   jdbcparams.put(PostgisNGDataStoreFactory.PASSWD.key, props.getProperty(GeoProperties.DB_PWD));
   pgStore = pgFactory.createDataStore(jdbcparams);
  }
 }

 /**
  * Insert all specified shapefiles in Postgre
  * 
  * @param shapefilePaths files
  * @throws IOException all
  */
 public void insertShpIntoDb(List<String> shapefilePaths) throws IOException {
  Iterator<String> iterator = shapefilePaths.iterator();
  String path = null;
  while (iterator.hasNext()) {
   path = iterator.next();

   Map<String, Object> shpparams = new HashMap<String, Object>();
   shpparams.put("url", "file://" + path);
   // create indexes only for last file (performance issue)
   FileDataStore shpStore = (FileDataStore) shpFactory.createDataStore(shpparams);
   SimpleFeatureCollection features = shpStore.getFeatureSource().getFeatures();

   if (schema == null) {
    // Copy schema and change name in order to refer to the same
    // global schema for all files
    SimpleFeatureType originalSchema = shpStore.getSchema();
    Name originalName = originalSchema.getName();
    NameImpl theName = new NameImpl(originalName.getNamespaceURI(), originalName.getSeparator(), POSTGIS_TABLENAME);
    schema = factory.createSimpleFeatureType(theName, originalSchema.getAttributeDescriptors(), originalSchema.getGeometryDescriptor(),
      originalSchema.isAbstract(), originalSchema.getRestrictions(), originalSchema.getSuper(), originalSchema.getDescription());
    pgStore.createSchema(schema);
   }
   SimpleFeatureStore featureStore = (SimpleFeatureStore) pgStore.getFeatureSource(POSTGIS_TABLENAME);

   // Ajout des objets du shapefile dans la table PostGIS
   DefaultTransaction transaction = new DefaultTransaction("create");
   featureStore.setTransaction(transaction);
   try {
    featureStore.addFeatures(features);
    transaction.commit();
   } catch (Exception problem) {
    LOGGER.error(problem.getMessage(), problem);
    transaction.rollback();
   } finally {
    transaction.close();
   }
   shpStore.dispose();
  }
  extractFromDb();
 }

 /**
  * Extracts local data from postgis DB
  * 
  * @throws IOException all
  */
 public void extractFromDb() throws IOException {
  // Faire une requête spatiale dans la base
  ContentFeatureCollection filteredFeatures = null;

  String destFolder = "/shp/";

  for (Object dep : ReferentielDepartement.getDepartements()) {
   try {
    filteredFeatures = pgStore.getFeatureSource(POSTGIS_TABLENAME).getFeatures(CQL.toFilter("DPT_NUM = '" + dep + "'"));
   } catch (CQLException e) {
    LOGGER.error(e.getMessage(), e);
   }
   if (filteredFeatures != null && filteredFeatures.size() > 0) {
    // Écrire le résultat dans un fichier shapefile
    Map<String, String> destshpparams = new HashMap<String, String>();
    SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
    String destinationSchemaName = "MySchema_" + dep;
    destshpparams.put("url", "file://" + destFolder + destinationSchemaName + "_" + formatter.format(new Date()) + ".shp");
    DataStore destShpStore = shpFactory.createNewDataStore(destshpparams);

    // duplicate existing schema to create destination's one
    Name originalName = schema.getName();
    NameImpl theName = new NameImpl(originalName.getNamespaceURI(), originalName.getSeparator(), destinationSchemaName);
    SimpleFeatureType destschema = factory.createSimpleFeatureType(theName, schema.getAttributeDescriptors(),
      schema.getGeometryDescriptor(), schema.isAbstract(), schema.getRestrictions(), schema.getSuper(), schema.getDescription());
    destShpStore.createSchema(destschema);

    SimpleFeatureStore destFeatureStore = (SimpleFeatureStore) destShpStore.getFeatureSource(destinationSchemaName);
    destFeatureStore.addFeatures(filteredFeatures);

    // Fermer les connections et les fichiers
    destShpStore.dispose();
   }
  }
 }
}

Avec ce type de code, il est possible d'extraire une nouvelle cartographie spécifique (découpage selon la variable DPT_NUM) à partir d'un lot de données source.

Pour une mise en place plus rapide, voici les dépendances nécessaires (pom.xml) :

...
<repositories>
 <repository>
  <id>osgeo</id>
  <name>Open Source Geospatial Foundation Repository</name>
  <url>http://download.osgeo.org/webdav/geotools/</url>
 </repository>
</repositories>
...
<dependencies>
 <!-- Geo Tools -->
 <dependency>
  <groupId>org.geotools</groupId>
  <artifactId>gt-shapefile</artifactId>
  <version>8.0-M4</version>
 </dependency>
 <dependency>
  <groupId>org.geotools.jdbc</groupId>
  <artifactId>gt-jdbc-postgis</artifactId>
  <version>8.0-M4</version>
 </dependency>
 <dependency>
  <groupId>org.geotools</groupId>
  <artifactId>gt-cql</artifactId>
  <version>8.0-M4</version>
 </dependency>
</dependencies>

Voilà tout, bon courage!
HTH


Fichier(s) joint(s) :

0 commentaires: