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.spatial.topoprim;
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_LineString;
036:
037: /**
038: * Noeud topologique (orientation positive).
039: * <P>L'operation "CoBoundary" redefinie sur TP_Object renvoie ici un set de TP_DirectedEdge,
040: * oriente positivement pour les entrants, negativement pour les sortants.
041: * <P>L'operation "Boundary" sur TP_Object renvoie null.
042: * <P>Dans le modele, cette classe herite directement de TP_Primitive (double heritage TP_Primitive / TP_DirectedNode).
043: * Ceci n'a pas ete repris en java, mais l'heritage se retrouve par l'intermediaire de TP_DirectedTopo.
044: *
045: * A EXPLIQUER : la structure de graphe
046: *
047: * @author Thierry Badard, Arnaud Braun & Audrey Simon
048: * @version 1.0
049: *
050: */
051:
052: public class TP_Node extends TP_DirectedNode {
053:
054: /** Les 2 primitives orientees de this. */
055: // hesitation sur le fait : proxy[0] = this ou proxy[0] = new TP_DirectedNode(id) avec proxy[0].topo = this ?
056: protected TP_DirectedNode[] proxy;
057:
058: /////////////////////////////////////////////////////////////////////////////////////
059: // constructeur /////////////////////////////////////////////////////////////////////
060: /////////////////////////////////////////////////////////////////////////////////////
061: public TP_Node() {
062: orientation = +1;
063: proxy = new TP_DirectedNode[2];
064: proxy[0] = this ;
065: topo = this ;
066: proxy[1] = new TP_DirectedNode();
067: proxy[1].topo = this ;
068: proxy[1].orientation = -1;
069: }
070:
071: // redefinition pour affecter un bon id au proxy negatif
072: public void setId(int Id) {
073: super .setId(Id);
074: proxy[1].setId(-Id);
075: if (Id < 0)
076: System.out
077: .println("TP_Node::setId(id) : L'identifiant doit être positif");
078: }
079:
080: /////////////////////////////////////////////////////////////////////////////////////
081: // asTP_DirectedTopo() //////////////////////////////////////////////////////////////
082: /////////////////////////////////////////////////////////////////////////////////////
083: /** Renvoie le TP_DirectedNode d'orientation "sign". "sign" doit valoir +1 ou -1, sinon renvoie null.*/
084: public TP_DirectedNode asTP_DirectedTopo(int sign) {
085: if (sign == +1)
086: return proxy[0];
087: else if (sign == -1)
088: return proxy[1];
089: else {
090: System.out
091: .println("TP_Node::asTP_DirectedTopo(sign) : Passer +1 ou -1 en paramètre.");
092: return null;
093: }
094: }
095:
096: /////////////////////////////////////////////////////////////////////////////////////
097: // container ////////////////////////////////////////////////////////////////////////
098: /////////////////////////////////////////////////////////////////////////////////////
099: /** Face dans laquelle est incluse this, pour les noeuds isoles. */
100: public TP_Face container;
101:
102: public TP_Face getContainer() {
103: return container;
104: };
105:
106: public void setContainer(TP_Face Container) {
107: if (Container.getId() == -1)
108: container = null;
109: else if (Container != null) {
110: this .container = Container;
111: this .containerID = Container.getId();
112: if (!Container.getIsolated().contains(this ))
113: Container.addIsolated(this );
114: } else {
115: container = null;
116: containerID = 0;
117: }
118: }
119:
120: // pour le mapping avec OJB
121: // on affecte -1 pour avoir une valeur par defaut non nulle
122: // il faut un objet topo avec un id = -1 (TP_Face) dans la table TP_Object;
123: // cela accelere le chargement
124: public int containerID = -1;
125:
126: public int getContainerID() {
127: return containerID;
128: }
129:
130: public void setContainerID(int ContainerID) {
131: containerID = ContainerID;
132: }
133:
134: /////////////////////////////////////////////////////////////////////////////////////
135: // coBoundary ///////////////////////////////////////////////////////////////////////
136: /////////////////////////////////////////////////////////////////////////////////////
137: /** Renvoie les TP_DirectedEdge qui ont self pour frontiere,
138: * orientes positivement pour les entrants, negativement pour les sortants.
139: * La liste est ordonnee dans le sens trigo.*/
140: public List coBoundary() {
141: List result = new ArrayList();
142: Iterator i;
143: i = entrant.iterator();
144: while (i.hasNext()) {
145: TP_Edge edge = (TP_Edge) i.next();
146: result.add(edge.asTP_DirectedTopo(+1));
147: }
148: i = sortant.iterator();
149: while (i.hasNext()) {
150: TP_Edge edge = (TP_Edge) i.next();
151: result.add(edge.asTP_DirectedTopo(-1));
152: }
153: if (result.size() > 1)
154: ordonne(result);
155:
156: return result;
157: }
158:
159: /** Les TP_Edge entrants dans ce noeud. */
160: // c'est en collection et pas en liste pour permettre le lazy loading Castor
161: public Collection entrant = new ArrayList();
162:
163: public Collection getEntrant() {
164: return entrant;
165: };
166:
167: public void addEntrant(TP_Edge edge) {
168: if (edge != null) {
169: entrant.add(edge);
170: if (edge.getEndnode() != this )
171: edge.setEndnode(this );
172: }
173: }
174:
175: /** Les TP_Edge sortants dans ce noeud. */
176: public Collection sortant = new ArrayList();
177:
178: public Collection getSortant() {
179: return sortant;
180: }
181:
182: public void addSortant(TP_Edge edge) {
183: if (edge != null) {
184: sortant.add(edge);
185: if (edge.getStartnode() != this )
186: edge.setStartnode(this );
187: }
188: }
189:
190: /////////////////////////////////////////////////////////////////////////////////////
191: // boundary /////////////////////////////////////////////////////////////////////////
192: /////////////////////////////////////////////////////////////////////////////////////
193: /** Renvoie null. */
194: public TP_Boundary boundary() {
195: return null;
196: }
197:
198: /////////////////////////////////////////////////////////////////////////////////////
199: // methodes privees pour ordonner la coboundary /////////////////////////////////////
200: /////////////////////////////////////////////////////////////////////////////////////
201: /* ordonne les edges dans le sens trigo (ordonne l'angle des 2 premiers points de la geometrie dans le sens croissant)*/
202: private void ordonne(List theDirEdges) {
203: double[] listOfAngles = new double[theDirEdges.size()];
204: for (int i = 0; i < theDirEdges.size(); i++) {
205: TP_DirectedEdge diredge = (TP_DirectedEdge) theDirEdges
206: .get(i);
207: // attention a l'orientation de la geometrie si on a un directed edge negatif
208: // directed negatif => sortant : OK
209: // directed positif => entrant : il faut le retourner
210: GM_LineString geom = null;
211: if (diredge.getId() <= 0)
212: geom = (GM_LineString) diredge.topo().getGeom();
213: else
214: geom = (GM_LineString) ((GM_LineString) (diredge.topo()
215: .getGeom())).reverse();
216: listOfAngles[i] = calculeAngle(geom);
217: }
218:
219: // on ordonne la liste en fonction des valeurs des angles
220: for (int i = 1; i < listOfAngles.length; i++) {
221: double angle = listOfAngles[i];
222: for (int j = 0; j < i; j++)
223: if (angle < listOfAngles[j]) {
224: // on decale dans la liste des dir edges
225: theDirEdges.add(j, theDirEdges.get(i));
226: theDirEdges.remove(i + 1);
227: // on decale dans la liste des angles
228: for (int k = i; k > j; k--)
229: listOfAngles[k] = listOfAngles[k - 1];
230: listOfAngles[j] = angle;
231: break;
232: }
233: }
234: }
235:
236: /* calcule l'angle forme par les 2 PREMIERS points de la ligne (dans [0, 2.pi]*/
237: private double calculeAngle(GM_LineString line) {
238: DirectPosition pt1 = line.getControlPoint().get(0);
239: DirectPosition pt2 = line.getControlPoint().get(1);
240: double deltaX = pt2.getX() - pt1.getX();
241: double deltaY = pt2.getY() - pt1.getY();
242: if (deltaX > 0 && deltaY >= 0)
243: return Math.atan(deltaY / deltaX);
244: else if (deltaX < 0 && deltaY >= 0)
245: return (Math.atan(deltaY / deltaX) + Math.PI);
246: else if (deltaX < 0 && deltaY <= 0)
247: return (Math.atan(deltaY / deltaX) + Math.PI);
248: else if (deltaX > 0 && deltaY <= 0)
249: return (Math.atan(deltaY / deltaX) + 2. * Math.PI);
250: else if (deltaX == 0 && deltaY >= 0)
251: return (Math.PI / 2.);
252: else
253: /*if (deltaX==0 && deltaY<=0)*/return (Math.PI / (-2.));
254: }
255:
256: }
|