001: /*
002: * This file is part of the GeOxygene project source files.
003: *
004: * GeOxygene aims at providing an open framework which implements OGC/ISO specifications for
005: * the development and deployment of geographic (GIS) applications. It is a open source
006: * contribution of the COGIT laboratory at the Institut Géographique National (the French
007: * National Mapping Agency).
008: *
009: * See: http://oxygene-project.sourceforge.net
010: *
011: * Copyright (C) 2005 Institut Géographique National
012: *
013: * This library is free software; you can redistribute it and/or modify it under the terms
014: * of the GNU Lesser General Public License as published by the Free Software Foundation;
015: * either version 2.1 of the License, or any later version.
016: *
017: * This library is distributed in the hope that it will be useful, but WITHOUT ANY
018: * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
019: * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
020: *
021: * You should have received a copy of the GNU Lesser General Public License along with
022: * this library (see file LICENSE if present); if not, write to the Free Software
023: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024: *
025: */
026:
027: package fr.ign.cogit.geoxygene.feature;
028:
029: import java.sql.Time;
030: import java.util.ArrayList;
031: import java.util.Collection;
032: import java.util.HashSet;
033: import java.util.Iterator;
034: import java.util.Set;
035:
036: import fr.ign.cogit.geoxygene.spatial.coordgeom.GM_Envelope;
037: import fr.ign.cogit.geoxygene.spatial.geomroot.GM_Object;
038: import fr.ign.cogit.geoxygene.util.index.Tiling;
039:
040: /**
041: * Une population représente TOUS les objets d'une classe héritant de FT_Feature.
042: *
043: * <P> Les objets qui la composent peuvent avoir une géometrie ou non.
044: * La population peut être persistante ou non, associée à un index spatial ou non.
045: *
046: * <P> NB: une population existe indépendamment des ses éléments.
047: * Avant de charger ses élements, la population existe mais ne contient aucun élément.
048: *
049: * <P> Difference avec FT_FeatureCollection :
050: * une Population est une FT_FeatureCollection possedant les proprietes suivantes.
051: * <UL>
052: * <LI> Lien vers DataSet. </LI>
053: * <LI> Une population peut-etre persistante et exister independamment de ses elements. </LI>
054: * <LI> Une population contient TOUS les elements de la classe. </LI>
055: * <LI> Un element ne peut appartenir qu'a une seule population (mais a plusieurs FT_FeatureCollection). </LI>
056: * <LI> Permet de gerer la persistence des elements de maniere efficace (via chargeElement(), nouvelElement(), etc.) </LI>
057: * <LI> Possede quelques attributs (nom classe, etc.). </LI>
058: * </UL>
059: *
060: * @author Sébastien Mustière
061: * @version 1.1
062: *
063: */
064:
065: public class Population extends FT_FeatureCollection {
066:
067: /** Identifiant. Correspond au "cogitID" des tables du SGBD.*/
068: protected int id;
069:
070: /** Renvoie l'identifiant. NB: l'ID n'est remplit automatiquement que si la population est persistante */
071: public int getId() {
072: return id;
073: }
074:
075: /** Affecte une valeur a l'identifiant */
076: public void setId(int I) {
077: id = I;
078: }
079:
080: ///////////////////////////////////////////////////////
081: // Constructeurs / Chargement / persistance
082: ///////////////////////////////////////////////////////
083: /** Constructeur par défaut. Sauf besoins particuliers, utiliser plutôt l'autre constructeur */
084: public Population() {
085: }
086:
087: /** Constructeur d'une population.
088: * Une population peut être persistante ou non (la population elle-même est alors rendue persistante dans ce constructeur).
089: * Une population a un nom logique (utile pour naviguer entre populations).
090: * Les élements d'une population se réalisent dans une classe contrète (classeElements).
091: * NB: lors la construction, auncun élément n'est affectée à la population, cela doit être fait
092: * à partir d'elements peristant avec chargeElements, ou a partir d'objets Java avec les setElements
093: */
094: public Population(boolean persistance, String nomLogique,
095: Class classeElements, boolean drapeauGeom) {
096: this .setPersistant(persistance);
097: this .setNom(nomLogique);
098: this .setNomClasse(classeElements.getName());
099: this .flagGeom = drapeauGeom;
100: if (persistance)
101: DataSet.db.makePersistent(this );
102: }
103:
104: /** Chargement des éléments persistants d'une population.
105: * Tous les éléments de la table correspondante sont chargés.
106: */
107: public void chargeElements() {
108: System.out.println("");
109: System.out
110: .println("-- Chargement des elements de la population "
111: + this .getNom());
112:
113: if (!this .getPersistant()) {
114: System.out
115: .println("----- ATTENTION : Aucune instance n'est chargee dans la population "
116: + this .getNom());
117: System.out
118: .println("----- La population n'est pas persistante");
119: return;
120: }
121:
122: try {
123: elements = DataSet.db.loadAllFeatures(classe).getElements();
124: } catch (Exception e) {
125: System.out
126: .println("----- ATTENTION : Chargement impossible de la population "
127: + this .getNom());
128: System.out
129: .println("----- Sans doute un probleme avec le SGBD, ou table inexistante, ou pas de mapping ");
130: e.printStackTrace();
131: return;
132: }
133:
134: System.out.println("-- " + this .size()
135: + " instances chargees dans la population");
136: }
137:
138: /** Chargement des éléments persistants d'une population qui intersectent une géométrie donnée.
139: * ATTENTION: la table qui stocke les éléments doit avoir été indexée dans le SGBD.
140: * ATTENTION AGAIN: seules les populations avec une géométrie sont chargées.
141: */
142: public void chargeElementsPartie(GM_Object geom) {
143: System.out.println("");
144: System.out
145: .println("-- Chargement des elements de la population "
146: + this .getNom());
147:
148: if (!this .getPersistant()) {
149: System.out
150: .println("----- ATTENTION : Aucune instance n'est chargee dans la population "
151: + this .getNom());
152: System.out
153: .println("----- La population n'est pas persistante");
154: return;
155: }
156:
157: if (!this .hasGeom()) {
158: System.out
159: .println("----- ATTENTION : Aucune instance n'est chargee dans la population "
160: + this .getNom());
161: System.out
162: .println("----- Les éléments de la population n'ont pas de géométrie");
163: return;
164: }
165:
166: try {
167: elements = DataSet.db.loadAllFeatures(this .getClasse(),
168: geom).getElements();
169: } catch (Exception e) {
170: System.out
171: .println("----- ATTENTION : Chargement impossible de la population "
172: + this .getNom());
173: System.out
174: .println("----- La classe n'est peut-être pas indexée dans le SGBD");
175: System.out
176: .println("----- ou table inexistante, ou pas de mapping ou probleme avec le SGBD ");
177: return;
178: }
179:
180: System.out.println(" " + this .size()
181: + " instances chargees dans la population");
182: }
183:
184: /** Chargement des éléments persistants d'une population.
185: * Tous les éléments de la table correspondante sont chargés.
186: * Les données doivent d'abord avoir été indexées.
187: * PB: TRES LENT !!!!!!!
188: */
189: public void chargeElementsProches(Population pop, double dist) {
190: System.out.println("");
191: System.out
192: .println("-- Chargement des elements de la population "
193: + this .getNom());
194: System.out.println("-- à moins de " + dist
195: + " de ceux de la population " + pop.getNom());
196:
197: if (!this .getPersistant()) {
198: System.out
199: .println("----- ATTENTION : Aucune instance n'est chargee dans la population "
200: + this .getNom());
201: System.out
202: .println("----- La population n'est pas persistante");
203: return;
204: }
205:
206: try {
207: Iterator itPop = pop.getElements().iterator();
208: Collection selectionTotale = new HashSet();
209: while (itPop.hasNext()) {
210: FT_Feature objet = (FT_Feature) itPop.next();
211: FT_FeatureCollection selection = DataSet.db
212: .loadAllFeatures(classe, objet.getGeom(), dist);
213: selectionTotale.addAll(selection.getElements());
214: }
215: elements = new ArrayList(selectionTotale);
216: } catch (Exception e) {
217: System.out
218: .println("----- ATTENTION : Chargement impossible de la population "
219: + this .getNom());
220: System.out
221: .println("----- Sans doute un probleme avec le SGBD, ou table inexistante, ou pas de mapping ");
222: e.printStackTrace();
223: return;
224: }
225:
226: System.out.println("-- " + this .size()
227: + " instances chargees dans la population");
228: }
229:
230: /** Renvoie une population avec tous les éléments de this
231: * situés à moins de "dist" des éléments de la population
232: * Travail sur un index en mémoire (pas celui du SGBD).
233: * Rmq : Fonctionne avec des objets de géométrie quelconque
234: */
235: public Population selectionElementsProchesGenerale(Population pop,
236: double dist) {
237: Population popTemporaire = new Population();
238: Population popResultat = new Population(false, this .getNom(),
239: this .getClasse(), true);
240: Set selectionUnObjet, selectionTotale = new HashSet();
241:
242: popTemporaire.addCollection(this );
243: popTemporaire.initSpatialIndex(Tiling.class, true, 20);
244: System.out.println("Fin indexation "
245: + (new Time(System.currentTimeMillis())).toString());
246: Iterator itPop = pop.getElements().iterator();
247: while (itPop.hasNext()) {
248: FT_Feature objet = (FT_Feature) itPop.next();
249: GM_Envelope enveloppe = objet.getGeom().envelope();
250: double xmin = enveloppe.getLowerCorner().getX() - dist;
251: double xmax = enveloppe.getUpperCorner().getX() + dist;
252: double ymin = enveloppe.getLowerCorner().getY() - dist;
253: double ymax = enveloppe.getUpperCorner().getY() + dist;
254: enveloppe = new GM_Envelope(xmin, xmax, ymin, ymax);
255: FT_FeatureCollection selection = popTemporaire
256: .select(enveloppe);
257: Iterator itSel = selection.getElements().iterator();
258: selectionUnObjet = new HashSet();
259: while (itSel.hasNext()) {
260: FT_Feature objetSel = (FT_Feature) itSel.next();
261: //if (Distances.premiereComposanteHausdorff((GM_LineString)objetSel.getGeom(),(GM_LineString)objet.getGeom())<dist)
262: if (objetSel.getGeom().distance(objet.getGeom()) < dist)
263: selectionUnObjet.add(objetSel);
264: }
265: popTemporaire.getElements().removeAll(selectionUnObjet);
266: selectionTotale.addAll(selectionUnObjet);
267: }
268: popResultat.setElements(new ArrayList(selectionTotale));
269: return popResultat;
270: }
271:
272: /** Renvoie une population avec tous les éléments de this
273: * situés à moins de "dist" des éléments de la population pop.
274: */
275: public Population selectionLargeElementsProches(Population pop,
276: double dist) {
277: Population popTemporaire = new Population();
278: Population popResultat = new Population(false, this .getNom(),
279: this .getClasse(), true);
280:
281: popTemporaire.addCollection(this );
282: popTemporaire.initSpatialIndex(Tiling.class, true);
283: Iterator itPop = pop.getElements().iterator();
284: while (itPop.hasNext()) {
285: FT_Feature objet = (FT_Feature) itPop.next();
286: GM_Envelope enveloppe = objet.getGeom().envelope();
287: double xmin = enveloppe.getLowerCorner().getX() - dist;
288: double xmax = enveloppe.getUpperCorner().getX() + dist;
289: double ymin = enveloppe.getLowerCorner().getY() - dist;
290: double ymax = enveloppe.getUpperCorner().getY() + dist;
291: enveloppe = new GM_Envelope(xmin, xmax, ymin, ymax);
292: FT_FeatureCollection selection = popTemporaire
293: .select(enveloppe);
294: popTemporaire.getElements().removeAll(
295: selection.getElements());
296: popResultat.addCollection(selection);
297: }
298: return popResultat;
299: }
300:
301: /** Chargement des éléments persistants d'une population qui intersectent une zone d'extraction donnée.
302: * ATTENTION: la table qui stocke les éléments doit avoir été indexée dans le SGBD.
303: * ATTENTION AGAIN: seules les populations avec une géométrie sont chargées.
304: */
305: public void chargeElementsPartie(Extraction zoneExtraction) {
306: chargeElementsPartie(zoneExtraction.getGeom());
307: }
308:
309: /** Detruit la population si elle est persistante,
310: * MAIS ne détruit pas les éléments de cette population (pour cela vider la table correspondante dans le SGBD).
311: */
312: public void detruitPopulation() {
313: if (!this .getPersistant())
314: return;
315: System.out.println("Destruction de la population des "
316: + this .getNom());
317: DataSet.db.deletePersistent(this );
318: }
319:
320: ///////////////////////////////////////////////////////
321: // Attributs décrivant la population
322: ///////////////////////////////////////////////////////
323: /** Nom logique des éléments de la population.
324: * La seule contrainte est de ne pas dépasser 255 caractères, les accents et espaces sont autorisés.
325: * A priori, on met le nom des éléments au singulier.
326: * Exemple: "Tronçon de route"
327: */
328: protected String nom;
329:
330: public String getNom() {
331: return nom;
332: }
333:
334: public void setNom(String S) {
335: nom = S;
336: }
337:
338: /** Classe par défaut des instances de la population.
339: * Ceci est utile pour pouvoir savoir dans quelle classe créer de nouvelles instances.
340: */
341: protected Class classe;
342:
343: public Class getClasse() {
344: return classe;
345: }
346:
347: public void setClasse(Class C) {
348: classe = C;
349: this .nomClasse = classe.getName();
350: }
351:
352: /** Nom complet (package+classe java) de la classe par défaut des instances de la population.
353: * Pertinent uniquement pour les population peristantes.
354: */
355: protected String nomClasse;
356:
357: /** Récupère le nom complet (package+classe java) de la classe par défaut des instances de la population.
358: * Pertinent uniquement pour les population peristantes.
359: */
360: public String getNomClasse() {
361: return nomClasse;
362: }
363:
364: /** Définit le nom complet (package+classe java) de la classe par défaut des instances de la population.
365: * CONSEIL : ne pas utiliser cette méthode directement, remplir en utilisant setClasse().
366: * Met également à jour l'attribut classe.
367: * Utile uniquement pour les population peristantes.
368: */
369: public void setNomClasse(String S) {
370: nomClasse = S;
371: try {
372: this .classe = Class.forName(nomClasse);
373: } catch (Exception e) {
374: System.out.println("----- ATTENTION : Nom de classe #"
375: + nomClasse + "# non valide");
376: System.out
377: .println("----- Causes possibles : la classe n'existe pas ou n'est pas compilee");
378: }
379: }
380:
381: /** Booléen spécifiant si la population est persistente ou non (vrai par défaut). */
382: // NB pour dévelopeurs : laisser 'true' par défaut.
383: // Sinon, cela pose des problèmes au chargement (un thème persistant chargé a son attribut persistant à false).
384: protected boolean persistant = true;
385:
386: /** Booléen spécifiant si la population est persistente ou non (vrai par défaut). */
387: public boolean getPersistant() {
388: return persistant;
389: }
390:
391: /** Booléen spécifiant si la population est persistente ou non (vrai par défaut). */
392: public void setPersistant(boolean b) {
393: persistant = b;
394: }
395:
396: ///////////////////////////////////////////////////////
397: // Relations avec les thèmes et les étéments
398: ///////////////////////////////////////////////////////
399: /** DataSet auquel apparient la population (une population appartient à un seul DataSet). */
400: protected DataSet dataSet;
401:
402: /** Récupère le DataSet de la population. */
403: public DataSet getDataSet() {
404: return dataSet;
405: }
406:
407: /** Définit le DataSet de la population, et met à jour la relation inverse. */
408: public void setDataSet(DataSet O) {
409: DataSet old = dataSet;
410: dataSet = O;
411: if (old != null)
412: old.getPopulations().remove(this );
413: if (O != null) {
414: dataSetID = O.getId();
415: if (!(O.getPopulations().contains(this )))
416: O.getPopulations().add(this );
417: } else
418: dataSetID = 0;
419: }
420:
421: private int dataSetID;
422:
423: /** Ne pas utiliser, necessaire au mapping OJB */
424: public void setDataSetID(int I) {
425: dataSetID = I;
426: }
427:
428: /** Ne pas utiliser, necessaire au mapping OJB */
429: public int getDataSetID() {
430: return dataSetID;
431: }
432:
433: //////////////////////////////////////////////////
434: // Methodes surchargeant des trucs de FT_FeatureCollection, avec une gestion de la persistance
435:
436: /** Enlève, ET DETRUIT si il est persistant, un élément de la liste des elements de la population,
437: * met également à jour la relation inverse, et eventuellement l'index.
438: * NB : différent de remove (hérité de FT_FeatureCollection) qui ne détruit pas l'élément.
439: */
440: public void enleveElement(FT_Feature O) {
441: super .remove(O);
442: if (this .getPersistant())
443: DataSet.db.deletePersistent(O);
444: }
445:
446: /** Crée un nouvel élément de la population, instance de sa classe par défaut, et l'ajoute à la population.
447: * Si la population est persistante, alors le nouvel élément est rendu persistant dans cette méthode
448: * NB : différent de add (hérité de FT_FeatureCollection) qui ajoute un élément déjà existant.
449: */
450: public FT_Feature nouvelElement() {
451: try {
452: FT_Feature elem = (FT_Feature) this .getClasse()
453: .newInstance();
454: //elem.setPopulation(this);
455: super .add(elem);
456: if (this .getPersistant())
457: DataSet.db.makePersistent(elem);
458: return elem;
459: } catch (Exception e) {
460: System.out
461: .println("ATTENTION : Problème à la création d'un élément de la population "
462: + this .getNom());
463: System.out
464: .println(" Soit la classe des éléments est non valide : "
465: + this .getNomClasse());
466: System.out
467: .println(" Causes possibles : la classe n'existe pas? n'est pas compilée? est abstraite?");
468: System.out
469: .println(" Soit problème à la mise à jour de l'index ");
470: System.out
471: .println(" Causes possibles : mise à jour automatique de l'index, mais l'objet n'a pas encore de géoémtrie");
472: return null;
473: }
474: }
475:
476: /** Crée un nouvel élément de la population (avec la géoémtrie geom),
477: * instance de sa classe par défaut, et l'ajoute à la population.
478: *
479: * Si la population est persistante, alors le nouvel élément est rendu persistant dans cette méthode
480: * NB : différent de add (hérité de FT_FeatureCollection) qui ajoute un élément déjà existant.
481: */
482: public FT_Feature nouvelElement(GM_Object geom) {
483: try {
484: FT_Feature elem = (FT_Feature) this .getClasse()
485: .newInstance();
486: elem.setGeom(geom);
487: //elem.setPopulation(this);
488: super .add(elem);
489: if (this .getPersistant())
490: DataSet.db.makePersistent(elem);
491: return elem;
492: } catch (Exception e) {
493: System.out
494: .println("ATTENTION : Problème à la création d'un élément de la population "
495: + this .getNom());
496: System.out
497: .println(" Soit la classe des éléments est non valide : "
498: + this .getNomClasse());
499: System.out
500: .println(" Causes possibles : la classe n'existe pas? n'est pas compilée? est abstraite?");
501: System.out
502: .println(" Soit problème à la mise à jour de l'index ");
503: System.out
504: .println(" Causes possibles : mise à jour automatique de l'index, mais l'objet n'a pas encore de géoémtrie");
505: return null;
506: }
507: }
508:
509: /** Crée un nouvel élément de la population, instance de sa classe par défaut, et l'ajoute à la population.
510: * La création est effectuée à l'aide du constructeur spécifié par les tableaux signature(classe des
511: * objets du constructeur), et param (objets eux-mêmes).
512: * Si la population est persistante, alors le nouvel élément est rendu persistant dans cette méthode
513: * NB : différent de add (hérité de FT_FeatureCollection) qui ajoute un élément déjà existant.
514: */
515:
516: public FT_Feature nouvelElement(Class[] signature, Object[] param) {
517: try {
518: FT_Feature elem = (FT_Feature) this .getClasse()
519: .getConstructor(signature).newInstance(param);
520: super .add(elem);
521: if (this .getPersistant())
522: DataSet.db.makePersistent(elem);
523: return elem;
524: } catch (Exception e) {
525: System.out
526: .println("ATTENTION : Problème à la création d'un élément de la population "
527: + this .getNom());
528: System.out
529: .println(" Classe des éléments non valide : "
530: + this .getNomClasse());
531: System.out
532: .println(" Causes possibles : la classe n'existe pas? n'est pas compilée?");
533: return null;
534: }
535: }
536:
537: //////////////////////////////////////////////////
538: // Copie de population
539: /** Copie la population passée en argument dans la population traitée (this)
540: * NB: 1/ ne copie pas l'eventuelle indexation spatiale,
541: * 2/ n'affecte pas la population au DataSet de la population à copier.
542: * 3/ mais recopie les autres infos: élements, classe, FlagGeom, Nom et NomClasse
543: */
544: public void copiePopulation(Population populationACopier) {
545: this.setElements(populationACopier.getElements());
546: this.setClasse(populationACopier.getClasse());
547: this.setFlagGeom(populationACopier.getFlagGeom());
548: this.setNom(populationACopier.getNom());
549: this.setNomClasse(populationACopier.getNomClasse());
550: }
551:
552: }
|