3.2) Transfert et organisation de la nouvelle base de
données :
a) Programme de transfert en C :
Pour pouvoir utiliser la base de données avec le
programme qui allait être réalisé en Java, il fallait tout
d'abord trouver une nouvelle forme d'organisation de la base et la
transférer car la librairie C-Isam qui permettait de la gérer
jusqu'à maintenant ne peut être accéder qu'avec le langage
C. Afin d'effectuer ce transfert deux solutions ont été
examinées et après une étude approfondie c'est la
deuxième qui a finalement été choisie ce qui est
expliqué ciaprès.
La première solution envisagée fut d'utiliser
JNI (Java Native Interface) pour effectuer ce transfert. Le package JNI de Java
permet de coupler du code Java et du code C ou C++ en respectant cinq
étapes de programmation :
- codage de la partie du programme en Java (classes .java) :
déclarations des méthodes natives et compilation avec
l'exécutable "javac".
- génération d'un fichier *.h grâce à
l'exécutable "javah" qui crée l'en-tête du fichier C de
manière automatique.
- codage de la partie C : implémentation des
méthodes natives
- génération d'une librairie (*.so sous un
environnement UNIX et *.dll sous Windows) qui contient les fichiers *.h et
*.c
- exécution du programme avec la commande "java"
Ainsi il était possible de réutiliser les
fonctions déjà écrites en C pour consulter la base et
récupérer les données avec un programme Java qui se
chargerait de les organiser d'une nouvelle façon. Puis après
étude de cette solution et quelques essais, il s'est avéré
que les possibilités de ce système étaient assez
restreintes et qu'il était difficile de la mettre en place.
C'est pourquoi une deuxième solution pour
transférer la base de données a été
envisagée : le plus simple était de passer par un fichier
intermédiaire entre un programme C et un programme Java. Ainsi un
premier programme en C a été réalisé pour lire
l'ensemble des données stockées dans la base et les écrire
dans un fichier texte qui pourrait donc être relu de
n'importe quelle manière. Quatre fichiers textes ont
donc été construits par ce programme et représentent les 4
années de données. Ces fichiers étaient seulement
destinés à être relus une fois par un autre programme en
Java qui lui se chargerait de les organiser autrement.
Le programme de transfert en Java lit donc chaque fichier
texte de façon séquentielle et ordonne les données dans un
fichier binaire. On aurait pu conserver les fichiers textes obtenus à la
suite de l'exécution du programme de transfert en C, cependant ces
fichiers sont très volumineux puisqu'ils font chacun entre 200 et 300
Mo. Cela rendrait les recherches très longues puisque l'accès
à un fichier texte ne peut se faire que de manière
séquentielle. Il fallait alors trouver une autre façon de stocker
les données qui seraient exploitées par le programme de
visualisation en Java. Examiné
Après le transfert des données dans un fichier
texte intermédiaire, il a fallu trouver un moyen de stocker et de
structurer ces données qui seront utilisées par le programme en
Java.
b) Nouvelle organisation de la base :
Afin d'organiser la nouvelle base de données plusieurs
méthodes ont été considérées. La
première qui est présentée ici est la
sérialisation. Après quelques tests cette solution n'a finalement
pas été retenue au profit de la deuxième méthode
examinée détaillée ensuite.
· Etude d'une méthode : la
sérialisation :
Etant donné que Java est un langage orienté
objet et permet donc d'exploiter pleinement les avantages d'une structure avec
des classes et des objets, le plus simple était de relire chaque fichier
texte et de construire des objets à partir de ces données. Les
objets seraient ensuite enregistrés dans un fichier et pourraient
être réutilisés ultérieurement.
Pour réaliser ceci Java propose la
sérialisation d'objets. L'interface "Serializable" permet de stocker des
objets dans un fichier qui sont accédés séquentiellement.
Quand on sérialise un objet dans un fichier, toutes ses
dépendances et les objets avec lesquels il a des liens sont
également sérialisés. C'est pourquoi il était
intéressant de créer une classe "Base" qui regrouperait
l'ensemble des objets de la base de données et cet objet "Base" serait
seul sérialisé dans un fichier. Alors tous les autres objets
seraient également enregistrés dans le fichier et pourraient
être retrouvés par la suite.
Tableau de comparaison avantages / inconvénients pour la
sérialisation :
Avantages
|
Inconvénients
|
Permet la persistance des objets et des liens entre objets de
manière très simple.
|
Très long si l'on a beaucoup d'objets ou des objets de
grande taille.
|
Format indépendant du système d'exploitation,
donc possibilité d'utiliser les objets avec d'autres systèmes par
la suite.
|
|
Système applicable à tous les objets.
|
|
Enregistre tous les objets référencés par
l'objet sérialisé.
|
|
|
Cependant il existe un énorme inconvénient
à cette méthode : puisque la base de données est d'une
taille très importante, l'objet "Base" aurait fait plusieurs centaines
de Mo également et il fallait plusieurs heures pour le sérialiser
ou le désérialiser, car en effet on est obligé de lire
(désérialiser) l'objet en une seule fois et avec tous les objets
qui en dépendent. La sérialisation est donc très utile et
très pratique dans certains cas, en particulier quand on ne
possède pas d'importants objets à mémoriser mais pour ce
projet elle n'était pas du tout envisageable.
· Choix d'une autre méthode
:
Les autres méthodes pour stocker des données
dans des fichiers en Java sont les fichiers textes et les fichiers binaires
à accès séquentiel, ce qui aurait été
beaucoup trop long ici. Il restait donc une solution qui est l'utilisation de
fichiers binaires à accès direct ("RandomAccessFile" pour Java).
La classe RandomAccessFile permet de créer et de lire des fichiers
binaires à accès direct grâce à la fonction "seek"
qui place le "pointeur du fichier" où l'on veut dans le fichier.
Cependant il faut indiquer à cette fonction à
quel octet on veut aller dans le fichier et non pas en lui indiquant une
clé comme dans les fichiers indexés. Cela implique de
connaître l'endroit exact où est stockée une donnée
si on veut la retrouver. Le programme Java servant au transfert de la base
construit un fichier binaire pour les données de chaque année
(donc 4 saisons) et organise les données de manière
ordonnée. Ainsi il relit les données dans le fichier texte de
manière chronologique et les retranscrit dans le fichier binaire de la
façon suivante (Figure 6) :
Enregistrements
|
Champs
|
Total
|
Evénement1
|
jd md ad hd mind sd jf mf af hf minf sf nbim
|
15 octets
|
Image1
|
j m a h min s seco site reso nbrad ppirhi
|
19 octets
|
Radiale1 1
|
azi site p1 p2 p3 p4 ... p512
|
520 octets
|
Radiale12
|
azi site p1 p2 p3 p4 ... p512
|
520 octets
|
...
|
...
|
...
|
Radiale1Max
|
azi site p1 p2 p3 p4 ... p512
|
520 octets
|
Image2
|
j m a h min s seco site reso nbrad ppirhi
|
19 octets
|
Radiale21
|
azi site p1 p2 p3 p4 ... p512
|
520 octets
|
...
|
...
|
...
|
Evénement2
|
jd md ad hd mind sd jf mf af hf minf sf nbim
|
15 octets
|
...
|
...
|
...
|
|
Figure 6 : Structure du fichier binaire
Les enregistrements et les champs sont les suivants :
Evénement :
Champs :
- jd : jour de la date de début de
l'événement (byte : 1 octet)
- md : mois de début (byte : 1 octet)
- ad : année de début (short : 2 octets) - hd :
heure de début (byte : 1 octet) - mind : minute de début (1
octet) - sd : seconde de début (1 octet)
- jf : jour de la date de fin de l'événement (byte
: 1 octet)
- mf : mois de fin (1 octet)
- af : année de fin (short : 2 octets) - hf : heure de
fin (byte : 1 octet)
- minf : minute de fin (byte : 1 octet) - sf : seconde de fin
(byte : 1 octet)
- nbim : nombre d'images composant l'événement
(byte : 1 octet) -
Taille totale : 15 octets
Image :
Champs . ·
- j : jour de la date de l'image (byte : 1 octet)
- m : mois (byte : 1 octet)
- a : année (short : 2 octets) - h : heure (byte : 1
octet)
- min : minute (byte : 1 octet) - s : seconde (byte : 1
octet)
- seco : secteur couvert (2) (float : 4 octets)
- site : site (1) (float : 4 octets)
- reso : résolution de l'image (byte : 1 octet)
- nbrad : nombre de radiales composant l'image (short : 2
octets)
- ppirhi : indique si l'image est PPI ou RHI (1)
(byte : 1 octet)
Taille totale : 19 octets
Radiale :
Champs . ·
- azi : azimut (1) de la radiale (float : 4
octets)
- site : site (float : 4 octets)
- p1, p2... p512 : portes composant la radiale (512 portes par
radiale) : valeur en DBz (1) (byte : 1 octet)
Taille totale : 520 octets
(1) Cf. définition dans le Glossaire page 33
Pour réduire le plus possible la taille des fichiers
binaires il fallait bien choisir les types de chaque donnée pour que de
la place ne soit pas gaspillée. Les différents types primitifs
proposés par Java sont les suivants (Figure 7) :
Primitive
|
Signification
|
Taille
|
Valeurs acceptées
|
char
|
Caractère
|
2 octets
|
Valeur du jeu de caractères Unicode (65 000
caractères possibles).
|
byte
|
Entier très court
|
1 octet
|
-128 à 127
|
short
|
Entier court
|
2 octets
|
-32768 à 32767
|
int
|
Entier
|
4 octets
|
-2 147 483 648 à 2 147 483 647
|
long
|
Entier long
|
8 octets
|
-9223372036854775808 à 9223372036854775807
|
float
|
Flottant (réel)
|
4 octets
|
-1.4* 1 0-45 à 3.4* 1038
|
double
|
Flottant double
|
8 octets
|
4.9* 10-324 à 1.7* 1 0308
|
boolean
|
Booléen
|
1 octet
|
0 ou 1 (en réalité, toute autre valeur que 0 est
considérée égale à 1)
|
|
Figure 7 : Types primitifs en Java
Etant donné que la plupart des valeurs des
données que l'on a à stocker sont inférieures à 256
du fait de la conversion des données depuis le système SANAGA
(où elles étaient sur 8 bits), il était
préférable de privilégier l'utilisation du type byte pour
réduire la taille du fichier. Ainsi toutes les petites valeurs ne
dépassant pas 127 ont été enregistrées comme des
bytes. Le problème était que la plupart des valeurs
stockées dans la base et qui occupent la majeure partie des fichiers
sont les valeurs des portes de chaque radiale.
Or la valeur d'une porte peut varier entre 0 et 255, il n'est
donc pas possible de la coder sur un byte. Cependant toutes les valeurs
négatives n'étaient pas utilisées car une porte ne peut
pas avoir une réflectivité négative. C'est pourquoi le
système adopté pour coder les valeurs des portes a
été d'utiliser des bytes de la manière suivante :
- pour les portes de valeur comprise entre 0 et 127 : codage
normal sur un byte.
- pour les portes de valeur comprise entre 128 et 255 : on
soustrait 256 à la valeur initiale de façon à obtenir un
nombre négatif compris entre -128 et -1. Lors de la relecture du fichier
il faut donc rajouter 256 pour avoir la bonne valeur. Ce système permet
de gagner de la place en codant toutes les portes sur un byte, la taille des
fichiers est alors pratiquement divisée par 2, ce qui était
nécessaire pour qu'ils tiennent tous un CD-Rom.
Grâce à cette organisation on connaît la
taille de chaque donnée ce qui permet de calculer le nombre d'octets que
l'on veut sauter pour accéder à un certain champ. Par exemple, si
l'on recherche un événement dans la base pour une date
donnée, il faut consulter la date du 1 er événement puis
si ce n'est pas celui qu'on recherche, on peut se placer directement au
début de l'événement suivant.
Ainsi si l'on recherche un événement pour une
date donnée il faut faire la boucle
suivante (1) :
- ouverture du fichier et placement du pointeur en
début de fichier (position 0).
(1) Cf. Annexe 3 page 47 pour le détail du code
- lecture du 1er champ du 1er
événement (c'est-à-dire le jour).
Si le jour est identique à celui de la date
recherchée on lit les autres champs pour voir si c'est le bon
événement, si c'est effectivement le bon on
récupère les autres données relatives à
l'événement, sinon on reprend la boucle de recherche.
- Si le jour est différent on doit se placer au
début du prochain événement. Pour cela il faut
connaître le nombre d'octets (et donc d'images enregistrées)
à sauter. Le champ "nbim" de l'événement nous indique le
nombre d'images qui sont enregistrées pour celui-ci (et pour chaque
image le champ "nbrad" indique le nombre de radiales enregistré).
- Il faut donc pour accéder à
l'événement suivant se placer à l'octet de rang
égal à : octet n° = 15 + 19*nbim + 520*nbrad*nbim (sachant
que le nombre de radiales nbrad est différent pour chaque image).
L'accès n'est donc pas tout fait direct puisqu'il faut
faire un parcours séquentiel pour trouver l'événement
recherché. Cependant cet accès est très rapide puisqu'il
ne faut consulter qu'un seul champ de l'événement pour savoir si
c'est le bon, c'est-à-dire si la date correspond. Il en est exactement
de même si l'on recherche une image pour une date donnée.
Maintenant que la banque de données est
organisée dans de nouveaux fichiers lisibles par le programme en Java,
la suite de la programmation est la réalisation de l'interface graphique
du programme.
3.3) Interface graphique : architecture et
fonctionnalité
|