/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2004-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.data.vpf.readers; import java.io.File; import java.io.IOException; import java.sql.SQLException; import java.util.LinkedList; import java.util.List; import java.util.Vector; import org.geotools.data.vpf.VPFFeatureType; import org.geotools.data.vpf.file.VPFFile; import org.geotools.data.vpf.file.VPFFileFactory; import org.geotools.data.vpf.ifc.FileConstants; import org.geotools.data.vpf.io.TripletId; import org.geotools.feature.IllegalAttributeException; import org.opengis.feature.simple.SimpleFeature; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.LinearRing; import com.vividsolutions.jts.geom.Polygon; /** * Creates Geometries for area objects * * @author <a href="mailto:jeff@ionicenterprise.com">Jeff Yutzler</a> * * * @source $URL$ */ public class AreaGeometryFactory extends VPFGeometryFactory implements FileConstants { /* (non-Javadoc) * @see com.ionicsoft.wfs.jdbc.geojdbc.module.vpf.VPFGeometryFactory#createGeometry(java.lang.String, int, int) */ public void createGeometry(VPFFeatureType featureType, SimpleFeature values) throws SQLException, IOException, IllegalAttributeException { int tempEdgeId; boolean isLeft = false; Coordinate previousCoordinate = null; Coordinate coordinate = null; List coordinates = null; Polygon result = null; GeometryFactory geometryFactory = new GeometryFactory(); LinearRing outerRing = null; List innerRings = new Vector(); // Get face information //TODO: turn these column names into constants int faceId = Integer.parseInt(values.getAttribute("fac_id").toString()); // Retrieve the tile directory String baseDirectory = featureType.getFeatureClass().getDirectoryName(); String tileDirectory = baseDirectory; // If the primitive table is there, this coverage is not tiled if (!new File(tileDirectory.concat(File.separator).concat(FACE_PRIMITIVE)) .exists()) { Short tileId = new Short(Short.parseShort( values.getAttribute("tile_id").toString())); tileDirectory = tileDirectory.concat(File.separator) .concat(featureType.getFeatureClass() .getCoverage() .getLibrary() .getTileMap() .get(tileId) .toString()).trim(); } // all edges from this tile that use the face String edgeTableName = tileDirectory.concat(File.separator).concat(EDGE_PRIMITIVE); VPFFile edgeFile = VPFFileFactory.getInstance().getFile(edgeTableName); // Get the rings String faceTableName = tileDirectory.concat(File.separator).concat(FACE_PRIMITIVE); VPFFile faceFile = VPFFileFactory.getInstance().getFile(faceTableName); faceFile.reset(); String ringTableName = tileDirectory.concat(File.separator).concat(RING_TABLE); VPFFile ringFile = VPFFileFactory.getInstance().getFile(ringTableName); ringFile.reset(); SimpleFeature faceFeature = faceFile.readFeature(); while (faceFeature != null) { if (faceFeature.getAttribute("id").equals(new Integer(faceId))) { coordinates = new LinkedList(); int ringId = Integer.parseInt(faceFeature.getAttribute( "ring_ptr").toString()); // Get the starting edge int startEdgeId = ((Number) ringFile.getRowFromId("id", ringId) .getAttribute("start_edge")) .intValue(); int nextEdgeId = startEdgeId; int prevNodeId = -1; while (nextEdgeId > 0) { SimpleFeature edgeRow = edgeFile.getRowFromId("id", nextEdgeId); // Read all the important stuff from the edge row data int leftFace = ((TripletId) edgeRow.getAttribute("left_face")).getId(); int rightFace = ((TripletId) edgeRow.getAttribute("right_face")).getId(); int startNode = ((Integer) edgeRow.getAttribute("start_node")).intValue(); int endNode = ((Integer) edgeRow.getAttribute("end_node")).intValue(); int leftEdge = ((TripletId) edgeRow.getAttribute("left_edge")).getId(); int rightEdge = ((TripletId) edgeRow.getAttribute("right_edge")).getId(); boolean addPoints = true; // If both faceIds are this faceId then this is a line extending into // the face and not an edge line of the face so don't add it's points // to the coordinates list. Except if it's the first edge encountered. // ASCII art showing this case: // /-----------\ // | | // +---+ | // | ^^ | // | This one | // \-----------/ if (faceId == leftFace && faceId == rightFace) { addPoints = false; if (prevNodeId == startNode) { isLeft = false; prevNodeId = endNode; } else if (prevNodeId == endNode) { isLeft = true; prevNodeId = startNode; } else if (prevNodeId == -1) { // This edge is the first one to be encountered. // This is a messy case where we've got to figure out if // we should start to the left or right. This peeks ahead // at the left and right edges to see which has a start node // that's the same as this edge's end node. Hopefully someone // smarter can come up with a better solution. int leftEdgeStartNode = ((Integer)edgeFile.getRowFromId("id", leftEdge).getAttribute("start_node")).intValue(); int rightEdgeStartNode = ((Integer)edgeFile.getRowFromId("id", rightEdge).getAttribute("start_node")).intValue(); if (leftEdgeStartNode == endNode) { isLeft = true; prevNodeId = startNode; } else if (rightEdgeStartNode == endNode) { isLeft = false; prevNodeId = endNode; } else { // Something really bad happened because we should never get here throw new SQLException( "This edge is not part of this face."); } } else { // Something really bad happened because we should never get here throw new SQLException( "This edge is not part of this face."); } } else if (faceId == rightFace) { isLeft = false; prevNodeId = endNode; } else if (faceId == leftFace) { isLeft = true; prevNodeId = startNode; } else { throw new SQLException( "This edge is not part of this face."); } // Get the geometry of the edge and add it to our line geometry LineString edgeGeometry = (LineString) edgeRow.getAttribute( "coordinates"); if ( addPoints ) { if (isLeft) { // We must take the coordinate values backwards for (int inx = edgeGeometry.getNumPoints() - 1; inx >= 0; inx--) { coordinate = edgeGeometry.getCoordinateSequence().getCoordinate(inx); if ((previousCoordinate == null) || (!coordinate.equals3D(previousCoordinate))) { coordinates.add(coordinate); previousCoordinate = coordinate; } } } else { for (int inx = 0; inx < edgeGeometry.getNumPoints(); inx++) { coordinate = edgeGeometry.getCoordinateSequence().getCoordinate(inx); if ((previousCoordinate == null) || (!coordinate.equals3D(previousCoordinate))) { coordinates.add(coordinate); previousCoordinate = coordinate; } } } } else { coordinate = edgeGeometry.getCoordinateSequence().getCoordinate( isLeft ? 0 : edgeGeometry.getNumPoints() - 1); } tempEdgeId = isLeft ? leftEdge : rightEdge; if (tempEdgeId == startEdgeId) { nextEdgeId = 0; } else { // Here is where we need to consider crossing tiles nextEdgeId = tempEdgeId; } } // The dorks at JTS insist that you explicitly close your rings. Ugh. if (!coordinate.equals(coordinates.get(0))) { coordinates.add(coordinates.get(0)); } Coordinate[] coordinateArray = new Coordinate[coordinates.size()]; for (int cnx = 0; cnx < coordinates.size(); cnx++) { coordinateArray[cnx] = (Coordinate) coordinates.get(cnx); } LinearRing ring = null; ring = geometryFactory.createLinearRing(coordinateArray); if (outerRing == null) { outerRing = ring; } else { // I haven't found any data to test this yet. // If you do and it works, remove this comment. innerRings.add(ring); } } if (faceFile.hasNext()) { faceFeature = faceFile.readFeature(); } else { faceFeature = null; } } if (innerRings.isEmpty()) { result = geometryFactory.createPolygon(outerRing, null); } else { LinearRing[] ringArray = new LinearRing[innerRings.size()]; for (int cnx = 0; cnx < innerRings.size(); cnx++) { ringArray[cnx] = (LinearRing) innerRings.get(cnx); } result = geometryFactory.createPolygon(outerRing, ringArray); } values.setDefaultGeometry(result); } }