/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-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.gml; import java.util.Vector; import java.util.logging.Logger; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.XMLFilterImpl; import com.vividsolutions.jts.geom.Geometry; /** * LEVEL3 GML filter: translates JTS elements and attribute data into features. * * <p> * This filter simply reads in the events and coordinates passed to it by its * GMLFilterDocument child and converts them into JTS objects. Note that it * passes through anything not specifically sent to it by GMLFilterDocument * (i.e. more or less everything not in geometry.xsd). The parent of this * filter must implement GMLHandlerJTS in order to receive the JTS objects * passed by this filter. * </p> * * @author Rob Hranac, Vision for New York * * @source $URL$ * @version $Id$ */ public class GMLFilterFeature extends XMLFilterImpl implements GMLHandlerJTS { //private FeatureSchema metadata = new FeatureSchema(); // Static Globals to handle some expected elements /** The logger for the GML module. */ private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger( "org.geotools.data.gml"); /** GML namespace string. */ //private static final String GML_NAMESPACE = "http://www.opengis.net/gml"; /** Handler for the JTS elements generated by this filter. */ private GMLHandlerFeature parent; //private boolean defaultGeom = false; /** Factory for the JTS geometries. */ //private Feature currentFeature; // = new FeatureFlat(); /** Stores current feature attributes. */ private Vector attributes = new Vector(); private Vector attributeNames = new Vector(); private String fid = null; /** Stores current feature attributes. */ private boolean insideAttribute = false; /** Stores current feature attributes. */ private boolean insideFeature = false; /** Stores current feature attributes. */ //private boolean insideGeometry = false; /** Stores current feature attributes. */ private Object tempValue = null; private String attName = ""; /** The current namespace we're in. */ private String NAMESPACE; /** Some sort of feature name. */ //private String FEATURE_MEMBER_NAME = "featureMember"; private String typeName = "GenericFeature"; /** Collects string chunks in {@link #characters(char[], int, int)} * callback to be handled at the beggining of {@link #endElement(String, String, String)} */ private StringBuffer characters = new StringBuffer(); /** * Constructor with parent, which must implement GMLHandlerJTS. * * @param parent The parent of this filter. */ public GMLFilterFeature(GMLHandlerFeature parent) { super(); this.parent = parent; } public void setSchema(String uri) { } /** * Manages the start of a new main or sub geometry. This method looks at * the status of the current handler and either returns a new sub-handler * (if the last one was successfully returned already) or passes the * element start notification along to the current handler as a sub * geometry notice. * * @param geometry The geometry from the child. */ public void geometry(Geometry geometry) { //insideGeometry = true; //_log.debug("adding geometry with name "+attName); if (insideFeature) { if (attName.equals("")) { attributeNames.addElement("geometry"); } else { attributeNames.addElement(attName); } attributes.addElement(geometry); endAttribute(); //currentFeature.setGeometry(geometry); } //else { // parent.geometry(geometry); //} } /** * Checks for GML element start and - if not a coordinates element - sends * it directly on down the chain to the appropriate parent handler. If it * is a coordinates (or coord) element, it uses internal methods to set * the current state of the coordinates reader appropriately. * * @param namespaceURI The namespace of the element. * @param localName The local name of the element. * @param qName The full name of the element, including namespace prefix. * @param atts The element attributes. * * @throws SAXException Some parsing error occured while reading * coordinates. * * @task HACK:The method for determining if something is a feature or not * is too crude. */ public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { characters.setLength(0); if (localName.endsWith("Collection")) { // if we scan the scema this can be done better. NAMESPACE = namespaceURI; //_log.debug("starting a collection with namespace " + NAMESPACE + " and Name " + localName); return; } // if it ends with Member we'll assume it's a feature for the time being // nasty hack to fix members of multi lines and polygons if (isFeatureMember(localName)) { attributes = new Vector(); attributeNames = new Vector(); //currentFeature = new FeatureFlat(); insideFeature = true; tempValue = null; //_log.debug("Starting a feature " + localName); } else if (insideFeature) { //_log.debug("inside feature " + localName); for (int i = 0; i < atts.getLength(); i++) { String name = atts.getLocalName(i); if (name.equalsIgnoreCase("fid")) { //currentFeature.setTypeName(localName); typeName = new String(localName); //_log.debug("set type name " + localName); fid = atts.getValue(i); } else { attributes.add(atts.getValue(i)); attributeNames.add(name); } } if (!typeName.equalsIgnoreCase(localName)) { if (attName.equals("")) { //_log.debug("setting attName to " + localName); attName = localName; } else { //_log.debug("adding " + localName + " to " + attName); attName = attName + "/" + localName; } //_log.debug("attName now equals " + attName); } insideAttribute = true; return; } else if (insideAttribute) { //_log.debug("inside attribute"); } else { parent.startElement(namespaceURI, localName, qName, atts); } } private boolean isFeatureMember(String localName) { return localName.endsWith("Member") && !localName.endsWith("StringMember") && !localName.endsWith("polygonMember") && !localName.endsWith("pointMember"); } /** * Reads the only internal characters read by pure GML parsers, which are * coordinates. These coordinates are sent to the coordinates reader * class which interprets them appropriately, depending on the its current * state. * * @param ch Raw coordinate string from the GML document. * @param start Beginning character position of raw coordinate string. * @param length Length of the character string. * * @throws SAXException Some parsing error occurred while reading * coordinates. */ public void characters(char[] ch, int start, int length) throws SAXException { characters.append(ch, start, length); } /** * Handles the string chunks collected in {@link #characters}. */ private void handleCharacters() throws SAXException{ if(characters.length() == 0){ return; } // the methods here read in both coordinates and coords and take the // grunt-work out of this task for geometry handlers. // See the documentation for CoordinatesReader to see what this entails String rawAttribute = characters.toString().trim(); characters.setLength(0); if (insideAttribute && !rawAttribute.equals("")) { LOGGER.info("raw att = " + rawAttribute); try { tempValue = new Integer(rawAttribute); } catch (NumberFormatException e1) { try { tempValue = new Double(rawAttribute); } catch (NumberFormatException e2) { if (tempValue instanceof StringBuffer) { ((StringBuffer) tempValue).append(" " + rawAttribute); } else { tempValue = new StringBuffer(rawAttribute); } } } } else { parent.characters(rawAttribute.toCharArray(), 0, rawAttribute.length()); } } /** * Checks for GML element end and - if not a coordinates element - sends it * directly on down the chain to the appropriate parent handler. If it is * a coordinates (or coord) element, it uses internal methods to set the * current state of the coordinates reader appropriately. * * @param namespaceURI Namespace of the element. * @param localName Local name of the element. * @param qName Full name of the element, including namespace prefix. * * @throws SAXException Parsing error occurred while reading coordinates. */ public void endElement(String namespaceURI, String localName, String qName) throws SAXException { handleCharacters(); if (isFeatureMember(localName)) { SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder(); tb.setName( typeName ); tb.setNamespaceURI( namespaceURI ); // AttributeType attDef[] = new AttributeTypeDefault[attributes.size()]; // for (int i = 0; i < attributes.size(); i++){ // attDef[i] = new AttributeTypeDefault((String) attributeNames.get(i),attributes.get(i).getClass()); // } // try { // FeatureType schema = FeatureTypeFactory.create(attDef).setTypeName(typeName); // schema.setNamespace(namespaceURI); // FlatFeatureFactory fac = new FlatFeatureFactory(schema); // Feature feature = fac.create((Object []) attributes.toArray(),fid); // //currentFeature.setAttributes((Object []) attributes.toArray()); // parent.feature(feature); // //_log.debug("resetting attName at end of feature"); // attName = ""; // } // catch (org.geotools.feature.SchemaException sve){ // //TODO: work out what to do in this case! // //_log.error("Unable to create valid schema",sve); // } // catch (org.geotools.feature.IllegalFeatureException ife){ // //TODO: work out what to do in this case! // //_log.error("Unable to build feature",ife); // } // insideFeature = false; for (int i = 0, ii = attributes.size(); i < ii; i++) { String name = (String) attributeNames.get(i); Class clazz = attributes.get(i).getClass(); tb.add( name, clazz ); } SimpleFeatureType featureType = tb.buildFeatureType(); try { SimpleFeature feature = SimpleFeatureBuilder.build(featureType, attributes, fid); parent.feature(feature); } catch (org.geotools.feature.IllegalAttributeException ife) { //TODO: work out what to do in this case! //_log.error("Unable to build feature",ife); // UNBELIEVABLE !!!!!!!!!!!!!!!!!!!!!!!!!!! - IanS } attName = ""; insideFeature = false; } else if (insideAttribute) { //_log.debug("end - inside attribute [" + tempValue + "]"); if ((tempValue != null) && !tempValue.toString().trim().equals("")) { if (tempValue instanceof StringBuffer) { tempValue = tempValue.toString(); } attributes.add(tempValue); attributeNames.add(attName); tempValue = null; } endAttribute(); } else { parent.endElement(namespaceURI, localName, qName); //_log.debug("end - inside feature"); //insideFeature = false; } } /** * Ends an attribute, by resetting the attribute name and setting * insideAttribute to false. */ private void endAttribute() { int index = attName.lastIndexOf('/'); if (index > -1) { //_log.debug("removing " + attName.substring(index+1)); attName = attName.substring(0, index); } else { attName = ""; } //_log.debug("attName now equals " + attName); insideAttribute = false; } }