/* * 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; import java.io.File; import java.io.IOException; import java.net.URI; import java.util.AbstractList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Vector; import org.geotools.data.vpf.file.VPFFile; import org.geotools.data.vpf.file.VPFFileFactory; import org.geotools.data.vpf.ifc.DataTypesDefinition; import org.geotools.data.vpf.ifc.FileConstants; import org.geotools.data.vpf.readers.AreaGeometryFactory; import org.geotools.data.vpf.readers.ConnectedNodeGeometryFactory; import org.geotools.data.vpf.readers.EntityNodeGeometryFactory; import org.geotools.data.vpf.readers.LineGeometryFactory; import org.geotools.data.vpf.readers.TextGeometryFactory; import org.geotools.data.vpf.readers.VPFGeometryFactory; import org.geotools.feature.AttributeTypeBuilder; import org.geotools.feature.IllegalAttributeException; import org.geotools.feature.SchemaException; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.feature.type.AnnotationFeatureType; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeDescriptor; import org.opengis.feature.type.GeometryDescriptor; import org.opengis.feature.type.GeometryType; import org.opengis.feature.type.Name; import org.opengis.feature.type.Name; import org.opengis.feature.type.PropertyDescriptor; import org.opengis.filter.Filter; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.util.InternationalString; import com.vividsolutions.jts.geom.Geometry; /** * A VPF feature class. Note that feature classes may contain one * or more feature types. However, all of the feature types of a * feature class share the same schema. A feature type will therefore * delegate its schema related operations to its feature class. * * @author <a href="mailto:jeff@ionicenterprise.com">Jeff Yutzler</a> * * * @source $URL$ */ public class VPFFeatureClass implements DataTypesDefinition, FileConstants, SimpleFeatureType { /** * The contained feature type */ private SimpleFeatureType featureType; /** * The columns that are part of this feature class */ private final List columns = new Vector(); /** The coverage this feature class is part of */ private final VPFCoverage coverage; /** The path of the directory containing this feature class */ private final String directoryName; /** A list of files which are read to retrieve data for this feature class */ private final AbstractList fileList = new Vector(); /** A list of ColumnPair objects which identify the file joins */ private final AbstractList joinList = new Vector(); /** The name of the feature class */ private final String typeName; /** The uri of the namespace in which features should be created */ private final URI namespace; /** * The geometry factory for this feature class */ private VPFGeometryFactory geometryFactory; /** * Indicator that the feature type is a text feature. */ private boolean textTypeFeature = false; /** * Constructor * * @param cCoverage the owning coverage * @param cName the name of the class * @param cDirectoryName the directory containing the class * @throws SchemaException For problems making one of the feature classes as a FeatureType. */ public VPFFeatureClass(VPFCoverage cCoverage, String cName, String cDirectoryName) throws SchemaException{ this(cCoverage, cName, cDirectoryName, null); } /** * Constructor * * @param cCoverage the owning coverage * @param cName the name of the class * @param cDirectoryName the directory containing the class * @param cNamespace the namespace to create features with. If null then * a default from VPFLibrary.DEFAULTNAMESPACE is assigned. * @throws SchemaException For problems making one of the feature classes as a FeatureType. */ public VPFFeatureClass(VPFCoverage cCoverage, String cName, String cDirectoryName, URI cNamespace) throws SchemaException{ coverage = cCoverage; directoryName = cDirectoryName; typeName = cName; if (cNamespace == null) { namespace = VPFLibrary.DEFAULT_NAMESPACE; } else { namespace = cNamespace; } String fcsFileName = directoryName + File.separator + TABLE_FCS; try { VPFFile fcsFile = (VPFFile) VPFFileFactory.getInstance().getFile(fcsFileName); Iterator iter = fcsFile.readAllRows().iterator(); while (iter.hasNext()) { SimpleFeature feature = (SimpleFeature) iter.next(); String featureClassName = feature.getAttribute("feature_class") .toString().trim(); if (typeName.equals(featureClassName)) { addFCS(feature); } } // Deal with the geometry column iter = columns.iterator(); GeometryDescriptor gat = null; AttributeDescriptor geometryColumn = null; while (iter.hasNext()) { geometryColumn = (AttributeDescriptor) iter.next(); if(Geometry.class.isAssignableFrom(geometryColumn.getType().getBinding())){ if(geometryColumn instanceof GeometryDescriptor){ gat = (GeometryDescriptor)geometryColumn; }else if (geometryColumn instanceof VPFColumn){ gat = ((VPFColumn)geometryColumn).getGeometryAttributeType(); } break; } } SimpleFeatureType superType = null; // if it's a text geometry feature type add annotation as a super type if (textTypeFeature) { superType = AnnotationFeatureType.ANNOTATION; } SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder(); b.setName(cName); b.setNamespaceURI(namespace); b.setSuperType(superType); b.addAll(columns); b.setDefaultGeometry(gat.getLocalName()); featureType = b.buildFeatureType(); } catch (IOException exp) { //We've already searched the FCS file once successfully //So this should never happen exp.printStackTrace(); } } /** * Add the information from a new FCS row. * * @param row The FCS table row */ private void addFCS(SimpleFeature row) //throws IOException { String table1 = row.getAttribute("table1").toString(); String table1Key = row.getAttribute("table1_key").toString(); String table2 = row.getAttribute("table2").toString(); String table2Key = row.getAttribute("table2_key").toString(); try { VPFFile vpfFile1 = VPFFileFactory.getInstance().getFile(directoryName.concat( File.separator).concat(table1)); addFileToTable(vpfFile1); VPFFile vpfFile2 = null; AttributeDescriptor joinColumn1 = (VPFColumn) vpfFile1.getDescriptor(table1Key); AttributeDescriptor joinColumn2; try { vpfFile2 = VPFFileFactory.getInstance().getFile(directoryName.concat( File.separator).concat(table2)); addFileToTable(vpfFile2); joinColumn2 = (VPFColumn) vpfFile2.getDescriptor(table2Key); } catch (IOException exc) { fileList.add(null); // We need to add a geometry column joinColumn2 = buildGeometryColumn(table2); } // FCS's that are the inverse of existing ones are not needed // But we should never get this far if (!joinList.contains(new ColumnPair(joinColumn2, joinColumn1))) { joinList.add(new ColumnPair(joinColumn1, joinColumn2)); } } catch (IOException exc) { // File was not present // which means it is for a geometry table // we can safely ignore it for now // exc.printStackTrace(); } } /** * Create a geometry column (usually for feature classes that * make use of tiles so simple joins can not be used) * @param table The name of the table containing the geometric primitives * @return An <code>AttributeType</code> for the geometry column which is actually a <code>GeometryAttributeType</code> */ private AttributeDescriptor buildGeometryColumn(String table) { AttributeDescriptor result = null; table = table.trim().toLowerCase(); // Why would the fileList already contain a null? // if(!fileList.contains(null)){ CoordinateReferenceSystem crs = getCoverage().getLibrary().getCoordinateReferenceSystem(); if(crs != null){ result = new AttributeTypeBuilder().binding( Geometry.class ) .nillable(true).length(-1).crs(crs).buildDescriptor("GEOMETRY"); }else{ result = new AttributeTypeBuilder().binding( Geometry.class ) .nillable(true).buildDescriptor("GEOMETRY"); } columns.add(result); setGeometryFactory(table); // } return result; } /** * Identifies the type of geometry factory to use based on the * name of the table containing the geometry, then constructs the * appropriate geometry factory object. * @param table The name of the geometry table */ private void setGeometryFactory(String table) { if (table.equals(EDGE_PRIMITIVE)) { geometryFactory = new LineGeometryFactory(); } else if (table.equals(FACE_PRIMITIVE)) { geometryFactory = new AreaGeometryFactory(); } else if (table.equals(CONNECTED_NODE_PRIMITIVE)) { geometryFactory = new ConnectedNodeGeometryFactory(); } else if (table.equals(ENTITY_NODE_PRIMITIVE)) { geometryFactory = new EntityNodeGeometryFactory(); } else if (table.equals(TEXT_PRIMITIVE)) { geometryFactory = new TextGeometryFactory(); textTypeFeature = true; } // if an invalid string is returned, there will be no geometry } /** * Adds all of the columns from a VPF file into the table. Note: * This does not handle columns with the same name particularly well. * Perhaps the xpath mechanism can be used to help here. * @param vpfFile the <code>VPFFile</code> object to use */ private void addFileToTable(VPFFile vpfFile) { // Class columnClass; boolean addPrimaryKey = fileList.isEmpty(); // Check to see if we have already grabbed this file if (!fileList.contains(vpfFile)) { fileList.add(vpfFile); // Pull the columns off of the file and add them to our schema // Except for the first file, ignore the first column since it is a join column for (int inx = addPrimaryKey ? 0 : 1; inx < vpfFile.getAttributeCount(); inx++) { columns.add(vpfFile.getDescriptor(inx)); } } } /** * The coverage that owns this feature class * @return a <code>VPFCoverage</code> object */ public VPFCoverage getCoverage() { return coverage; } /** * The path to the directory that contains this feature class * * @return a <code>String</code> value representing the path to the directory. */ public String getDirectoryName() { return directoryName; } /** * Returns a list of file objects * * @return a <code>List</code> containing <code>VPFFile</code> objects. */ public List getFileList() { return fileList; } /** * DOCUMENT ME! * * @return a<code>List</code> containing <code>ColumnPair</code> objects * which identify the file joins. */ public List getJoinList() { return joinList; } /* (non-Javadoc) * @see org.geotools.feature.FeatureType#getTypeName() */ public String getTypeName() { return featureType.getTypeName(); } /* (non-Javadoc) * @see org.geotools.feature.FeatureType#getAttributeCount() */ public int getAttributeCount() { return featureType.getAttributeCount(); } /* (non-Javadoc) * @see org.geotools.feature.FeatureType#isAbstract() */ public boolean isAbstract() { return featureType.isAbstract(); } /** * @return Returns the geometryFactory. */ public VPFGeometryFactory getGeometryFactory() { return geometryFactory; } public boolean equals(Object obj) { return featureType.equals(obj); } public int hashCode() { return featureType.hashCode(); } public AttributeDescriptor getDescriptor(Name name) { return featureType.getDescriptor(name); } public AttributeDescriptor getDescriptor(String name) { return featureType.getDescriptor(name); } public AttributeDescriptor getDescriptor(int index) { return featureType.getDescriptor(index); } public List getAttributeDescriptors() { return featureType.getAttributeDescriptors(); } public org.opengis.feature.type.AttributeType getType(Name name) { return featureType.getType( name ); } public org.opengis.feature.type.AttributeType getType(String name) { return featureType.getType( name ); } public org.opengis.feature.type.AttributeType getType(int index) { return featureType.getType( index ); } public List getTypes() { return featureType.getTypes(); } public CoordinateReferenceSystem getCoordinateReferenceSystem() { return featureType.getCoordinateReferenceSystem(); } public GeometryDescriptor getGeometryDescriptor() { return featureType.getGeometryDescriptor(); } public Class getBinding() { return featureType.getBinding(); } public Collection getDescriptors() { return featureType.getDescriptors(); } public boolean isInline() { return featureType.isInline(); } public org.opengis.feature.type.AttributeType getSuper() { return featureType.getSuper(); } public boolean isIdentified() { return featureType.isIdentified(); } public InternationalString getDescription() { return featureType.getDescription(); } public Name getName() { return featureType.getName(); } public int indexOf(String name) { return featureType.indexOf(name); } public int indexOf(Name name) { return featureType.indexOf(name); } public List<Filter> getRestrictions() { return featureType.getRestrictions(); } public Map<Object, Object> getUserData() { return featureType.getUserData(); } }