001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2004-2006, Geotools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.data.vpf.readers;
017:
018: import java.io.File;
019: import java.io.IOException;
020: import java.sql.SQLException;
021: import java.util.LinkedList;
022: import java.util.List;
023: import java.util.Vector;
024:
025: import org.geotools.data.vpf.VPFFeatureType;
026: import org.geotools.data.vpf.file.VPFFile;
027: import org.geotools.data.vpf.file.VPFFileFactory;
028: import org.geotools.data.vpf.ifc.FileConstants;
029: import org.geotools.data.vpf.io.TripletId;
030: import org.geotools.feature.Feature;
031: import org.geotools.feature.IllegalAttributeException;
032:
033: import com.vividsolutions.jts.geom.Coordinate;
034: import com.vividsolutions.jts.geom.GeometryFactory;
035: import com.vividsolutions.jts.geom.LineString;
036: import com.vividsolutions.jts.geom.LinearRing;
037: import com.vividsolutions.jts.geom.Polygon;
038:
039: /**
040: * Creates Geometries for area objects
041: *
042: * @author <a href="mailto:jeff@ionicenterprise.com">Jeff Yutzler</a>
043: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/vpf/src/main/java/org/geotools/data/vpf/readers/AreaGeometryFactory.java $
044: */
045: public class AreaGeometryFactory extends VPFGeometryFactory implements
046: FileConstants {
047: /* (non-Javadoc)
048: * @see com.ionicsoft.wfs.jdbc.geojdbc.module.vpf.VPFGeometryFactory#createGeometry(java.lang.String, int, int)
049: */
050: public void createGeometry(VPFFeatureType featureType,
051: Feature values) throws SQLException, IOException,
052: IllegalAttributeException {
053:
054: int tempEdgeId;
055: boolean isLeft = false;
056: Coordinate previousCoordinate = null;
057: Coordinate coordinate = null;
058: List coordinates = null;
059: Polygon result = null;
060: GeometryFactory geometryFactory = new GeometryFactory();
061: LinearRing outerRing = null;
062: List innerRings = new Vector();
063:
064: // Get face information
065: //TODO: turn these column names into constants
066: int faceId = Integer.parseInt(values.getAttribute("fac_id")
067: .toString());
068:
069: // Retrieve the tile directory
070: String baseDirectory = featureType.getFeatureClass()
071: .getDirectoryName();
072: String tileDirectory = baseDirectory;
073:
074: // If the primitive table is there, this coverage is not tiled
075: if (!new File(tileDirectory.concat(File.separator).concat(
076: FACE_PRIMITIVE)).exists()) {
077: Short tileId = new Short(Short.parseShort(values
078: .getAttribute("tile_id").toString()));
079: tileDirectory = tileDirectory.concat(File.separator)
080: .concat(
081: featureType.getFeatureClass().getCoverage()
082: .getLibrary().getTileMap().get(
083: tileId).toString()).trim();
084: }
085:
086: // all edges from this tile that use the face
087: String edgeTableName = tileDirectory.concat(File.separator)
088: .concat(EDGE_PRIMITIVE);
089: VPFFile edgeFile = VPFFileFactory.getInstance().getFile(
090: edgeTableName);
091:
092: // Get the rings
093: String faceTableName = tileDirectory.concat(File.separator)
094: .concat(FACE_PRIMITIVE);
095: VPFFile faceFile = VPFFileFactory.getInstance().getFile(
096: faceTableName);
097: faceFile.reset();
098:
099: String ringTableName = tileDirectory.concat(File.separator)
100: .concat(RING_TABLE);
101: VPFFile ringFile = VPFFileFactory.getInstance().getFile(
102: ringTableName);
103: ringFile.reset();
104:
105: Feature faceFeature = faceFile.readFeature();
106:
107: while (faceFeature != null) {
108: if (faceFeature.getAttribute("id").equals(
109: new Integer(faceId))) {
110: coordinates = new LinkedList();
111:
112: int ringId = Integer.parseInt(faceFeature.getAttribute(
113: "ring_ptr").toString());
114:
115: // Get the starting edge
116: int startEdgeId = ((Number) ringFile.getRowFromId("id",
117: ringId).getAttribute("start_edge")).intValue();
118: int nextEdgeId = startEdgeId;
119: int prevNodeId = -1;
120:
121: while (nextEdgeId > 0) {
122: Feature edgeRow = edgeFile.getRowFromId("id",
123: nextEdgeId);
124:
125: // Read all the important stuff from the edge row data
126: int leftFace = ((TripletId) edgeRow
127: .getAttribute("left_face")).getId();
128: int rightFace = ((TripletId) edgeRow
129: .getAttribute("right_face")).getId();
130: int startNode = ((Integer) edgeRow
131: .getAttribute("start_node")).intValue();
132: int endNode = ((Integer) edgeRow
133: .getAttribute("end_node")).intValue();
134: int leftEdge = ((TripletId) edgeRow
135: .getAttribute("left_edge")).getId();
136: int rightEdge = ((TripletId) edgeRow
137: .getAttribute("right_edge")).getId();
138: boolean addPoints = true;
139:
140: // If both faceIds are this faceId then this is a line extending into
141: // the face and not an edge line of the face so don't add it's points
142: // to the coordinates list. Except if it's the first edge encountered.
143: // ASCII art showing this case:
144: // /-----------\
145: // | |
146: // +---+ |
147: // | ^^ |
148: // | This one |
149: // \-----------/
150: if (faceId == leftFace && faceId == rightFace) {
151: addPoints = false;
152:
153: if (prevNodeId == startNode) {
154: isLeft = false;
155: prevNodeId = endNode;
156: } else if (prevNodeId == endNode) {
157: isLeft = true;
158: prevNodeId = startNode;
159: } else if (prevNodeId == -1) {
160: // This edge is the first one to be encountered.
161: // This is a messy case where we've got to figure out if
162: // we should start to the left or right. This peeks ahead
163: // at the left and right edges to see which has a start node
164: // that's the same as this edge's end node. Hopefully someone
165: // smarter can come up with a better solution.
166: int leftEdgeStartNode = ((Integer) edgeFile
167: .getRowFromId("id", leftEdge)
168: .getAttribute("start_node"))
169: .intValue();
170: int rightEdgeStartNode = ((Integer) edgeFile
171: .getRowFromId("id", rightEdge)
172: .getAttribute("start_node"))
173: .intValue();
174:
175: if (leftEdgeStartNode == endNode) {
176: isLeft = true;
177: prevNodeId = startNode;
178: } else if (rightEdgeStartNode == endNode) {
179: isLeft = false;
180: prevNodeId = endNode;
181: } else {
182: // Something really bad happened because we should never get here
183: throw new SQLException(
184: "This edge is not part of this face.");
185: }
186: } else {
187: // Something really bad happened because we should never get here
188: throw new SQLException(
189: "This edge is not part of this face.");
190: }
191: } else if (faceId == rightFace) {
192: isLeft = false;
193: prevNodeId = endNode;
194: } else if (faceId == leftFace) {
195: isLeft = true;
196: prevNodeId = startNode;
197: } else {
198: throw new SQLException(
199: "This edge is not part of this face.");
200: }
201:
202: // Get the geometry of the edge and add it to our line geometry
203: LineString edgeGeometry = (LineString) edgeRow
204: .getAttribute("coordinates");
205:
206: if (addPoints) {
207: if (isLeft) {
208: // We must take the coordinate values backwards
209: for (int inx = edgeGeometry.getNumPoints() - 1; inx >= 0; inx--) {
210: coordinate = edgeGeometry
211: .getCoordinateSequence()
212: .getCoordinate(inx);
213:
214: if ((previousCoordinate == null)
215: || (!coordinate
216: .equals3D(previousCoordinate))) {
217: coordinates.add(coordinate);
218: previousCoordinate = coordinate;
219: }
220: }
221: } else {
222: for (int inx = 0; inx < edgeGeometry
223: .getNumPoints(); inx++) {
224: coordinate = edgeGeometry
225: .getCoordinateSequence()
226: .getCoordinate(inx);
227:
228: if ((previousCoordinate == null)
229: || (!coordinate
230: .equals3D(previousCoordinate))) {
231: coordinates.add(coordinate);
232: previousCoordinate = coordinate;
233: }
234: }
235: }
236: } else {
237: coordinate = edgeGeometry
238: .getCoordinateSequence().getCoordinate(
239: isLeft ? 0 : edgeGeometry
240: .getNumPoints() - 1);
241: }
242:
243: tempEdgeId = isLeft ? leftEdge : rightEdge;
244:
245: if (tempEdgeId == startEdgeId) {
246: nextEdgeId = 0;
247: } else {
248: // Here is where we need to consider crossing tiles
249: nextEdgeId = tempEdgeId;
250: }
251: }
252:
253: // The dorks at JTS insist that you explicitly close your rings. Ugh.
254: if (!coordinate.equals(coordinates.get(0))) {
255: coordinates.add(coordinates.get(0));
256: }
257:
258: Coordinate[] coordinateArray = new Coordinate[coordinates
259: .size()];
260:
261: for (int cnx = 0; cnx < coordinates.size(); cnx++) {
262: coordinateArray[cnx] = (Coordinate) coordinates
263: .get(cnx);
264: }
265:
266: LinearRing ring = null;
267:
268: ring = geometryFactory
269: .createLinearRing(coordinateArray);
270:
271: if (outerRing == null) {
272: outerRing = ring;
273: } else {
274: // I haven't found any data to test this yet.
275: // If you do and it works, remove this comment.
276: innerRings.add(ring);
277: }
278: }
279:
280: if (faceFile.hasNext()) {
281: faceFeature = faceFile.readFeature();
282: } else {
283: faceFeature = null;
284: }
285: }
286:
287: if (innerRings.isEmpty()) {
288: result = geometryFactory.createPolygon(outerRing, null);
289: } else {
290: LinearRing[] ringArray = new LinearRing[innerRings.size()];
291:
292: for (int cnx = 0; cnx < innerRings.size(); cnx++) {
293: ringArray[cnx] = (LinearRing) innerRings.get(cnx);
294: }
295:
296: result = geometryFactory
297: .createPolygon(outerRing, ringArray);
298: }
299:
300: values.setDefaultGeometry(result);
301: }
302: }
|