Cartographie avec GeoTools


précédentsommaire

III. Les données

III-A. Récupérer les données

L'interface DataStore est utilisée par les classes offrant la lecture et éventuellement l'écriture des données. Il faut savoir qu'il y un grand nombre de formats SIG existant et en conséquence des méthodes pour accéder aux données très variées. Il peut s'agir de bases de données, auquel cas la communication sera en SQL, de fichier donc de flux ou encore de serveur distant avec des flux RSS, WMS ... sans oublier des connexions temps réel.
Rassurez-vous, l'interface DataStore est là pour offrir des méthodes communes pour récupérer les données quelque soit leur source d'origine.

III-A-1. Données vectorielles : DataStore

La façon la plus simple de récupérer un DataStore est d'utiliser la fabrique dédiée à cet effet.

 
Sélectionnez

Map config = new HashMap();
config.put("url", file.toURL() );
 
DataStore dataStore = DataStoreFinder.getDataStore( config );

La fabrique va parcourir chaque implémentation de DataStore qu'elle connait et va essayer d'en créer une instance avec les paramètres fournis.
Dans le cas ci-dessus, nous n'avons qu'une url donc en fonction du fichier on pourrait obtenir un datastore correspondant à un fichier shape, GML ...etc...

Pour une base de données il faut fournir d'avantage d'informations. Voici un exemple pour une source de type PostGIS.

 
Sélectionnez

Map config = new HashMap();
config.put( PostgisDataStoreFactory.DBTYPE.key, "postgis" );		
config.put( PostgisDataStoreFactory.HOST.key, "127.0.0.1" );
config.put( PostgisDataStoreFactory.PORT.key, "5432" );		
config.put( PostgisDataStoreFactory.SCHEMA.key, "public" );
config.put( PostgisDataStoreFactory.DATABASE.key, "mabase" );
config.put( PostgisDataStoreFactory.USER.key, "user" );
config.put( PostgisDataStoreFactory.PASSWD.key, "mdp" );
DataStore dataStore = DataStoreFinder.getDataStore( config );	

Les clés possibles peuvent être trouvées de plusieurs façons :
- comme proriétés statiques dans les datastores
- dynamiquement (code ci-dessous)
- dans la javadoc

 
Sélectionnez

PostgisDataStoreFactory pdsf = new PostgisDataStoreFactory();
 
Param[] params = pdsf.getParametersInfo();
for(Param pm : params){
	//une description de la clé
    String desc = pm.description;
    //la clé
    String key = pm.key;
    //Vrai si cette clé est obligatoire
    boolean needed = pm.required;
    //un exemple de valeur possible
    Object value = pm.sample;
    //la classe nécessaire
    Class classe = pm.type;
}

Il y a aussi un bon nombre de méthodes disponibles sur la classe DataStoreFinder qui peuvent vous servir.

L'autre méthode consiste à créer un Datastore directement avec un objet de classe DataStore que l'on souhaite, vous en avez un exemple au chapitre 1. Toutefois tous les constructeurs vont devenir privés et seul le DataStoreFinder pourra créer ce genre d'objet.

III-A-2. Données rasters : GridCoverage

Pour les rasters il existe un GridCoverageFinder mais il n'est pas encore au point, il est préférable que vous utilisiez les classes qu'il vous faut plutôt que celui-ci.
Pour les fichiers rasters voici une fonction qui peut être utilisée.

 
Sélectionnez

 
private static final String GEOTIFF = ".tif";
private static final String BMP = ".bmp";
private static final String JPG = ".jpg";
private static final String JPEG = ".jpeg";
private static final String PNG = ".png";
 
/**
* return a gridcoverage for Raster file. Use a Map containing key "url"
* @param params 
* @return GridCoverage
*/
    public static GridCoverage getGridCoverage(Map params){
 
        GridCoverage cover = null;
 
        URL url = (URL) params.get("url");
 
        if (url != null) {
            String name = url.getFile().toLowerCase();
            File file = null;
 
            try {
                file = new File(url.toURI());
            } catch (URISyntaxException ex) {
                ex.printStackTrace();
            }
 
            if (file != null) {
                // try a geotiff gridcoverage
                if (name.endsWith(GEOTIFF)) {
 
                    try {
                        GeoTiffReader reader = new GeoTiffReader(file, new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, Boolean.TRUE));
                        cover = (GridCoverage2D) reader.read(null);
                    } catch (DataSourceException ex) {
                        cover = null;
                        ex.printStackTrace();
                    }catch (IOException ex){
                        cover = null;
                        ex.printStackTrace();
                    }
                } 
                // try a world image file
                else if (name.endsWith(BMP) || name.endsWith(JPG) || name.endsWith(JPEG) || name.endsWith(PNG)) {
 
                    try {
                        WorldImageReader reader = new WorldImageReader(file);
                        cover = (GridCoverage2D) reader.read(null);
                    } catch (DataSourceException ex) {
                        cover = null;
                        ex.printStackTrace();
                    }catch (IOException ex){
                        cover = null;
                        ex.printStackTrace();
                    }
                }
            }
 
        }
 
        return cover;
    }

III-B. Explorer les données

Chap2Donnees.java

Nous allons créer un MapLayer à partir d'un fichier shape.

 
Sélectionnez

    public Chap2Donnees() {
        URL url = null;
        try {
            url = new File("c:/RESROU_TRONCON_ROUTE.shp").toURI().toURL();
        } catch (MalformedURLException ex) {
            ex.printStackTrace();
        }
 
        if (url != null) {
            MapLayer layer = createLayer(url);
 
            try {
                explorer(layer);
                parcourir(layer);
                ajouter(layer);
                supprimer(layer);
                modifier(layer);
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
 
    private MapLayer createLayer(URL url) {
 
        MapLayer layer = null;
        FeatureSource source = null;
 
        try {
            DataStore store = new ShapefileDataStore(url);
            String name = store.getTypeNames()[0];
            source = store.getFeatureSource(name);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
 
        if (source != null) {
            Style style = createStyle();
            layer = new DefaultMapLayer(source, style);
        }
 
        return layer;
    }

III-B-1. Schéma

Le schéma correspond à la structure des données, un peu comme une table dans une base de données, on peut connaitre quelles sont les colonnes et leur type.

 
Sélectionnez

    private void explorer(MapLayer layer) throws IOException {
 
        FeatureType type = layer.getFeatureSource().getSchema();
 
        GeometryDescriptor geodesc = type.getDefaultGeometry();
        System.out.println("GEOM : " + geodesc.getName());
        System.out.println("GEOM : " + geodesc.getType().getBinding());
 
 
        Collection<PropertyDescriptor> properties = type.getProperties();
        Iterator<PropertyDescriptor> propertiesIte = properties.iterator();
 
        while (propertiesIte.hasNext()) {
            PropertyDescriptor oneProperty = propertiesIte.next();
            System.out.println(oneProperty.getName() + " : " + oneProperty.getType().getBinding());
        }
    }

La méthode getDefaultGeometry() nous permet de récupérer les informations sur la géométrie.
Les interfaces concernant la description des attributs sont les suivantes :
PropertyDescriptor - AttributDescriptor - GeometryDescriptor

La méthode getName() est assez claire, c'est le nom de l'attribut.
getType().getBinding() va nous permettre de récupérer le type de l'attribut. Dans le cas de la "colonne" correspondant à la géométrie nous pourrons avoir les classes de géométrie de la librairie JTS (Java Topology Suite).

III-B-2. Parcourir

 
Sélectionnez

    private void parcourir(MapLayer layer) throws IOException {
 
        FeatureCollection features = layer.getFeatureSource().getFeatures();
        FeatureIterator featuresIte = features.features();
 
        while (featuresIte.hasNext()) {
            SimpleFeature oneFeature = featuresIte.next();
 
            for (int i = 0,  max = oneFeature.getAttributeCount(); i < max; i++) {
                System.out.println(oneFeature.getAttribute(i));
            }
        }
    }

On récupère d'abord une collection de nos éléments que l'on parcourt avec un itérateur.

- Pourquoi ne pas fournir un tableau de tous les éléments ?
Car on ne sait pas combien il y en a, il n'est pas rare d'avoir quelques centaines de milliers d'enregistrements. En plus, il ne faut pas oublier le champ géométrique qui peut être très volumineux. On arriverait très vite en saturation mémoire.

Il y a eu un bon nombre de discussions à propos de l'usage de la classe FeatureCollection, à cause de certaines limites dans la flexibilité de celle-ci. Donc dans l'avenir cette facon de procéder devrait peut être changer.

III-B-5. Modifier

 
Sélectionnez

    private void modifier(MapLayer layer) throws IOException {
        FeatureStore store;
 
        // (1)
        FeatureCollection features = layer.getFeatureSource().getFeatures();
        SimpleFeature oneFeature = features.features().next();
 
        // (2)
        AttributeDescriptor oneAttribut = layer.getFeatureSource().getSchema().getAttribute("NB_VOIES");
 
        // (3)
        String value = "4";
 
 
        // (4)
        if (layer.getFeatureSource() instanceof FeatureStore) {
 
            store = (FeatureStore) layer.getFeatureSource();
            // (5)
            DefaultTransaction transaction = new DefaultTransaction("trans_maj");
            store.setTransaction(transaction);
 
            // (6)
            FilterFactory factory = CommonFactoryFinder.getFilterFactory(GeoTools.getDefaultHints());
            Filter filter = factory.id(Collections.singleton(factory.featureId(oneFeature.getID())));
 
            // (7)
            try {
                store.modifyFeatures(oneAttribut, value, filter);
                transaction.commit();
            } catch (IOException ex) {
                ex.printStackTrace();
                try {
                    transaction.rollback();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }finally{
                transaction.close();
            }
        }
    }

Voilà qui doit paraître compliqué.

1) La première chose à faire c'est de récupérer l'élément que l'on veut modifier, dans le cas ci-dessus on prend tout simplement le premier qui vient.
2) Il faut ensuite savoir quel champ on va modifier, pour cela on passe par le schéma et on récupère le descripteur correspondant.
3) On récupère/crée la nouvelle valeur à affecter.
4) Ce test peut être fait en premier, c'est selon votre application. FeatureStore est une interface qui correspond à un FeatureSource éditable. Autrement dit si le FeatureSource possède cette interface il permet l'édition.
5) Toute communication, pour une modification un ajout ou une suppression passe par une transaction. La transaction peut concerner 1 ou N éléments, c'est pour ça que l'on utilise un filtre. 6) Ce filtre va permettre de limiter l'étendue des modifications uniquement aux éléments qui seront en sortie de celui-ci. Un filtre peut être très complexe, j'y reviendrai dans le chapître sur le requêtage.
7) Et on applique nos modifications, ainsi qu'un commit(). Vous remarquerez le rollback() comme en SQL .

III-B-3. Ajouter

 
Sélectionnez

    private void ajouter(MapLayer layer) {
        FeatureStore store;
 
        // (1)
        GeometryFactory geoFactory = new GeometryFactory();
 
        // (2)
        Coordinate coord1 = new Coordinate(50, 430);
        Coordinate coord2 = new Coordinate(21, 712);
        LineString line = geoFactory.createLineString(new Coordinate[]{coord1, coord2});
        MultiLineString lines = geoFactory.createMultiLineString(new LineString[]{line});
 
        SimpleFeatureType featureType = layer.getFeatureSource().getSchema();
 
        // (3)
        Object[] values = new Object[featureType.getAttributeCount()];
 
        AttributeDescriptor geomAttribut = featureType.getDefaultGeometry();
        List<AttributeDescriptor> attributes = featureType.getAttributes();
 
        // (4) 
        for (int i = 0,  max = attributes.size(); i < max; i++) {
            AttributeDescriptor oneAttribut = attributes.get(i);
 
            // (5) 
            if (oneAttribut.equals(geomAttribut)) {
                values[i] = lines;
            } else {
                values[i] = oneAttribut.getDefaultValue();
            }
        }
 
        // (6) 
        SimpleFeature myFeature = SimpleFeatureBuilder.build(featureType, values, null);
 
        // (7) 
        FeatureCollection lstFeatures = FeatureCollections.newCollection();
        lstFeatures.add(myFeature);
 
        // (8) 
        if (layer.getFeatureSource() instanceof FeatureStore) {
            store = (FeatureStore) layer.getFeatureSource();
 
            DefaultTransaction transaction = new DefaultTransaction();
            store.setTransaction(transaction);
 
            // (9)
            try {
                store.addFeatures(lstFeatures);
                transaction.commit();                
            } catch (Exception ex) {
                ex.printStackTrace();
                try {
                    store.getTransaction().rollback();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }finally{
                transaction.close();
            }
 
        }
 
    }

1) GeometryFactory vient de la librairie JTS, elle permet de créer les différents objets géometriques.
2) Ici on fabrique une multi-ligne, j'en reparlerai dans un article sur JTS.
3) On prépare un tableau de la taille du nombre d'attribut que possède notre couche.
4) On remplit le tableau
5) On fait attention à placer des valeurs correctes pour chaque attribut, pour avoir la valeur par défaut on utilise le getDefaultValue() du descripteur.
6) On crée un objet correspondant à un enregistrement
7) On le place dans une collection d'enregistrements
8) On vérifie que notre couche est bien éditable
9) et on ajoute la collection d'enregistrements

III-B-4. Supprimer

 
Sélectionnez

        FeatureStore store;
 
        // (1)
        FeatureCollection features = layer.getFeatureSource().getFeatures();
        SimpleFeature oneFeature = features.features().next();
 
        // (2)
        if (layer.getFeatureSource() instanceof FeatureStore) {
 
            store = (FeatureStore) layer.getFeatureSource();
            // (3)
            DefaultTransaction transaction = new DefaultTransaction("trans_maj");
            store.setTransaction(transaction);
 
            // (4)
            FilterFactory factory = CommonFactoryFinder.getFilterFactory(GeoTools.getDefaultHints());
            Filter filter = factory.id(Collections.singleton(factory.featureId(oneFeature.getID())));
 
            // (5)
            try {
                store.removeFeatures(filter);
                transaction.commit();
            } catch (IOException ex) {
                ex.printStackTrace();
                try {
                    transaction.rollback();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }finally{
                transaction.close();
            }
        }
    }

Le principe ici est très proche de la modification, même plus simple.

1) La première chose à faire c'est récupérer l'élément que l'on veut supprimer, dans le cas ci-dessus on prend tout simplement le premier qui vient.
2) On vérifie que notre couche est bien éditable
3) On crée une transaction.
4) On crée le filtre.
5) Et on applique la suppresion.

III-C. Widgets Swing

ATTENTION : Les Widgets Swing sont encore en développement !
Aucun support n'est assuré.

III-C-1. JDataChooser

Ce widget est semblable à un JFileChooser, bien qu'il ne soit pas terminé il permet déjà de se simplifier la vie si on a besoin de récupérer rapidement des objets MapLayer.

Image non disponible


Utilisation :

 
Sélectionnez

//liste des panneaux visibles
List<DataPanel> lst = new ArrayList<DataPanel>();
lst.add(new JFileDataPanel());
lst.add(new JDatabaseDataPanel());
 
JDataChooser jdc = new JDataChooser(null,lst);            
JDataChooser.ACTION ret = jdc.showDialog();
 
if (ret == JDataChooser.ACTION.APPROVE) {
	//tous les MapLayers correspondants aux fichiers/tables sélectionnées.
	List<MapLayer> layers = jdc.getLayers();
	}

III-D. FAQ

III-D-1. Comment reconnaître un MapLayer de type Raster ?

 
Sélectionnez

if (layer.getFeatureSource().getSchema().getTypeName().equals("GridCoverage")) {
	System.out.println("Layer de type raster");
}else{
	System.out.println("Layer de type vecteur");
}

III-D-2. Comment connaître le type de géométrie ?

 
Sélectionnez

import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.Polygon;
 
 
Class jtsClass = layer.getFeatureSource().getSchema().getDefaultGeometry().getType().getBinding();
 
if (jtsClass.equals(Point.class)) {
	System.out.println("Geometrie de type POINT");
} else if (jtsClass.equals(MultiPoint.class)) {
    System.out.println("Geometrie de type MULTI-POINT");
 } else if (jtsClass.equals(LineString.class)) {
    System.out.println("Geometrie de type LIGNE");
} else if (jtsClass.equals(MultiLineString.class)) {
    System.out.println("Geometrie de type MULTI-LIGNE");
} else if (jtsClass.equals(Polygon.class)) {
    System.out.println("Geometrie de type POLYGONE");
} else if (jtsClass.equals(MultiPolygon.class)) {
    System.out.println("Geometrie de type MULTI-POLYGONE");
}

III-D-3. Comment savoir si un MapLayer est éditable ?

 
Sélectionnez

if (layer.getFeatureSource() instanceof FeatureStore) {
	FeatureStore store = (FeatureStore) layer.getFeatureSource();  
	System.out.println("Ce layer est éditable");
}else{
	System.out.println("Ce layer n'est pas éditable");
}

précédentsommaire

  

Copyright © 16/06/2007 Johann Sorel. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.