Cartographie avec GeoTools

johann Sorel

Articles sur la manipulation de la librairie GeoTools. ?

- Titre : Cartographie avec GeoTools

- Auteur : johann Sorel

- Parution : 5 juillet 2007

- Mise à jour : 17 janvier 2008

- Licence : 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.

Sommaire

- I. Introduction

- I-A. Pourquoi utiliser une librairie SIG ?

- I-B. API GeoTools

- I-C. Liens

- I-D. Remerciements

- II. Première carte

- II-A. Le décor

- II-B. La carte

- II-C. Le contrôle

- II-D. Le Résultat

- III. Les données

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

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

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

- III-B. Explorer les données

- III-B-1. Schéma

- III-B-2. Parcourir

- III-B-3. Modifier

- III-B-4. Ajouter

- III-B-5. Supprimer

- III-C. Widgets Swing

- III-C-1. JDataChooser

- III-D. FAQ

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

- III-D-2. Comment connaitre le type de géométrie ?

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

I - Introduction

I-A - Pourquoi utiliser une librairie SIG ?

On peut voir de nos jours de plus en plus d'applications tournant autour des SIG (Systèmes d'Informations Géographiques). Vous les utilisez sans vous en rendre compte peut-être, si vous utilisez une carte IGN, mappy, googleEarth, un GPS de voiture ou une simple carte touristique de la ville, alors vous pouvez être sûr qu'il y a un SIG derrière.

Faisons un tour rapide des données utilisées pour les SIG :
- les images rasters (photo aérienne, satellite, altimétrique…) ;
- les images vectorielles (polygone, linéaire, ponctuel…) ;
- les attributs (nom, etat, catégorie…).

Ces données peuvent être stockées sous plusieurs formes, voici les 3 plus en vogue du moment :
- format GeoTiff, comme son nom l'indique c'est un tiff avec des données de référencement géographique ;
- format shape, le fichier shape n'est pas nouveau, il permet de stocker les données vectorielles ;
- PostGIS, il s'agit d'une amélioration de la base de données libre PostgreSQL, cette « cartouche spatiale » comme on l'appelle permet de stocker les formes géométriques 2D et 3D dans la base de données.

I-B - API GeoTools

GeoTools est une librairie Java sous licence LGPL qui regroupe une quantité d'autres librairies, ce qui explique sa taille d'une vingtaine de mégaoctets. Elle se base sur les normes en vigueur dans le domaine des SIG, j'entends par là les normes ISO ainsi que celles particulières à la géographie de l'OGC (Open Geospatial Consortium).

La première version de GeoTools date de 1996 et la deuxième de 2000. Actuellement la version stable est la 2.3.2 et une version 2.4 est en cours de développement.
Parmi les librairies qui sont regroupées, on note la présence de :
- GeoAPI : qui fournit les classes de type interfaces afin de normaliser et améliorer l'interopérabilité des applications SIG ;
- JTS : pour Java Topology Suite, librairie qui permet de faire des opérations sur les géométries ;
- de nombreuses librairies de connexion aux bases de données et de lecture de fichier.

I-C - Liens

GeoTools : http://geotools.codehaus.org/
GeoTools, documentation utilisateur : http://www.geotools.fr/manual/geotools.xhtml
GeoTools, tutoriels (pour anciennes versions) : http://geotools.codehaus.org/Tutorials
GeoTools, javadoc : http://javadoc.geotools.fr/2.3/

JTS : http://www.vividsolutions.com/jts/jtshome.htm
GeoAPI : http://geoapi.sourceforge.net/stable/site/index.html
OGC : http://www.opengeospatial.org/

Forum java developpez.com :
https://www.developpez.net/forums/forumdisplay.php?f=6

Forum SIG :
forumsig
georezo

I-D - Remerciements

Vincent Heurteaux : Directeur/cofondateur de Geomatys.

II - Première carte

Chap1Contact.java

II-A - Le décor

Commençons par récupérer les librairies qui nous seront nécessaires.
GeoTools : http://geotools.codehaus.org/Downloads
JAI : https://jai.dev.java.net/binary-builds.html
Et deux fichiers de données : shapefiles

JAI est une API pour la lecture et le traitement des images, il est fortement conseillé de l'avoir avec Geotools, car certaines fonctions nécessitent celle-ci afin d'améliorer la vitesse ou simplement afin de marcher.

Commençons par une petite fenêtre, rien que du classique :

public Main() {
JFrame frm = new JFrame();
frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frm.setSize(800,600);
frm.setTitle("Ma Première carte avec GeoTools");

JPanel pfond = new JPanel(new BorderLayout());
frm.setContentPane(pfond);

frm.setJMenuBar(buildMenu());
frm.getContentPane().add(BorderLayout.CENTER,buildMap());
frm.getContentPane().add(BorderLayout.NORTH,buildTool());

frm.setVisible(true);
}

private JMenuBar buildMenu() {
JMenuBar menu = new JMenuBar();
JMenu mfichier = new JMenu("Fichier");
JMenuItem iquitter = new JMenuItem("Quitter");
iquitter.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
mfichier.add(iquitter);
menu.add(mfichier);

return menu;
}

II-B - La carte

Voyons maintenant ce qui nous intéresse vraiment.

Nous allons prendre soin de déclarer un objet JMapPane pour la classe :

private JMapPane mappane = new JMapPane();

Cet objet est le composant graphique qui va nous permettre de voir notre carte. Ce genre de composant graphique est assez rare dans GeoTools. GeoTools essaie de séparer le côté pratique (calcul, chargement des données…) de l'aspect visuel. On ne trouve donc que le strict minimum graphique dans GeoTools. La tâche de présentation est laissée aux développeurs que nous sommes.

private JPanel buildMap() {

mappane.setBackground(new Color(157,201,255));

try{

MapContext mapcontext = new DefaultMapContext();
mapcontext.setTitle( "Projet" );

mappane.setContext(mapcontext);

MapLayer maplayer;

On commence par donner une couleur de fond bleu à notre composant graphique.
Voilà un nouvel objet, le MapContext, celui-ci représente notre projet au sens cartographique. Cet objet va stocker les informations, par exemple les différentes couches (fichiers) qui ont été chargées, la projection (la représentation du global terrestre) ou encore l'ordre des couches présentes.

La classe Mapcontext est une interface tandis que DefaultMapContext en est une implémentation. Dans GeoTools il y a de très nombreuses interfaces, il est conseillé de manipuler les objets par leur interface.
Petite chose à noter, GeoTools est en constante mutation, si vous tombez sur des méthodes ou des classes dépréciées parmi les exemples que je donne ou sur le site officiel, ne soyez pas surpris.

L'objet MapLayer est une couche qui sera ajoutée au MapContext.

Si vous êtes assez peu familier avec les termes SIG, je vous propose d'aller explorer les liens ci-dessous :
http://www.cartographie.ird.fr/publi/documents/sig1.pdf
http://fr.wikipedia.org/wiki/Système_d'information_géographique

Ainsi que ce document (trouvé au hasard) qui montre bien le problème de la représentation cartographique :
il ne s'agit pas de le comprendre, mais juste de constater que ce n'est pas chose aisée.
http://www.carto.net/papers/florent_chuffart/florent_chuffart_-_webmapping_3D.pdf

URL shapeURL = Main.class.getResource("/occ_sol.shp");

ShapefileDataStore store = new ShapefileDataStore(shapeURL);
String name = store.getTypeNames()[0];
FeatureSource source = store.getFeatureSource(name);

Ci-dessus il s'agit de récupérer des données vectorielles contenues dans un fichier shape (.shp).
On récupère l'URL de ce fichier puis on construit un ShapefileDataStore avec. Le fichier peut être situé à différents endroits, en local, sur un serveur ou dans le jar de l'application.
Le ShapefileDataStore ou plus généralement les DataStore sont des classes qui vont nous permettre de lire et écrire nos données.
Le FeatureSource correspond au jeu de données une fois en mémoire, on pourra parcourir les éléments grâce à lui.

Je reviendrai plus avant sur les DataStore dans un prochain article.

StyleBuilder sb = new StyleBuilder();

PolygonSymbolizer ps = sb.createPolygonSymbolizer( new Color(253,241,187),new Color(163,151,97),1);
Style solstyle = sb.createStyle();
solstyle.addFeatureTypeStyle(sb.createFeatureTypeStyle(ps));

Maintenant que nous avons chargé nos données vectorielles, il faut leur attribuer un style, autrement la façon dont on visualisera les géométries sur la carte. Ici on crée une symbologie pour un polygone avec sa couleur de fond, sa couleur de contour et l'épaisseur du contour.

Je reviendrai plus avant sur la Symbologie dans un prochain article.

maplayer = new DefaultMapLayer(source,solstyle);
maplayer.setTitle("occ_sol.shp");
maplayer.setVisible(true);
maplayer.setQuery(Query.ALL);
mapcontext.addLayer(maplayer);

Les données et le style définis, on peut maintenant créer notre couche, lui donner un nom et la rendre visible, on termine en l'ajoutant au mapcontext. Le « maplayer.setQuery(Query.ALL); » signifie que l'on veut que toutes les données soient visibles, on pourrait très bien choisir de n'afficher que les données selon un certain attribut.

Je reviendrai plus avant sur le requêtage dans un prochain article.

shapeURL = Menu.class.getResource("/reseau_route.shp");

store = new ShapefileDataStore(shapeURL);
name = store.getTypeNames()[0];
source = store.getFeatureSource(name);

sb = new StyleBuilder();

LineSymbolizer ls2 = sb.createLineSymbolizer(Color.RED, 1);
Style roadsStyle = sb.createStyle();
roadsStyle.addFeatureTypeStyle(sb.createFeatureTypeStyle(ls2));

maplayer = new DefaultMapLayer(source,roadsStyle);
maplayer.setTitle("reseau_route.shp");
maplayer.setVisible(true);
maplayer.setQuery(Query.ALL);
mapcontext.addLayer(maplayer);

Même chose que précédemment, on charge une nouvelle couche de données.

StreamingRenderer render = new StreamingRenderer();

mappane.setRenderer(render);
mappane.setMapArea(mapcontext.getLayerBounds());

} catch(Exception e){
e.printStackTrace();
}

return mappane;
}

Le StreamingRenderer est un objet qui définit divers paramètres pour le rendu graphique des données, ignorez cela pour le moment.
On définit ensuite le MapArea (la zone visible de la carte) avec le rectangle englobant de nos données.

II-C - Le contrôle

private JPanel buildTool() {
JPanel outil = new JPanel(new FlowLayout(FlowLayout.LEFT));

JButton plus = new JButton("+");
plus.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
mappane.setState(JMapPane.ZoomIn);
}
});
outil.add(plus);

JButton moins = new JButton("-");
moins.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
mappane.setState(JMapPane.ZoomOut);
}
});
outil.add(moins);

JButton pan = new JButton("Pan");
pan.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
mappane.setState(JMapPane.Pan);
}
});
outil.add(pan);

return outil;
}

Un seul élément à noter ici :
- mappane.setState(JMapPane.ZoomIn);
- mappane.setState(JMapPane.ZoomOut);
- mappane.setState(JMapPane.Pan);
Cela va changer le comportement de la navigation sur la carte quand on cliquera avec la souris.

II-D - Le Résultat

Si tout s'est bien passé vous devriez obtenir ceci :

Voilà déjà un début intéressant.

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, quelle que 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.

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 davantage d'informations. Voici un exemple pour une source de type PostGIS.

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

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.

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.

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.

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

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 à l'avenir cette façon de procéder devrait peut-être changer.

III-B-3 - Modifier

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 chapitre sur le requêtage.
7) Et on applique nos modifications, ainsi qu'un commit(). Vous remarquerez le rollback() comme en SQL .

III-B-4 - Ajouter

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 multiligne, 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-5 - Supprimer

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 suppression.

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.

Utilisation :

//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és.
List<MapLayer> layers = jdc.getLayers();
}

III-D - FAQ

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

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 connaitre le type de géométrie ?

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 ?

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");
}