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=6http://www.developpez.net/forums/forumdisplay.php?f=6
Forum SIG :
forumsigforumsig
georezogeorezo
I-D. Remerciements▲
Vincent Heurteaux : Directeur/cofondateur de Geomatys.
II. Première carte▲
II-A. Le décor▲
Commençons par récupérer les librairies qui nous seront nécessaires.
GeoTools : http://geotools.codehaus.org/Downloadshttp://geotools.codehaus.org/Downloads
JAI : https://jai.dev.java.net/binary-builds.htmlhttps://jai.dev.java.net/binary-builds.html
Et deux fichiers de données : shapefilesshapefiles
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.pdfhttp://www.cartographie.ird.fr/publi/documents/sig1.pdf
http://fr.wikipedia.org/wiki/Système_d'information_géographiquehttp://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.pdfhttp://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▲
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"
);
}