0001: /*
0002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
0003: * for visualizing and manipulating spatial features with geometry and attributes.
0004: *
0005: * Copyright (C) 2003 Vivid Solutions
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License
0009: * as published by the Free Software Foundation; either version 2
0010: * of the License, or (at your option) any later version.
0011: *
0012: * This program is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0015: * GNU General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * along with this program; if not, write to the Free Software
0019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
0020: *
0021: * For more information, contact:
0022: *
0023: * Vivid Solutions
0024: * Suite #1A
0025: * 2328 Government Street
0026: * Victoria BC V8T 5G5
0027: * Canada
0028: *
0029: * (250)385-6040
0030: * www.vividsolutions.com
0031: */
0032: package com.vividsolutions.jump.io;
0033:
0034: import com.vividsolutions.jts.geom.*;
0035:
0036: import com.vividsolutions.jump.feature.*;
0037:
0038: import org.xml.sax.*;
0039: import org.xml.sax.helpers.DefaultHandler;
0040:
0041: import java.io.*;
0042:
0043: import java.util.List;
0044: import java.util.ArrayList;
0045: import java.util.StringTokenizer;
0046:
0047: /**
0048: * GMLReader is a {@link JUMPReader} specialized to read GML files.
0049: *
0050: * <p>
0051: * DataProperties for the JCSReader load(DataProperties) interface:
0052: * </p>
0053: * <p>
0054: * <table border='1' cellspacing='0' cellpadding='4'>
0055: * <tr>
0056: * <th>Parameter</th>
0057: * <th>Meaning</th>
0058: * </tr>
0059: * <tr>
0060: * <td>
0061: * File or DefaultValue
0062: * </td>
0063: *
0064: * <td>
0065: * File name for the input .xml file
0066: * </td>
0067: * </tr>
0068: *
0069: * <tr>
0070: * <td>
0071: * InputTemplateFile
0072: * </td>
0073: * <td>
0074: * Filename for the GMLInputTemplate .xml file
0075: * </td>
0076: * </tr>
0077: *
0078: * <tr>
0079: * <td>CompressedFile</td>
0080: * <td>
0081: * File name (a .zip or .gz) with a .jml/.xml/.gml inside
0082: * (specified by File)
0083: * </td>
0084: * </tr>
0085: *
0086: * <tr>
0087: * <td>
0088: * CompressedFileTemplate</td><td>File name (.zip or .gz)
0089: * with the input template in (specified by InputTemplateFile)
0090: * </td>
0091: * </tr>
0092: * </table>
0093: * </p>
0094: *
0095: * <br>
0096: * NOTE: If InputTemplateFile is unspecified, GMLReader will try to read one
0097: * off the top of the input .xml file (the JCS format). This is the same as
0098: * specifying the File and TemplateFile to be the same. <br>
0099: * <br>
0100: *
0101: * Typically, you would write:<br>
0102: *
0103: * <pre>
0104: * gmlReader = new GMLReader();
0105: * gmlReader.load( DriverProperties) ; // has InputFile and InputTemplate
0106: * </pre>
0107: * or:
0108: * <pre>
0109: * gmlReader.setInputTemplate( GMLInputTemplate);
0110: * gmlReader.load( <Reader> , <stream name> );
0111: * </pre>
0112: * <br>
0113: * <br>
0114: * Internal Details - This is based on a small finite state machine with these
0115: * states: <br>
0116: * <br>
0117: * STATE MEANING <br>
0118: * 0 Init <br>
0119: * 1 Waiting for Collection tag <br>
0120: * 2 Waiting for Feature tag <br>
0121: * 3 Getting jcs columns <br>
0122: * 4 Parsing geometry (goes back to state 3) <br>
0123: * 1000 Parsing Multi-geometry, recursion level =1 <br>
0124: * 1001 Parsing Multi-geometry, recursion level =2 <br>
0125: * ... <br>
0126: * <br>
0127: * <br>
0128: * State Diagram <br>
0129: * <br>
0130: * init <br>
0131: * <PRE>
0132: * 0 -----> 1
0133: * |
0134: * | Collection start Tag
0135: * |
0136: * -->2----------------> FINISH
0137: * \ | End Collection tag
0138: * End Feature tag \ |
0139: * \|
0140: * 4<-------->3
0141: * Geometry start/end
0142: *</PRE>
0143: * <br>
0144: * For multi-geometries <br>
0145: * On start Multi-geometry, increment state by 1 (or jump to 1000 if at state
0146: * 4) <br>
0147: * make sure recursivegeometry[state-1000] is null <br>
0148: * <put any object into the recursivegeometry[state-1000] collection>
0149: *
0150: * <br>
0151: * <br>
0152: * on end multi-geometry, <br>
0153: * build geometry in recursivegeometry[state-1000], add it to
0154: * recursivegeometry[state-1000-1] <br>
0155: * state= state-1 <br>
0156: * <br>
0157: * For single geometries - they will be stuck into recursivegeometry[0], which
0158: * is the same <br>
0159: * as geometry <br>
0160: * <br>
0161: * For multi geometries - they will also be stuck into recursivegeometry[0],
0162: * which is the same <br>
0163: * as geometry. But, for the first nested geometry, it will be stuck into
0164: * recursivegeometry[1], <br>
0165: * which will then be geometry <br>
0166: * <pre>
0167: * example of double GCs:
0168: * START geometry ->move to state 4
0169: * START TAG: multi* -> move to state 1000, geometry = recursivegeometry[0]
0170: * <POINT>
0171: *
0172: * -> added to geometry <POINT>
0173: *
0174: * -> added to geometry START TAG: multi* -> move to state 1001, geometry =
0175: * recursivegeometry[1] <POINT>
0176: *
0177: * -> added to geometry <POINT>
0178: *
0179: * -> added to geometry END TAG: multi -> move to state 1000, build geometry in
0180: * recursivegeometry[1], add to recursivegeometry[0], geometry =
0181: * recursivegeometry[0] <POINT>
0182: *
0183: * -> added to geometry END TAG: multi -> <finished> move to state 4, build
0184: * geometry in recursivegeometry[0] (thats the result) and put it in
0185: * finalGeometry END geometry -> add to feature collection example of simple
0186: * geometry: START geometry ->move to state 4 BEGIN polygon ->clear out inner
0187: * ring accumulator BEGIN outerboundary BEGIN linearring END linearring -> put
0188: * points in linearRing END outerboundary -> put linearRing in outerBoundary
0189: * BEGIN innerboundary BEGIN linearring END linearring -> put points in
0190: * linearRing END innerboundary -> add linearRing to innerBoundary list END
0191: * polygon -> build polygon (put in geometry, which is recursivegeometry[0] END
0192: * geometry => add to feature collection
0193: * </pre>
0194: *
0195: * Most of the work is done in the endTag method!
0196: * <br>
0197: * <br>
0198: * New additions: Jan 2005 by Dave Blasby
0199: * allow srid to be parsed from the GML file
0200: * For example:
0201: * <gml:LineString srsName="EPSG:42102">
0202: * ....
0203: * </gml:LineString>
0204: * The SRID of the created geometry will be 42102.
0205: * It accepts srsNames of the form "<letters>:<number>".
0206: * ie. "EPSG:111" or "DAVE:222" or "BCGOV:333" etc...
0207: * The Geometry's SRID will be the number.
0208: * If you have a GEOMETRYCOLLECTION with more than one SRID specified
0209: * the SRID of the result will be indeterminate (this isnt correct GML).
0210: *
0211: * Geometries without a srsName will get SRID 0.
0212: *
0213: * This functionality defaults to off for compatibility.
0214: * To turn it on or off, call the acceptSRID(true|false) function.
0215: *
0216: * New Addition: Jan, 2005by Dave Blasby
0217: * Added slightly better support for type=OBJECT. It sticks a String in. Before it would probably throw an error.
0218: * Added support for multi-objects for example:
0219: * <a>
0220: * <b>...1...</b>
0221: * <b>...2...</b>
0222: * <b>...3...</b>
0223: * </a>
0224: * Old behavior would be to for column 'b' to have value "...3...".
0225: * New behavior (only if you set b's type to 'OBJECT' and set the GMLReader to processMultiItems as lists)
0226: * <a><b>...1...</b></a> --> b get the string "...1..." (as before)
0227: * <a><b>...1...</b><b>...2...</b><b>...3...</b></a> --> 'b' is a list of String ['...1...','...2...','...3...']
0228: *
0229: */
0230: public class GMLReader extends DefaultHandler implements JUMPReader {
0231: static int STATE_GET_COLUMNS = 3;
0232:
0233: /**
0234: * STATE MEANING <br>
0235: * 0 Init <br>
0236: * 1 Waiting for Collection tag <br>
0237: * 2 Waiting for Feature tag <br>
0238: * 3 Getting jcs columns <br>
0239: * 4 Parsing geometry (goes back to state 3) <br>
0240: * 1000 Parsing Multi-geometry, recursion level =1 <br>
0241: * 1001 Parsing Multi-geometry, recursion level =2 <br>
0242: */
0243: static int STATE_INIT = 0;
0244: static int STATE_PARSE_GEOM_NESTED = 1000;
0245: static int STATE_PARSE_GEOM_SIMPLE = 4;
0246: static int STATE_WAIT_COLLECTION_TAG = 1;
0247: static int STATE_WAIT_FEATURE_TAG = 2;
0248: GMLInputTemplate GMLinput = null;
0249: int STATE = STATE_INIT; //list of points
0250: Point apoint;
0251: Feature currentFeature;
0252: int currentGeometryNumb = 1;
0253: FeatureCollection fc;
0254: FeatureSchema fcmd; // list of geometries
0255: Geometry finalGeometry; //list of geometrycollections - list of list of geometry
0256: ArrayList geometry;
0257: GeometryFactory geometryFactory = new GeometryFactory(); //this might get replaced if there's an SRID change
0258: ArrayList innerBoundaries = new ArrayList();
0259: Attributes lastStartTag_atts;
0260: String lastStartTag_name;
0261: String lastStartTag_qName; //accumulate values inside a tag
0262:
0263: // info about the last start tag encountered
0264: String lastStartTag_uri;
0265: LineString lineString;
0266: LinearRing linearRing; // a LR
0267: LinearRing outerBoundary; //list of LinearRing
0268: ArrayList pointList = new ArrayList(); // list of accumulated points (Coordinate)
0269: Polygon polygon; // polygon
0270:
0271: // higherlevel geomery object
0272: ArrayList recursivegeometry = new ArrayList();
0273:
0274: // low-level geometry objects
0275: Coordinate singleCoordinate = new Coordinate();
0276: String streamName; //result geometry -
0277: StringBuffer tagBody;
0278: XMLReader xr; //see above
0279:
0280: int SRID = 0; // srid to give the created geometries
0281: public boolean parseSRID = false; //true = put SRID for srsName="EPSG:42102"
0282: /**
0283: * true => for 'OBJECT' types, if you find more than 1 item, make a list and store all the results
0284: */
0285: public boolean multiItemsAsLists = false;
0286:
0287: /**
0288: * Constructor - make a SAXParser and have this GMLReader be its
0289: * ContentHandler and ErrorHandler.
0290: */
0291: public GMLReader() {
0292: super ();
0293: xr = new org.apache.xerces.parsers.SAXParser();
0294: xr.setContentHandler(this );
0295: xr.setErrorHandler(this );
0296: }
0297:
0298: /**
0299: * parse SRID information in geometry tags
0300: * @param parseTheSRID true = parse
0301: */
0302: public void acceptSRID(boolean parseTheSRID) {
0303: parseSRID = parseTheSRID;
0304: }
0305:
0306: /**
0307: * Added slightly better support for type=OBJECT. It sticks a String in. Before it would probably throw an error.
0308: * Added support for multi-objects for example:
0309: * <a>
0310: * <b>...1...</b>
0311: * <b>...2...</b>
0312: * <b>...3...</b>
0313: * </a>
0314: * Old behavior would be to for column 'b' to have value "...3...".
0315: * New behavior (only if you set b's type to 'OBJECT' and set the GMLReader to processMultiItems as lists)
0316: * <a><b>...1...</b></a> --> b get the string "b" (as before)
0317: * <a><b>...1...</b><b>...2...</b><b>...3...</b></a> --> 'b' is a list of String ['...1...','...2...','...3...']
0318: */
0319: public void processMultiItems(boolean accept) {
0320: multiItemsAsLists = accept;
0321: }
0322:
0323: /**
0324: * Attach a GMLInputTemplate information class.
0325: *
0326: *@param template The new inputTemplate value
0327: */
0328: public void setInputTemplate(GMLInputTemplate template) {
0329: GMLinput = template;
0330: }
0331:
0332: /**
0333: * SAX handler - store and accumulate tag bodies
0334: *
0335: *@param ch Description of the Parameter
0336: *@param start Description of the Parameter
0337: *@param length Description of the Parameter
0338: *@exception SAXException Description of the Exception
0339: */
0340: public void characters(char[] ch, int start, int length)
0341: throws SAXException {
0342: try {
0343: tagBody.append(ch, start, length);
0344: } catch (Exception e) {
0345: throw new SAXException(e.getMessage());
0346: }
0347: }
0348:
0349: /**
0350: * SAX HANDLER - move to state 0
0351: */
0352: public void endDocument() {
0353: //System.out.println("End document");
0354: STATE = STATE_INIT;
0355: }
0356:
0357: /**
0358: * SAX handler - handle state information and transitions based on ending
0359: * elements Most of the work of the parser is done here.
0360: *
0361: *@param uri Description of the Parameter
0362: *@param name Description of the Parameter
0363: *@param qName Description of the Parameter
0364: *@exception SAXException Description of the Exception
0365: */
0366: public void endElement(String uri, String name, String qName)
0367: throws SAXException {
0368: try {
0369: int index;
0370:
0371: // System.out.println("End element: " + qName);
0372: if (STATE == STATE_INIT) {
0373: tagBody = new StringBuffer();
0374:
0375: return; //something wrong
0376: }
0377:
0378: if (STATE > STATE_GET_COLUMNS) {
0379: if (isMultiGeometryTag(qName)) {
0380: if (STATE == STATE_PARSE_GEOM_NESTED) {
0381: STATE = STATE_PARSE_GEOM_SIMPLE; //finished - no action. geometry is correct
0382: } else {
0383: //build the geometry that was in that collection
0384: Geometry g;
0385:
0386: g = geometryFactory.buildGeometry(geometry);
0387: geometry = (ArrayList) recursivegeometry
0388: .get(STATE - STATE_PARSE_GEOM_NESTED
0389: - 1);
0390: geometry.add(g);
0391: recursivegeometry.remove(STATE
0392: - STATE_PARSE_GEOM_NESTED);
0393: g = null;
0394:
0395: STATE--;
0396: }
0397: }
0398:
0399: if (GMLinput.isGeometryElement(qName)) {
0400: tagBody = new StringBuffer();
0401: STATE = STATE_GET_COLUMNS;
0402:
0403: finalGeometry = geometryFactory
0404: .buildGeometry(geometry);
0405:
0406: //System.out.println("end geom: "+finalGeometry.toString() );
0407: currentFeature.setGeometry(finalGeometry);
0408: currentGeometryNumb++;
0409:
0410: return;
0411: }
0412:
0413: //these correspond to <coord><X>0.0</X><Y>0.0</Y></coord>
0414: if ((qName.compareToIgnoreCase("X") == 0)
0415: || (qName.compareToIgnoreCase("gml:X") == 0)) {
0416: singleCoordinate.x = (new Double(tagBody.toString()))
0417: .doubleValue();
0418: } else if ((qName.compareToIgnoreCase("Y") == 0)
0419: || (qName.compareToIgnoreCase("gml:y") == 0)) {
0420: singleCoordinate.y = (new Double(tagBody.toString()))
0421: .doubleValue();
0422: } else if ((qName.compareToIgnoreCase("Z") == 0)
0423: || (qName.compareToIgnoreCase("gml:z") == 0)) {
0424: singleCoordinate.z = (new Double(tagBody.toString()))
0425: .doubleValue();
0426: } else if ((qName.compareToIgnoreCase("COORD") == 0)
0427: || (qName.compareToIgnoreCase("gml:coord") == 0)) {
0428: pointList.add(new Coordinate(singleCoordinate)); //remember it
0429: }
0430: // this corresponds to <gml:coordinates>1195156.78946687,382069.533723461</gml:coordinates>
0431: else if ((qName.compareToIgnoreCase("COORDINATES") == 0)
0432: || (qName
0433: .compareToIgnoreCase("gml:coordinates") == 0)) {
0434: //tagBody has a wack-load of points in it - we need
0435: // to parse them into the pointList list.
0436: // assume that the x,y,z coordinate are "," separated, and the points are " " separated
0437: parsePoints(tagBody.toString(), geometryFactory);
0438: } else if ((qName.compareToIgnoreCase("linearring") == 0)
0439: || (qName.compareToIgnoreCase("gml:linearring") == 0)) {
0440: Coordinate[] c = new Coordinate[0];
0441:
0442: c = (Coordinate[]) pointList.toArray(c);
0443:
0444: //c= (Coordinate[])l;
0445: linearRing = geometryFactory.createLinearRing(c);
0446: } else if ((qName
0447: .compareToIgnoreCase("outerBoundaryIs") == 0)
0448: || (qName
0449: .compareToIgnoreCase("gml:outerBoundaryIs") == 0)) {
0450: outerBoundary = linearRing;
0451: } else if ((qName
0452: .compareToIgnoreCase("innerBoundaryIs") == 0)
0453: || (qName
0454: .compareToIgnoreCase("gml:innerBoundaryIs") == 0)) {
0455: innerBoundaries.add(linearRing);
0456: } else if ((qName.compareToIgnoreCase("polygon") == 0)
0457: || (qName.compareToIgnoreCase("gml:polygon") == 0)) {
0458: //LinearRing[] lrs = new LinearRing[1];
0459: LinearRing[] lrs = new LinearRing[0];
0460:
0461: lrs = (LinearRing[]) innerBoundaries.toArray(lrs);
0462: polygon = geometryFactory.createPolygon(
0463: outerBoundary, lrs);
0464: geometry.add(polygon);
0465: } else if ((qName.compareToIgnoreCase("linestring") == 0)
0466: || (qName.compareToIgnoreCase("gml:linestring") == 0)) {
0467: Coordinate[] c = new Coordinate[0];
0468:
0469: c = (Coordinate[]) pointList.toArray(c);
0470:
0471: lineString = geometryFactory.createLineString(c);
0472: geometry.add(lineString);
0473: } else if ((qName.compareToIgnoreCase("point") == 0)
0474: || (qName.compareToIgnoreCase("gml:point") == 0)) {
0475: apoint = geometryFactory
0476: .createPoint((Coordinate) pointList.get(0));
0477: geometry.add(apoint);
0478: }
0479: } else if (STATE == STATE_GET_COLUMNS) {
0480: if (qName.compareToIgnoreCase(GMLinput.featureTag) == 0) {
0481: tagBody = new StringBuffer();
0482: STATE = STATE_WAIT_FEATURE_TAG;
0483:
0484: //System.out.println("end feature");
0485: //create a feature and put it inside the featurecollection
0486: if (currentFeature.getGeometry() == null) {
0487: Geometry g = currentFeature.getGeometry();
0488:
0489: if (g != null) {
0490: System.out.println(g.toString());
0491: }
0492:
0493: throw new ParseException(
0494: "no geometry specified in feature");
0495: }
0496:
0497: fc.add(currentFeature);
0498: currentFeature = null;
0499:
0500: return;
0501: } else {
0502: //check to see if this was a tag we want to store as a column
0503: //DB: added 2nd check for GML like <a><b></b></a>
0504: // the "b" tag is the "lastStartTag_qName" for "</b>" and "</a>" we only need to
0505: // process it once.
0506: try {
0507: if (((index = GMLinput.match(
0508: lastStartTag_qName, lastStartTag_atts)) > -1)
0509: && (lastStartTag_qName
0510: .equalsIgnoreCase(qName))) {
0511: // System.out.println("value of " + GMLinput.columnName(index)+" : " + GMLinput.getColumnValue(index,tagBody, lastStartTag_atts) );
0512:
0513: // if the column already has a value and multiItems support is turned on
0514: //..and its type ==object
0515: if ((multiItemsAsLists)
0516: && (currentFeature
0517: .getAttribute(GMLinput
0518: .columnName(index)) != null)
0519: && (((ColumnDescription) (GMLinput.columnDefinitions
0520: .get(index))).type == AttributeType.OBJECT)) {
0521: Object oldValue = currentFeature
0522: .getAttribute(GMLinput
0523: .columnName(index));
0524: if (oldValue instanceof List) {
0525: //already a list there - just stuff another thing in!
0526: ((List) oldValue).add(GMLinput
0527: .getColumnValue(index,
0528: tagBody.toString(),
0529: lastStartTag_atts));
0530: } else {
0531: //no list currently there - make a list and replace
0532: List l = new ArrayList();
0533: l.add(oldValue);
0534: l.add(GMLinput.getColumnValue(
0535: index, tagBody.toString(),
0536: lastStartTag_atts)); // new value
0537: currentFeature.setAttribute(
0538: GMLinput.columnName(index),
0539: l);
0540: }
0541: } else // handle normally
0542: {
0543: currentFeature.setAttribute(GMLinput
0544: .columnName(index), GMLinput
0545: .getColumnValue(index, tagBody
0546: .toString(),
0547: lastStartTag_atts));
0548: }
0549: }
0550: } catch (Exception e) {
0551: //dont actually do anything with the parse problem - just ignore it,
0552: // we cannot send it back because the function its overiding doesnt allow
0553: e.printStackTrace();
0554: }
0555:
0556: tagBody = new StringBuffer();
0557: }
0558: } else if (STATE == STATE_WAIT_FEATURE_TAG) {
0559: if (qName.compareToIgnoreCase(GMLinput.collectionTag) == 0) {
0560: STATE = STATE_INIT; //finish
0561:
0562: //System.out.println("DONE!");
0563: tagBody = new StringBuffer();
0564:
0565: return;
0566: }
0567: } else if (STATE == STATE_WAIT_COLLECTION_TAG) {
0568: tagBody = new StringBuffer();
0569:
0570: return; //still look for start collection tag
0571: }
0572: } catch (Exception e) {
0573: throw new SAXException(e.getMessage());
0574: }
0575: }
0576:
0577: public void error(SAXParseException exception) throws SAXException {
0578: throw exception;
0579: }
0580:
0581: public void fatalError(SAXParseException exception)
0582: throws SAXException {
0583: throw exception;
0584: }
0585:
0586: /**
0587: * Main Entry - load in a GML file
0588: *
0589: *@param dp Description of the Parameter
0590: *@return Description of the Return Value
0591: *@exception IllegalParametersException Description of the Exception
0592: *@exception Exception Description of the Exception
0593: */
0594: public FeatureCollection read(DriverProperties dp)
0595: throws IllegalParametersException, Exception {
0596: FeatureCollection fc;
0597: GMLInputTemplate gmlTemplate;
0598: String inputFname;
0599: boolean isCompressed;
0600: boolean isCompressed_template;
0601:
0602: isCompressed_template = (dp
0603: .getProperty("CompressedFileTemplate") != null);
0604:
0605: isCompressed = (dp.getProperty("CompressedFile") != null);
0606:
0607: inputFname = dp.getProperty("File");
0608:
0609: if (inputFname == null) {
0610: inputFname = dp.getProperty("DefaultValue");
0611: }
0612:
0613: if (inputFname == null) {
0614: throw new IllegalParametersException(
0615: "call to GMLReader.read() has DataProperties w/o a InputFile specified");
0616: }
0617:
0618: if (dp.getProperty("TemplateFile") == null) {
0619: // load from .gml file
0620: if (isCompressed) {
0621: InputStream in = CompressedFile.openFile(inputFname, dp
0622: .getProperty("CompressedFile"));
0623: gmlTemplate = inputTemplateFromFile(in);
0624: in.close();
0625: } else {
0626: gmlTemplate = inputTemplateFromFile(inputFname);
0627: }
0628: } else {
0629: //template file specified
0630: if (isCompressed_template) {
0631: InputStream in = CompressedFile.openFile(dp
0632: .getProperty("TemplateFile"), dp
0633: .getProperty("CompressedFileTemplate"));
0634: gmlTemplate = inputTemplateFromFile(in);
0635: in.close();
0636: } else {
0637: if (isCompressed) //special case if the .gml file is compressed, and a template file is specified
0638: {
0639: if (dp.getProperty("CompressedFile").equals(
0640: dp.getProperty("TemplateFile"))) //the template file is the compressed file
0641: {
0642: InputStream in = CompressedFile.openFile(
0643: inputFname, dp
0644: .getProperty("CompressedFile"));
0645: gmlTemplate = inputTemplateFromFile(in);
0646: in.close();
0647: } else {
0648: gmlTemplate = inputTemplateFromFile(dp
0649: .getProperty("TemplateFile"));
0650: }
0651: } else {
0652: //normal load
0653: gmlTemplate = inputTemplateFromFile(dp
0654: .getProperty("TemplateFile"));
0655: }
0656: }
0657: }
0658:
0659: java.io.Reader r;
0660:
0661: this .setInputTemplate(gmlTemplate);
0662:
0663: if (isCompressed) {
0664: r = new BufferedReader(new InputStreamReader(CompressedFile
0665: .openFile(inputFname, dp
0666: .getProperty("CompressedFile"))));
0667: } else {
0668: r = new BufferedReader(new FileReader(inputFname));
0669: }
0670:
0671: fc = read(r, inputFname);
0672: r.close();
0673:
0674: return fc;
0675: }
0676:
0677: /**
0678: * Helper function - calls read(java.io.Reader r,String readerName) with the
0679: * readerName "Unknown Stream". You should have already called
0680: * setInputTempalate().
0681: *
0682: *@param r reader to read the GML File from
0683: *@return Description of the Return Value
0684: *@exception Exception Description of the Exception
0685: */
0686: public FeatureCollection read(java.io.Reader r) throws Exception {
0687: return read(r, "Unknown Stream");
0688: }
0689:
0690: /**
0691: * Main function to read a GML file. You should have already called
0692: * setInputTempalate().
0693: *
0694: *@param r reader to read the GML File from
0695: *@param readerName what to call the reader for error reporting
0696: *@return Description of the Return Value
0697: *@exception Exception Description of the Exception
0698: */
0699: public FeatureCollection read(java.io.Reader r, String readerName)
0700: throws Exception {
0701: LineNumberReader myReader = new LineNumberReader(r);
0702:
0703: if (GMLinput == null) {
0704: throw new ParseException(
0705: "you must set the GMLinput template first!");
0706: }
0707:
0708: streamName = readerName;
0709:
0710: fcmd = GMLinput.toFeatureSchema();
0711: fc = new FeatureDataset(fcmd);
0712:
0713: try {
0714: xr.parse(new InputSource(myReader));
0715: } catch (SAXParseException e) {
0716: throw new ParseException(e.getMessage()
0717: + " Last Opened Tag: " + lastStartTag_qName
0718: + ". Reader reports last line read as "
0719: + myReader.getLineNumber(), streamName + " - "
0720: + e.getPublicId() + " (" + e.getSystemId() + ") ",
0721: e.getLineNumber(), e.getColumnNumber());
0722: } catch (SAXException e) {
0723: throw new ParseException(e.getMessage()
0724: + " Last Opened Tag: " + lastStartTag_qName,
0725: streamName, myReader.getLineNumber(), 0);
0726: }
0727:
0728: return fc;
0729: }
0730:
0731: ////////////////////////////////////////////////////////////////////
0732: // Event handlers.
0733: ////////////////////////////////////////////////////////////////////
0734:
0735: /**
0736: * SAX handler - move to state 1
0737: */
0738: public void startDocument() {
0739: //System.out.println("Start document");
0740: tagBody = new StringBuffer();
0741: STATE = STATE_WAIT_COLLECTION_TAG;
0742: }
0743:
0744: /**
0745: * SAX handler. Handle state and state transitions based on an element
0746: * starting
0747: *
0748: *@param uri Description of the Parameter
0749: *@param name Description of the Parameter
0750: *@param qName Description of the Parameter
0751: *@param atts Description of the Parameter
0752: *@exception SAXException Description of the Exception
0753: */
0754: public void startElement(String uri, String name, String qName,
0755: Attributes atts) throws SAXException {
0756: try {
0757: //System.out.println("Start element: " + qName);
0758: tagBody = new StringBuffer();
0759: lastStartTag_uri = uri;
0760: lastStartTag_name = name;
0761: lastStartTag_qName = qName;
0762: lastStartTag_atts = atts;
0763:
0764: if (STATE == STATE_INIT) {
0765: return; //something wrong
0766: }
0767:
0768: if ((STATE == STATE_WAIT_COLLECTION_TAG)
0769: && (qName
0770: .compareToIgnoreCase(GMLinput.collectionTag) == 0)) {
0771: //found the collection tag
0772: // System.out.println("found collection");
0773: STATE = STATE_WAIT_FEATURE_TAG;
0774:
0775: return;
0776: }
0777:
0778: if ((STATE == STATE_WAIT_FEATURE_TAG)
0779: && (qName.compareToIgnoreCase(GMLinput.featureTag) == 0)) {
0780: //found the feature tag
0781: //System.out.println("found feature");
0782: currentFeature = new BasicFeature(fcmd);
0783: STATE = STATE_GET_COLUMNS;
0784: SRID = 0;// default SRID (reset for each feature, but should be constant for a featurecollection)
0785: if (geometryFactory.getSRID() != SRID)
0786: geometryFactory = new GeometryFactory(
0787: new PrecisionModel(), SRID);
0788:
0789: return;
0790: }
0791:
0792: if ((STATE == STATE_GET_COLUMNS)
0793: && GMLinput.isGeometryElement(qName)) {
0794: //found the geom tag
0795: // System.out.println("found geom #"+currentGeometryNumb );
0796: recursivegeometry = new ArrayList();
0797: geometry = new ArrayList();
0798: recursivegeometry.add(geometry);
0799:
0800: // recursivegeometry[0] = geometry
0801: finalGeometry = null;
0802: STATE = STATE_PARSE_GEOM_SIMPLE;
0803:
0804: return;
0805: }
0806:
0807: if (parseSRID && (STATE >= STATE_PARSE_GEOM_SIMPLE)
0808: && isGeometryTag(qName)) {
0809: //System.out.println("src="+atts.getValue("srsName"));
0810: //System.out.println("srid="+ parseSRID(atts.getValue("srsName")));
0811:
0812: int newSRID = parseSRID(atts.getValue("srsName"));
0813: //NOTE: if parseSRID it usually means that there was an error parsing
0814: // but, it could actually be specified as 'EPGS:0'. Thats not
0815: // a problem because we've already defaulted to srid 0.
0816: if (newSRID != 0) {
0817: SRID = newSRID;
0818: if (geometryFactory.getSRID() != SRID)
0819: geometryFactory = new GeometryFactory(
0820: new PrecisionModel(), SRID);
0821: }
0822: }
0823:
0824: if ((STATE >= STATE_PARSE_GEOM_SIMPLE)
0825: && ((qName.compareToIgnoreCase("coord") == 0) || (qName
0826: .compareToIgnoreCase("gml:coord") == 0))) {
0827: singleCoordinate.x = Double.NaN;
0828: singleCoordinate.y = Double.NaN;
0829: singleCoordinate.z = Double.NaN;
0830: }
0831:
0832: if ((STATE >= STATE_PARSE_GEOM_SIMPLE)
0833: && (!((qName.compareToIgnoreCase("X") == 0)
0834: || (qName.compareToIgnoreCase("gml:x") == 0)
0835: || (qName.compareToIgnoreCase("y") == 0)
0836: || (qName.compareToIgnoreCase("gml:y") == 0)
0837: || (qName.compareToIgnoreCase("z") == 0)
0838: || (qName.compareToIgnoreCase("gml:z") == 0)
0839: || (qName.compareToIgnoreCase("coord") == 0) || (qName
0840: .compareToIgnoreCase("gml:coord") == 0)))) {
0841: pointList.clear(); //clear out any accumulated points
0842: }
0843:
0844: if ((STATE >= STATE_PARSE_GEOM_SIMPLE)
0845: && ((qName.compareToIgnoreCase("polygon") == 0) || (qName
0846: .compareToIgnoreCase("gml:polygon") == 0))) {
0847: innerBoundaries.clear(); //polygon just started - clear out the last one
0848: }
0849:
0850: if ((STATE > STATE_GET_COLUMNS)
0851: && (isMultiGeometryTag(qName))) {
0852: //in state 4 or a 1000 state and found a start GC (or Multi-geom) event
0853: if (STATE == STATE_PARSE_GEOM_SIMPLE) {
0854: // geometry already = recursivegeometry[0]
0855: STATE = STATE_PARSE_GEOM_NESTED;
0856: } else {
0857: STATE++;
0858: geometry = new ArrayList();
0859: recursivegeometry.add(geometry);
0860: }
0861: }
0862: } catch (Exception e) {
0863: throw new SAXException(e.getMessage());
0864: }
0865: }
0866:
0867: ////////////////////////////////////////////////////////////////////
0868: // Error handlers.
0869: ////////////////////////////////////////////////////////////////////
0870: public void warning(SAXParseException exception)
0871: throws SAXException {
0872: throw exception;
0873: }
0874:
0875: /**
0876: * returns true if the the string represents a geometry type
0877: * ie. "gml:linestring" or "linestring"
0878: *
0879: *@param s Description of the Parameter
0880: *@return true if this is a geometry tag
0881: */
0882: private boolean isGeometryTag(String s) {
0883: //remove the "gml:" if its there
0884: if ((s.length() > 5)
0885: && (s.substring(0, 4).compareToIgnoreCase("gml:") == 0)) {
0886: s = s.substring(4);
0887: }
0888:
0889: if ((s.compareToIgnoreCase("multigeometry") == 0)
0890: || (s.compareToIgnoreCase("multipoint") == 0)
0891: || (s.compareToIgnoreCase("multilinestring") == 0)
0892: || (s.compareToIgnoreCase("multipolygon") == 0)
0893: || (s.compareToIgnoreCase("polygon") == 0)
0894: || (s.compareToIgnoreCase("linestring") == 0)
0895: || (s.compareToIgnoreCase("point") == 0)
0896: || (s.compareToIgnoreCase("geometrycollection") == 0)) {
0897: return true;
0898: }
0899:
0900: return false;
0901: }
0902:
0903: /**
0904: * returns true if the the string represents a multi* geometry type
0905: *
0906: *@param s Description of the Parameter
0907: *@return The multiGeometryTag value
0908: */
0909: private boolean isMultiGeometryTag(String s) {
0910: //remove the "gml:" if its there
0911: if ((s.length() > 5)
0912: && (s.substring(0, 4).compareToIgnoreCase("gml:") == 0)) {
0913: s = s.substring(4);
0914: }
0915:
0916: if ((s.compareToIgnoreCase("multigeometry") == 0)
0917: || (s.compareToIgnoreCase("multipoint") == 0)
0918: || (s.compareToIgnoreCase("multilinestring") == 0)
0919: || (s.compareToIgnoreCase("multipolygon") == 0)) {
0920: return true;
0921: }
0922:
0923: return false;
0924: }
0925:
0926: private GMLInputTemplate inputTemplateFromFile(InputStream in)
0927: throws ParseException, FileNotFoundException, IOException {
0928: GMLInputTemplate result;
0929: java.io.Reader r = new BufferedReader(new InputStreamReader(in));
0930: result = inputTemplate(r);
0931: r.close();
0932:
0933: return result;
0934: }
0935:
0936: private GMLInputTemplate inputTemplateFromFile(String filename)
0937: throws ParseException, FileNotFoundException, IOException {
0938: GMLInputTemplate result;
0939: java.io.Reader r = new BufferedReader(new FileReader(filename));
0940: result = inputTemplate(r);
0941: r.close();
0942:
0943: return result;
0944: }
0945:
0946: /**
0947: * Parse a bunch of points - stick them in pointList. Handles 2d and 3d.
0948: *
0949: *@param ptString string containing a bunch of coordinates
0950: *@param geometryFactory JTS point/coordinate factory
0951: */
0952: private void parsePoints(String ptString,
0953: GeometryFactory geometryFactory) {
0954: String aPoint;
0955: StringTokenizer stokenizerPoint;
0956: Coordinate coord = new Coordinate();
0957: int dim;
0958: String numb;
0959: StringBuffer sb;
0960: int t;
0961: char ch;
0962:
0963: //remove \n and \r and replace with spaces
0964: sb = new StringBuffer(ptString);
0965:
0966: for (t = 0; t < sb.length(); t++) {
0967: ch = sb.charAt(t);
0968:
0969: if ((ch == '\n') || (ch == '\r')) {
0970: sb.setCharAt(t, ' ');
0971: }
0972: }
0973:
0974: StringTokenizer stokenizer = new StringTokenizer(
0975: new String(sb), " ", false);
0976:
0977: while (stokenizer.hasMoreElements()) {
0978: //have a point in memory - handle the single point
0979: aPoint = stokenizer.nextToken();
0980: stokenizerPoint = new StringTokenizer(aPoint, ",", false);
0981: coord.x = coord.y = coord.z = Double.NaN;
0982: dim = 0;
0983:
0984: while (stokenizerPoint.hasMoreElements()) {
0985: numb = stokenizerPoint.nextToken();
0986:
0987: if (dim == 0) {
0988: coord.x = Double.parseDouble(numb);
0989: } else if (dim == 1) {
0990: coord.y = Double.parseDouble(numb);
0991: } else if (dim == 2) {
0992: coord.z = Double.parseDouble(numb);
0993: }
0994:
0995: dim++;
0996: }
0997: if ((coord.x != coord.x) || (coord.y != coord.y)) //one (x,y) is NaN
0998: {
0999: throw new IllegalArgumentException(
1000: "GML error - coordinate list isnt valid GML. Watch your spaces and commas!");
1001: }
1002: pointList.add(coord); //remember it
1003: coord = new Coordinate();
1004: stokenizerPoint = null;
1005: }
1006: }
1007:
1008: private GMLInputTemplate inputTemplate(java.io.Reader r)
1009: throws IOException, ParseException {
1010: GMLInputTemplate gmlTemplate = new GMLInputTemplate();
1011: gmlTemplate.load(r);
1012: r.close();
1013:
1014: if (!(gmlTemplate.loaded)) {
1015: throw new ParseException(
1016: "Failed to load GML input template");
1017: }
1018:
1019: return gmlTemplate;
1020: }
1021:
1022: /**
1023: * parses the given srs text and returns the SRID
1024: * @param srsName srsName of the type "EPSG:<number>"
1025: * @return srid or 0 if there is a problem
1026: */
1027: private int parseSRID(String srsName) {
1028: try {
1029: int semicolonLoc = srsName.lastIndexOf(':');
1030: if (semicolonLoc == -1)
1031: return 0;
1032: srsName = srsName.substring(semicolonLoc + 1).trim();
1033: return Integer.parseInt(srsName);
1034: } catch (Exception e) {
1035: return 0;
1036: }
1037: }
1038: }
|