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.util.ArrayList;
030: import java.util.Collection;
031: import java.util.Iterator;
032: import java.util.List;
033:
034: import fr.ign.cogit.geoxygene.spatial.coordgeom.DirectPosition;
035: import fr.ign.cogit.geoxygene.spatial.coordgeom.GM_Envelope;
036: import fr.ign.cogit.geoxygene.spatial.geomaggr.GM_Aggregate;
037: import fr.ign.cogit.geoxygene.spatial.geomroot.GM_Object;
038: import fr.ign.cogit.geoxygene.util.index.SpatialIndex;
039:
040: /**
041: * Collection (liste) de FT_Feature. Peut porter un index spatial.
042: *
043: * @author Thierry Badard & Arnaud Braun
044: * @version 1.1
045: *
046: * 19.02.2007 : ajout des méthodes contains, addUnique, remove, removeCollection,
047: * addUniqueCollection, iterator
048: *
049: */
050:
051: public class FT_FeatureCollection {
052:
053: // constructeurs
054: public FT_FeatureCollection() {
055: }
056:
057: /** constructeur recopiant une autre collection.
058: * ATTENTION: ne recopie pas l'éventuel index spatial*/
059: public FT_FeatureCollection(FT_FeatureCollection listeACopier) {
060: this .setFlagGeom(listeACopier.getFlagGeom());
061: this .setFlagTopo(listeACopier.flagTopo);
062: this .getElements().addAll(listeACopier.getElements());
063: }
064:
065: /** Constructeur à partir d'une collection de FT_Feature */
066: public FT_FeatureCollection(Collection col) {
067: this .getElements().addAll(col);
068: }
069:
070: // ---------------------------------------
071: // --- Indicateurs de geometrie et topo --
072: // ---------------------------------------
073:
074: /** Boolean indiquant si les FT_Feature portent une geometrie (true par defaut). */
075: protected boolean flagGeom = true;
076:
077: /** Boolean indiquant si les FT_Feature portent une geometrie. */
078: public boolean getFlagGeom() {
079: return flagGeom;
080: }
081:
082: /** Boolean indiquant si les FT_Feature portent une geometrie. */
083: public boolean hasGeom() {
084: return flagGeom;
085: }
086:
087: /** Boolean indiquant si les FT_Feature portent une geometrie. */
088: public void setFlagGeom(boolean Geom) {
089: flagGeom = Geom;
090: }
091:
092: /** Boolean indiquant si les FT_Feature portent une topologie (false par defaut). */
093: protected boolean flagTopo = false;
094:
095: /** Boolean indiquant si les FT_Feature portent une topologie. */
096: public boolean hasTopo() {
097: return flagTopo;
098: }
099:
100: /** Boolean indiquant si les FT_Feature portent une topologie. */
101: public void setFlagTopo(boolean Topo) {
102: flagTopo = Topo;
103: }
104:
105: // ---------------------------------------
106: // --- Accesseurs ------------------------
107: // ---------------------------------------
108:
109: /** La liste des FT_Feature composant this. */
110: protected List elements = new ArrayList();
111:
112: /** Iterateur sur la liste des FT_Feature composant this. */
113: private Iterator iElements;
114:
115: /** Renvoie la liste de FT_Feature composant this. */
116: public List getElements() {
117: return this .elements;
118: }
119:
120: /** Affecte une liste de FT_Feature à this, et met à jour le lien inverse.
121: * Attention detruit l'index spatial si celui existait.
122: * Il faut donc le reinitialiser si on souhaite l'utiliser.*/
123: public void setElements(List L) {
124: List old = new ArrayList(elements);
125: Iterator it1 = old.iterator();
126: while (it1.hasNext()) {
127: FT_Feature O = (FT_Feature) it1.next();
128: elements.remove(O);
129: O.getFeatureCollections().remove(this );
130: }
131: Iterator it2 = L.iterator();
132: while (it2.hasNext()) {
133: FT_Feature O = (FT_Feature) it2.next();
134: elements.add(O);
135: if (!O.getFeatureCollections().contains(this ))
136: O.getFeatureCollections().add(this );
137: }
138: if (isIndexed)
139: removeSpatialIndex();
140: }
141:
142: /** Renvoie le i-eme element de la liste des composants de this. */
143: public FT_Feature get(int i) {
144: return (FT_Feature) this .elements.get(i);
145: }
146:
147: /** Ajoute un element a la liste des composants de this, et met à jour le lien inverse. */
148: public void add(FT_Feature value) {
149: if (value == null)
150: return;
151: this .elements.add(value);
152: value.getFeatureCollections().add(this );
153: if (isIndexed)
154: if (spatialindex.hasAutomaticUpdate())
155: spatialindex.update(value, +1);
156: }
157:
158: /** Ajoute les éléments d'une FT_FeatureCollection a la liste des composants de this, et met à jour le lien inverse. */
159: public void addCollection(FT_FeatureCollection value) {
160: FT_Feature elem;
161: if (value == null)
162: return;
163: Iterator iter = value.elements.iterator();
164: while (iter.hasNext()) {
165: elem = (FT_Feature) iter.next();
166: add(elem);
167: }
168: }
169:
170: /** Efface de la liste l'element passe en parametre.
171: * Attention, si l'élément est persistant, celui-ci n'est pas détruit, le faire après au besoin.
172: */
173: public void remove(FT_Feature value) {
174: if (value == null)
175: return;
176: this .elements.remove(value);
177: value.getFeatureCollections().remove(this );
178: if (isIndexed)
179: if (spatialindex.hasAutomaticUpdate())
180: spatialindex.update(value, -1);
181: }
182:
183: /** Efface de la liste tous les élements de la collection passée en paramètre.
184: * Attention, si l'élément est persistant, celui-ci n'est pas détruit, le faire après au besoin.
185: */
186: public void removeAll(Collection coll) {
187: if (coll == null)
188: return;
189: if (coll.size() == 0)
190: return;
191: Iterator itColl = coll.iterator();
192: while (itColl.hasNext()) {
193: FT_Feature objet = (FT_Feature) itColl.next();
194: this .remove(objet);
195: }
196: }
197:
198: /** Efface toute la liste.
199: * Detruit l'index spatial si celui existe. */
200: public void clear() {
201: Iterator it = elements.iterator();
202: while (it.hasNext()) {
203: FT_Feature O = (FT_Feature) it.next();
204: O.getFeatureCollections().remove(this );
205: }
206: this .elements.clear();
207: if (isIndexed)
208: removeSpatialIndex();
209: }
210:
211: /** Initialise l'iterateur de la liste. */
212: public void initIterator() {
213: iElements = this .elements.iterator();
214: }
215:
216: /** Renvoie true s'il reste des elements avec l'iterateur, false sinon. */
217: public boolean hasNext() {
218: if (iElements.hasNext())
219: return true;
220: else
221: return false;
222: }
223:
224: /** Renvoie le prochain element avec l'iterateur. */
225: public FT_Feature next() {
226: return (FT_Feature) iElements.next();
227: }
228:
229: /** Renvoie le nombre de elements */
230: public int size() {
231: return this .elements.size();
232: }
233:
234: // ---------------------------------------
235: // --- Calcul de l'emprise ---------------
236: // ---------------------------------------
237:
238: /** Calcul l'emprise rectangulaire des geometries de la collection. */
239: public GM_Envelope envelope() {
240: if (this .hasGeom())
241: return this .getGeomAggregate().envelope();
242: else {
243: System.out
244: .println("ATTENTION appel de envelope() sur une FT_FeatureCollection sans geometrie ! (renvoie null) ");
245: return null;
246: }
247: }
248:
249: // ---------------------------------------
250: // --- Utile ! ---------------------------
251: // ---------------------------------------
252:
253: /** Renvoie toutes les geometries sous la forme d'un GM_Aggregate. */
254: public GM_Aggregate getGeomAggregate() {
255: if (this .hasGeom()) {
256: GM_Aggregate aggr = new GM_Aggregate();
257: initIterator();
258: while (hasNext())
259: aggr.add(next().getGeom());
260: return aggr;
261: } else {
262: System.out
263: .println("ATTENTION appel de getGeom() sur une FT_FeatureCollection sans geometrie ! (renvoie null) ");
264: return null;
265: }
266: }
267:
268: // ---------------------------------------
269: // --- Index spatial ---------------------
270: // ---------------------------------------
271:
272: /** Index spatial. */
273: private SpatialIndex spatialindex;
274:
275: /** La collection possede-t-elle un index spatial ? */
276: private boolean isIndexed = false;
277:
278: /** Index spatial. */
279: public SpatialIndex getSpatialIndex() {
280: return spatialindex;
281: }
282:
283: /** La collection possede-t-elle un index spatial ? */
284: public boolean hasSpatialIndex() {
285: return isIndexed;
286: }
287:
288: /** Initialise un index spatial avec détermination automatique des paramètres.
289: * Le boolean indique si on souhaite une mise a jour automatique de l'index. */
290: public void initSpatialIndex(Class spatialIndexClass,
291: boolean automaticUpdate) {
292: if (!this .hasGeom()) {
293: System.out
294: .println("Attention initialisation d'index sur une liste ne portant pas de geometrie !");
295: return;
296: }
297: try {
298: spatialindex = (SpatialIndex) spatialIndexClass
299: .getConstructor(
300: new Class[] { FT_FeatureCollection.class,
301: Boolean.class }).newInstance(
302: new Object[] { this ,
303: new Boolean(automaticUpdate) });
304: isIndexed = true;
305: } catch (Exception e) {
306: System.out
307: .println("Probleme a l'initialisation de l'index spatial !");
308: e.printStackTrace();
309: }
310: }
311:
312: /** Initialise un index spatial avec un parametre entier (utilise pour le dallage).
313: * Le boolean indique si on souhaite une mise a jour automatique de l'index.*/
314: public void initSpatialIndex(Class spatialIndexClass,
315: boolean automaticUpdate, int i) {
316: if (!this .hasGeom()) {
317: System.out
318: .println("Attention initialisation d'index sur une liste ne portant pas de geometrie !");
319: return;
320: }
321: try {
322: spatialindex = (SpatialIndex) spatialIndexClass
323: .getConstructor(
324: new Class[] { FT_FeatureCollection.class,
325: Boolean.class, Integer.class })
326: .newInstance(
327: new Object[] { this ,
328: new Boolean(automaticUpdate),
329: new Integer(i) });
330: isIndexed = true;
331: } catch (Exception e) {
332: System.out
333: .println("Probleme a l'initialisation de l'index spatial !");
334: e.printStackTrace();
335: }
336: }
337:
338: /** Initialise un index spatial d'une collection de FT_Feature,
339: * en prenant pour paramètre les limites de la zone et un entier
340: * (pour le dallage, cet entier est le nombre en X et Y de cases souhaitées sur la zone).
341: *
342: * @param spatialIndexClass
343: * Nom de la classe d'index.
344: *
345: * @param automaticUpdate
346: * Spéciifie si l'index doit être mis à jour automatiquement
347: * quand on modifie les objets de fc.
348: *
349: * @param enveloppe
350: * Enveloppe décrivant les limites de l'index spatial.
351: * NB: Tout objet hors de ces limites ne sera pas traité lors des requêtes spatiales !!!!!
352: *
353: * @param i
354: * Nombre de dalles en X et en Y, du dallage.
355: */
356: public void initSpatialIndex(Class spatialIndexClass,
357: boolean automaticUpdate, GM_Envelope enveloppe, int i) {
358: if (!this .hasGeom()) {
359: System.out
360: .println("Attention initialisation d'index sur une liste ne portant pas de geometrie !");
361: return;
362: }
363: try {
364: spatialindex = (SpatialIndex) spatialIndexClass
365: .getConstructor(
366: new Class[] { FT_FeatureCollection.class,
367: Boolean.class, GM_Envelope.class,
368: Integer.class }).newInstance(
369: new Object[] { this ,
370: new Boolean(automaticUpdate),
371: enveloppe, new Integer(i) });
372: isIndexed = true;
373: } catch (Exception e) {
374: System.out
375: .println("Probleme a l'initialisation de l'index spatial !");
376: e.printStackTrace();
377: }
378: }
379:
380: /** Initialise un index spatial d'une collection de FT_Feature,
381: * en prenant pour paramètre ceux d'un index existant.
382: */
383: public void initSpatialIndex(SpatialIndex spIdx) {
384: //enlevé : Class spatialIndexClass,
385: if (!this .hasGeom()) {
386: System.out
387: .println("Attention initialisation d'index sur une liste ne portant pas de geometrie !");
388: return;
389: }
390: try {
391: // spatialindex = (SpatialIndex) spatialIndexClass.
392: spatialindex = (SpatialIndex) spIdx.getClass()
393: .getConstructor(
394: new Class[] { FT_FeatureCollection.class,
395: spIdx.getClass() }).newInstance(
396: new Object[] { this , spIdx });
397:
398: isIndexed = true;
399: } catch (Exception e) {
400: System.out
401: .println("Probleme a l'initialisation de l'index spatial !");
402: e.printStackTrace();
403: }
404: }
405:
406: /** Détruit l'index spatial. */
407: public void removeSpatialIndex() {
408: spatialindex = null;
409: isIndexed = false;
410: }
411:
412: // ---------------------------------------
413: // --- SELECTION AVEC L'Index spatial ----
414: // ---------------------------------------
415:
416: /** Selection dans le carre dont P est le centre, de cote D. */
417: public FT_FeatureCollection select(DirectPosition P, double D) {
418: if (!isIndexed) {
419: System.out
420: .println("select() sur FT_FeatureCollection : l'index spatial n'est pas initialise (renvoie null)");
421: return null;
422: }
423: return spatialindex.select(P, D);
424: }
425:
426: /** Selection dans un rectangle. */
427: public FT_FeatureCollection select(GM_Envelope env) {
428: if (!isIndexed) {
429: System.out
430: .println("select() sur FT_FeatureCollection : l'index spatial n'est pas initialise (renvoie null)");
431: return null;
432: }
433: return spatialindex.select(env);
434: }
435:
436: /** Selection des objets qui intersectent un objet geometrique quelconque. */
437: public FT_FeatureCollection select(GM_Object geometry) {
438: if (!isIndexed) {
439: System.out
440: .println("select() sur FT_FeatureCollection : l'index spatial n'est pas initialise (renvoie null)");
441: return null;
442: }
443: return spatialindex.select(geometry);
444: }
445:
446: /** Selection des objets qui croisent ou intersectent un objet geometrique quelconque.
447: *
448: * @param strictlyCrosses
449: * Si c'est TRUE : ne retient que les objets qui croisent (CROSS au sens JTS)
450: * Si c'est FALSE : ne retient que les objets qui intersectent (INTERSECT au sens JTS)
451: * Exemple : si 1 ligne touche "geometry" juste sur une extrémité,
452: * alors avec TRUE cela ne renvoie pas la ligne, avec FALSE cela la renvoie
453: */
454: public FT_FeatureCollection select(GM_Object geometry,
455: boolean strictlyCrosses) {
456: if (!isIndexed) {
457: System.out
458: .println("select() sur FT_FeatureCollection : l'index spatial n'est pas initialise (renvoie null)");
459: return null;
460: }
461: return spatialindex.select(geometry, strictlyCrosses);
462: }
463:
464: /** Selection a l'aide d'un objet geometrique quelconque et d'une distance. */
465: public FT_FeatureCollection select(GM_Object geometry,
466: double distance) {
467: if (!isIndexed) {
468: System.out
469: .println("select() sur FT_FeatureCollection : l'index spatial n'est pas initialise (renvoie null)");
470: return null;
471: }
472: return spatialindex.select(geometry, distance);
473:
474: }
475:
476: /** Encapsulation de la methode contains() avec typage */
477: public boolean contains(FT_Feature value) {
478: if (this .elements.contains(value))
479: return true;
480: else
481: return false;
482: }
483:
484: /** Ajoute un element a la liste des composants de this s'il n'est pas déjà présent, et
485: * met à jour le lien inverse. */
486: public void addUnique(FT_Feature value) {
487: if (value == null)
488: return;
489: if (this .elements.contains(value))
490: return;
491: this .elements.add(value);
492: value.getFeatureCollections().add(this );
493: if (isIndexed)
494: if (spatialindex.hasAutomaticUpdate())
495: spatialindex.update(value, +1);
496: }
497:
498: /** Efface de la liste l'element en position i.
499: * Attention, si l'élément est persistant, celui-ci n'est pas détruit, le faire après au besoin.
500: */
501: public void remove(int i) {
502: if (i > this .size())
503: return;
504: FT_Feature value = this .get(i);
505: this .elements.remove(value);
506: value.getFeatureCollections().remove(this );
507: if (isIndexed)
508: if (spatialindex.hasAutomaticUpdate())
509: spatialindex.update(value, -1);
510: }
511:
512: /** Efface de la liste la collection passée en parametre.
513: * Attention, si l'élément est persistant, celui-ci n'est pas détruit, le faire après au besoin.
514: */
515: public void removeCollection(FT_FeatureCollection value) {
516: FT_Feature elem;
517: if (value == null)
518: return;
519: Iterator iter = value.elements.iterator();
520: while (iter.hasNext()) {
521: elem = (FT_Feature) iter.next();
522: remove(elem);
523: }
524: }
525:
526: /** Ajoute les éléments d'une FT_FeatureCollection a la liste des composants de this, et met à jour le lien inverse. */
527: public void addUniqueCollection(FT_FeatureCollection value) {
528: FT_Feature elem;
529: if (value == null)
530: return;
531: Iterator iter = value.elements.iterator();
532: while (iter.hasNext()) {
533: elem = (FT_Feature) iter.next();
534: this .addUnique(elem);
535: }
536: }
537:
538: /** Iterateur
539: */
540: public Iterator iterator() {
541: return this.elements.iterator();
542: }
543:
544: }
|