/*
* The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
* for visualizing and manipulating spatial features with geometry and attributes.
*
* Copyright (C) 2003 Vivid Solutions
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* For more information, contact:
*
* Vivid Solutions
* Suite #1A
* 2328 Government Street
* Victoria BC V8T 5G5
* Canada
*
* (250)385-6040
* www.vividsolutions.com
*/
package com.vividsolutions.jump.io;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jump.feature.*;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import java.io.*;
import java.util.List;
import java.util.ArrayList;
import java.util.StringTokenizer;
/**
* GMLReader is a {@link JUMPReader} specialized to read GML files.
*
* <p>
* DataProperties for the JCSReader load(DataProperties) interface:
* </p>
* <p>
* <table border='1' cellspacing='0' cellpadding='4'>
* <tr>
* <th>Parameter</th>
* <th>Meaning</th>
* </tr>
* <tr>
* <td>
* File or DefaultValue
* </td>
*
* <td>
* File name for the input .xml file
* </td>
* </tr>
*
* <tr>
* <td>
* InputTemplateFile
* </td>
* <td>
* Filename for the GMLInputTemplate .xml file
* </td>
* </tr>
*
* <tr>
* <td>CompressedFile</td>
* <td>
* File name (a .zip or .gz) with a .jml/.xml/.gml inside
* (specified by File)
* </td>
* </tr>
*
* <tr>
* <td>
* CompressedFileTemplate</td><td>File name (.zip or .gz)
* with the input template in (specified by InputTemplateFile)
* </td>
* </tr>
* </table>
* </p>
*
* <br>
* NOTE: If InputTemplateFile is unspecified, GMLReader will try to read one
* off the top of the input .xml file (the JCS format). This is the same as
* specifying the File and TemplateFile to be the same. <br>
* <br>
*
* Typically, you would write:<br>
*
* <pre>
* gmlReader = new GMLReader();
* gmlReader.load( DriverProperties) ; // has InputFile and InputTemplate
* </pre>
* or:
* <pre>
* gmlReader.setInputTemplate( GMLInputTemplate);
* gmlReader.load( <Reader> , <stream name> );
* </pre>
* <br>
* <br>
* Internal Details - This is based on a small finite state machine with these
* states: <br>
* <br>
* STATE MEANING <br>
* 0 Init <br>
* 1 Waiting for Collection tag <br>
* 2 Waiting for Feature tag <br>
* 3 Getting jcs columns <br>
* 4 Parsing geometry (goes back to state 3) <br>
* 1000 Parsing Multi-geometry, recursion level =1 <br>
* 1001 Parsing Multi-geometry, recursion level =2 <br>
* ... <br>
* <br>
* <br>
* State Diagram <br>
* <br>
* init <br>
* <PRE>
* 0 -----> 1
* |
* | Collection start Tag
* |
* -->2----------------> FINISH
* \ | End Collection tag
* End Feature tag \ |
* \|
* 4<-------->3
* Geometry start/end
*</PRE>
* <br>
* For multi-geometries <br>
* On start Multi-geometry, increment state by 1 (or jump to 1000 if at state
* 4) <br>
* make sure recursivegeometry[state-1000] is null <br>
* <put any object into the recursivegeometry[state-1000] collection>
*
* <br>
* <br>
* on end multi-geometry, <br>
* build geometry in recursivegeometry[state-1000], add it to
* recursivegeometry[state-1000-1] <br>
* state= state-1 <br>
* <br>
* For single geometries - they will be stuck into recursivegeometry[0], which
* is the same <br>
* as geometry <br>
* <br>
* For multi geometries - they will also be stuck into recursivegeometry[0],
* which is the same <br>
* as geometry. But, for the first nested geometry, it will be stuck into
* recursivegeometry[1], <br>
* which will then be geometry <br>
* <pre>
* example of double GCs:
* START geometry ->move to state 4
* START TAG: multi* -> move to state 1000, geometry = recursivegeometry[0]
* <POINT>
*
* -> added to geometry <POINT>
*
* -> added to geometry START TAG: multi* -> move to state 1001, geometry =
* recursivegeometry[1] <POINT>
*
* -> added to geometry <POINT>
*
* -> added to geometry END TAG: multi -> move to state 1000, build geometry in
* recursivegeometry[1], add to recursivegeometry[0], geometry =
* recursivegeometry[0] <POINT>
*
* -> added to geometry END TAG: multi -> <finished> move to state 4, build
* geometry in recursivegeometry[0] (thats the result) and put it in
* finalGeometry END geometry -> add to feature collection example of simple
* geometry: START geometry ->move to state 4 BEGIN polygon ->clear out inner
* ring accumulator BEGIN outerboundary BEGIN linearring END linearring -> put
* points in linearRing END outerboundary -> put linearRing in outerBoundary
* BEGIN innerboundary BEGIN linearring END linearring -> put points in
* linearRing END innerboundary -> add linearRing to innerBoundary list END
* polygon -> build polygon (put in geometry, which is recursivegeometry[0] END
* geometry => add to feature collection
* </pre>
*
* Most of the work is done in the endTag method!
* <br>
* <br>
* New additions: Jan 2005 by Dave Blasby
* allow srid to be parsed from the GML file
* For example:
* <gml:LineString srsName="EPSG:42102">
* ....
* </gml:LineString>
* The SRID of the created geometry will be 42102.
* It accepts srsNames of the form "<letters>:<number>".
* ie. "EPSG:111" or "DAVE:222" or "BCGOV:333" etc...
* The Geometry's SRID will be the number.
* If you have a GEOMETRYCOLLECTION with more than one SRID specified
* the SRID of the result will be indeterminate (this isnt correct GML).
*
* Geometries without a srsName will get SRID 0.
*
* This functionality defaults to off for compatibility.
* To turn it on or off, call the acceptSRID(true|false) function.
*
* New Addition: Jan, 2005by Dave Blasby
* Added slightly better support for type=OBJECT. It sticks a String in. Before it would probably throw an error.
* Added support for multi-objects for example:
* <a>
* <b>...1...</b>
* <b>...2...</b>
* <b>...3...</b>
* </a>
* Old behavior would be to for column 'b' to have value "...3...".
* New behavior (only if you set b's type to 'OBJECT' and set the GMLReader to processMultiItems as lists)
* <a><b>...1...</b></a> --> b get the string "...1..." (as before)
* <a><b>...1...</b><b>...2...</b><b>...3...</b></a> --> 'b' is a list of String ['...1...','...2...','...3...']
*
*/
public class GMLReader extends DefaultHandler implements JUMPReader {
static int STATE_GET_COLUMNS = 3;
/**
* STATE MEANING <br>
* 0 Init <br>
* 1 Waiting for Collection tag <br>
* 2 Waiting for Feature tag <br>
* 3 Getting jcs columns <br>
* 4 Parsing geometry (goes back to state 3) <br>
* 1000 Parsing Multi-geometry, recursion level =1 <br>
* 1001 Parsing Multi-geometry, recursion level =2 <br>
*/
static int STATE_INIT = 0;
static int STATE_PARSE_GEOM_NESTED = 1000;
static int STATE_PARSE_GEOM_SIMPLE = 4;
static int STATE_WAIT_COLLECTION_TAG = 1;
static int STATE_WAIT_FEATURE_TAG = 2;
GMLInputTemplate GMLinput = null;
int STATE = STATE_INIT; //list of points
Point apoint;
Feature currentFeature;
int currentGeometryNumb = 1;
FeatureCollection fc;
FeatureSchema fcmd; // list of geometries
Geometry finalGeometry; //list of geometrycollections - list of list of geometry
ArrayList geometry;
GeometryFactory geometryFactory = new GeometryFactory(); //this might get replaced if there's an SRID change
ArrayList innerBoundaries = new ArrayList();
Attributes lastStartTag_atts;
String lastStartTag_name;
String lastStartTag_qName; //accumulate values inside a tag
// info about the last start tag encountered
String lastStartTag_uri;
LineString lineString;
LinearRing linearRing; // a LR
LinearRing outerBoundary; //list of LinearRing
ArrayList pointList = new ArrayList(); // list of accumulated points (Coordinate)
Polygon polygon; // polygon
// higherlevel geomery object
ArrayList recursivegeometry = new ArrayList();
// low-level geometry objects
Coordinate singleCoordinate = new Coordinate();
String streamName; //result geometry -
StringBuffer tagBody;
XMLReader xr; //see above
int SRID =0; // srid to give the created geometries
public boolean parseSRID = false ; //true = put SRID for srsName="EPSG:42102"
/**
* true => for 'OBJECT' types, if you find more than 1 item, make a list and store all the results
*/
public boolean multiItemsAsLists = false;
/**
* Constructor - make a SAXParser and have this GMLReader be its
* ContentHandler and ErrorHandler.
*/
public GMLReader() {
super();
xr = new org.apache.xerces.parsers.SAXParser();
xr.setContentHandler(this);
xr.setErrorHandler(this);
}
/**
* parse SRID information in geometry tags
* @param parseTheSRID true = parse
*/
public void acceptSRID(boolean parseTheSRID)
{
parseSRID =parseTheSRID;
}
/**
* Added slightly better support for type=OBJECT. It sticks a String in. Before it would probably throw an error.
* Added support for multi-objects for example:
* <a>
* <b>...1...</b>
* <b>...2...</b>
* <b>...3...</b>
* </a>
* Old behavior would be to for column 'b' to have value "...3...".
* New behavior (only if you set b's type to 'OBJECT' and set the GMLReader to processMultiItems as lists)
* <a><b>...1...</b></a> --> b get the string "b" (as before)
* <a><b>...1...</b><b>...2...</b><b>...3...</b></a> --> 'b' is a list of String ['...1...','...2...','...3...']
*/
public void processMultiItems(boolean accept)
{
multiItemsAsLists=accept;
}
/**
* Attach a GMLInputTemplate information class.
*
*@param template The new inputTemplate value
*/
public void setInputTemplate(GMLInputTemplate template) {
GMLinput = template;
}
/**
* SAX handler - store and accumulate tag bodies
*
*@param ch Description of the Parameter
*@param start Description of the Parameter
*@param length Description of the Parameter
*@exception SAXException Description of the Exception
*/
public void characters(char[] ch, int start, int length)
throws SAXException {
try {
tagBody.append(ch,start,length);
} catch (Exception e) {
throw new SAXException(e.getMessage());
}
}
/**
* SAX HANDLER - move to state 0
*/
public void endDocument() {
//System.out.println("End document");
STATE = STATE_INIT;
}
/**
* SAX handler - handle state information and transitions based on ending
* elements Most of the work of the parser is done here.
*
*@param uri Description of the Parameter
*@param name Description of the Parameter
*@param qName Description of the Parameter
*@exception SAXException Description of the Exception
*/
public void endElement(String uri, String name, String qName)
throws SAXException {
try {
int index;
// System.out.println("End element: " + qName);
if (STATE == STATE_INIT) {
tagBody = new StringBuffer();
return; //something wrong
}
if (STATE > STATE_GET_COLUMNS) {
if (isMultiGeometryTag(qName)) {
if (STATE == STATE_PARSE_GEOM_NESTED) {
STATE = STATE_PARSE_GEOM_SIMPLE; //finished - no action. geometry is correct
} else {
//build the geometry that was in that collection
Geometry g;
g = geometryFactory.buildGeometry(geometry);
geometry = (ArrayList) recursivegeometry.get(STATE -
STATE_PARSE_GEOM_NESTED - 1);
geometry.add(g);
recursivegeometry.remove(STATE -
STATE_PARSE_GEOM_NESTED);
g = null;
STATE--;
}
}
if (GMLinput.isGeometryElement(qName)) {
tagBody = new StringBuffer();
STATE = STATE_GET_COLUMNS;
//-- [sstein] 14.March.2009
// read LinearRings even if we don't have polygons
if ((linearRing != null) && (polygon == null)){
geometry.add(linearRing);
}
//-- sstein:end
finalGeometry = geometryFactory.buildGeometry(geometry);
//System.out.println("end geom: "+finalGeometry.toString() );
currentFeature.setGeometry(finalGeometry);
currentGeometryNumb++;
return;
}
//System.out.println("geom-element: " + qName);
//these correspond to <coord><X>0.0</X><Y>0.0</Y></coord>
if ((qName.compareToIgnoreCase("X") == 0) ||
(qName.compareToIgnoreCase("gml:X") == 0)) {
singleCoordinate.x = (new Double(tagBody.toString())).doubleValue();
} else if ((qName.compareToIgnoreCase("Y") == 0) ||
(qName.compareToIgnoreCase("gml:y") == 0)) {
singleCoordinate.y = (new Double(tagBody.toString())).doubleValue();
} else if ((qName.compareToIgnoreCase("Z") == 0) ||
(qName.compareToIgnoreCase("gml:z") == 0)) {
singleCoordinate.z = (new Double(tagBody.toString())).doubleValue();
} else if ((qName.compareToIgnoreCase("COORD") == 0) ||
(qName.compareToIgnoreCase("gml:coord") == 0)) {
pointList.add(new Coordinate(singleCoordinate)); //remember it
}
// this corresponds to <gml:coordinates>1195156.78946687,382069.533723461</gml:coordinates>
else if ((qName.compareToIgnoreCase("COORDINATES") == 0) ||
(qName.compareToIgnoreCase("gml:coordinates") == 0)) {
//tagBody has a wack-load of points in it - we need
// to parse them into the pointList list.
// assume that the x,y,z coordinate are "," separated, and the points are " " separated
parsePoints(tagBody.toString(), geometryFactory);
} else if ((qName.compareToIgnoreCase("linearring") == 0) ||
(qName.compareToIgnoreCase("gml:linearring") == 0)) {
Coordinate[] c = new Coordinate[0];
c = (Coordinate[]) pointList.toArray(c);
//c= (Coordinate[])l;
linearRing = geometryFactory.createLinearRing(c);
} else if ((qName.compareToIgnoreCase("outerBoundaryIs") == 0) ||
(qName.compareToIgnoreCase("gml:outerBoundaryIs") == 0)) {
outerBoundary = linearRing;
} else if ((qName.compareToIgnoreCase("innerBoundaryIs") == 0) ||
(qName.compareToIgnoreCase("gml:innerBoundaryIs") == 0)) {
innerBoundaries.add(linearRing);
} else if ((qName.compareToIgnoreCase("polygon") == 0) ||
(qName.compareToIgnoreCase("gml:polygon") == 0)) {
//LinearRing[] lrs = new LinearRing[1];
LinearRing[] lrs = new LinearRing[0];
lrs = (LinearRing[]) innerBoundaries.toArray(lrs);
polygon = geometryFactory.createPolygon(outerBoundary, lrs);
geometry.add(polygon);
} else if ((qName.compareToIgnoreCase("linestring") == 0) ||
(qName.compareToIgnoreCase("gml:linestring") == 0)) {
Coordinate[] c = new Coordinate[0];
c = (Coordinate[]) pointList.toArray(c);
lineString = geometryFactory.createLineString(c);
geometry.add(lineString);
} else if ((qName.compareToIgnoreCase("point") == 0) ||
(qName.compareToIgnoreCase("gml:point") == 0)) {
apoint = geometryFactory.createPoint((Coordinate) pointList.get(
0));
geometry.add(apoint);
}
} else if (STATE == STATE_GET_COLUMNS) {
if (qName.compareToIgnoreCase(GMLinput.featureTag) == 0) {
tagBody = new StringBuffer();
STATE = STATE_WAIT_FEATURE_TAG;
//System.out.println("end feature");
//create a feature and put it inside the featurecollection
if (currentFeature.getGeometry() == null) {
Geometry g = currentFeature.getGeometry();
if (g != null) {
System.out.println(g.toString());
}
throw new ParseException(
"no geometry specified in feature");
}
fc.add(currentFeature);
currentFeature = null;
return;
} else {
//check to see if this was a tag we want to store as a column
//DB: added 2nd check for GML like <a><b></b></a>
// the "b" tag is the "lastStartTag_qName" for "</b>" and "</a>" we only need to
// process it once.
try {
if ( ((index = GMLinput.match(lastStartTag_qName,lastStartTag_atts)) > -1) &&
(lastStartTag_qName.equalsIgnoreCase(qName))
)
{
// System.out.println("value of " + GMLinput.columnName(index)+" : " + GMLinput.getColumnValue(index,tagBody, lastStartTag_atts) );
// if the column already has a value and multiItems support is turned on
//..and its type ==object
if ( ( multiItemsAsLists) && (currentFeature.getAttribute(GMLinput.columnName(index)) != null)
&& (( (ColumnDescription) (GMLinput.columnDefinitions.get(index) )).type == AttributeType.OBJECT))
{
Object oldValue = currentFeature.getAttribute(GMLinput.columnName(index));
if (oldValue instanceof List)
{
//already a list there - just stuff another thing in!
((List)oldValue).add( GMLinput.getColumnValue(index, tagBody.toString(), lastStartTag_atts) );
}
else
{
//no list currently there - make a list and replace
List l = new ArrayList();
l.add(oldValue);
l.add(GMLinput.getColumnValue(index, tagBody.toString(), lastStartTag_atts)); // new value
currentFeature.setAttribute(GMLinput.columnName(index), l );
}
}
else // handle normally
{
currentFeature.setAttribute(GMLinput.columnName(index),
GMLinput.getColumnValue(index, tagBody.toString(), lastStartTag_atts));
}
}
} catch (Exception e) {
//dont actually do anything with the parse problem - just ignore it,
// we cannot send it back because the function its overiding doesnt allow
e.printStackTrace();
}
tagBody = new StringBuffer();
}
} else if (STATE == STATE_WAIT_FEATURE_TAG) {
if (qName.compareToIgnoreCase(GMLinput.collectionTag) == 0) {
STATE = STATE_INIT; //finish
//System.out.println("DONE!");
tagBody = new StringBuffer();
return;
}
} else if (STATE == STATE_WAIT_COLLECTION_TAG) {
tagBody = new StringBuffer();
return; //still look for start collection tag
}
} catch (Exception e) {
throw new SAXException(e.getMessage());
}
}
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
public void fatalError(SAXParseException exception)
throws SAXException {
throw exception;
}
/**
* Main Entry - load in a GML file
*
*@param dp Description of the Parameter
*@return Description of the Return Value
*@exception IllegalParametersException Description of the Exception
*@exception Exception Description of the Exception
*/
public FeatureCollection read(DriverProperties dp)
throws IllegalParametersException, Exception {
FeatureCollection fc;
GMLInputTemplate gmlTemplate;
String inputFname;
boolean isCompressed;
boolean isCompressed_template;
isCompressed_template = (dp.getProperty("CompressedFileTemplate") != null);
isCompressed = (dp.getProperty("CompressedFile") != null);
inputFname = dp.getProperty("File");
if (inputFname == null) {
inputFname = dp.getProperty("DefaultValue");
}
if (inputFname == null) {
throw new IllegalParametersException(
"call to GMLReader.read() has DataProperties w/o a InputFile specified");
}
if (dp.getProperty("TemplateFile") == null) {
// load from .gml file
if (isCompressed) {
InputStream in = CompressedFile.openFile(inputFname,
dp.getProperty("CompressedFile"));
gmlTemplate = inputTemplateFromFile(in);
in.close();
} else {
gmlTemplate = inputTemplateFromFile(inputFname);
}
} else {
//template file specified
if (isCompressed_template) {
InputStream in = CompressedFile.openFile(dp.getProperty(
"TemplateFile"),
dp.getProperty("CompressedFileTemplate"));
gmlTemplate = inputTemplateFromFile(in);
in.close();
} else {
if (isCompressed) //special case if the .gml file is compressed, and a template file is specified
{
if (dp.getProperty("CompressedFile").equals(dp.getProperty(
"TemplateFile"))) //the template file is the compressed file
{
InputStream in = CompressedFile.openFile(inputFname,
dp.getProperty("CompressedFile"));
gmlTemplate = inputTemplateFromFile(in);
in.close();
} else {
gmlTemplate = inputTemplateFromFile(dp.getProperty(
"TemplateFile"));
}
} else {
//normal load
gmlTemplate = inputTemplateFromFile(dp.getProperty(
"TemplateFile"));
}
}
}
java.io.Reader r;
this.setInputTemplate(gmlTemplate);
if (isCompressed) {
r = new BufferedReader(new InputStreamReader(
CompressedFile.openFile(inputFname,
dp.getProperty("CompressedFile"))));
} else {
r = new BufferedReader(new FileReader(inputFname));
}
fc = read(r, inputFname);
r.close();
return fc;
}
/**
* Helper function - calls read(java.io.Reader r,String readerName) with the
* readerName "Unknown Stream". You should have already called
* setInputTempalate().
*
*@param r reader to read the GML File from
*@return Description of the Return Value
*@exception Exception Description of the Exception
*/
public FeatureCollection read(java.io.Reader r) throws Exception {
return read(r, "Unknown Stream");
}
/**
* Main function to read a GML file. You should have already called
* setInputTempalate().
*
*@param r reader to read the GML File from
*@param readerName what to call the reader for error reporting
*@return Description of the Return Value
*@exception Exception Description of the Exception
*/
public FeatureCollection read(java.io.Reader r, String readerName)
throws Exception {
LineNumberReader myReader = new LineNumberReader(r);
if (GMLinput == null) {
throw new ParseException(
"you must set the GMLinput template first!");
}
streamName = readerName;
fcmd = GMLinput.toFeatureSchema();
fc = new FeatureDataset(fcmd);
try {
xr.parse(new InputSource(myReader));
} catch (SAXParseException e) {
throw new ParseException(e.getMessage() + " Last Opened Tag: " +
lastStartTag_qName + ". Reader reports last line read as " +
myReader.getLineNumber(),
streamName + " - " + e.getPublicId() + " (" + e.getSystemId() +
") ", e.getLineNumber(), e.getColumnNumber());
} catch (SAXException e) {
throw new ParseException(e.getMessage() + " Last Opened Tag: " +
lastStartTag_qName, streamName, myReader.getLineNumber(), 0);
}
return fc;
}
////////////////////////////////////////////////////////////////////
// Event handlers.
////////////////////////////////////////////////////////////////////
/**
* SAX handler - move to state 1
*/
public void startDocument() {
//System.out.println("Start document");
tagBody = new StringBuffer();
STATE = STATE_WAIT_COLLECTION_TAG;
}
/**
* SAX handler. Handle state and state transitions based on an element
* starting
*
*@param uri Description of the Parameter
*@param name Description of the Parameter
*@param qName Description of the Parameter
*@param atts Description of the Parameter
*@exception SAXException Description of the Exception
*/
public void startElement(String uri, String name, String qName,
Attributes atts) throws SAXException {
try {
//System.out.println("Start element: " + qName);
tagBody = new StringBuffer();
lastStartTag_uri = uri;
lastStartTag_name = name;
lastStartTag_qName = qName;
lastStartTag_atts = atts;
if (STATE == STATE_INIT) {
return; //something wrong
}
if ((STATE == STATE_WAIT_COLLECTION_TAG) &&
(qName.compareToIgnoreCase(GMLinput.collectionTag) == 0)) {
//found the collection tag
// System.out.println("found collection");
STATE = STATE_WAIT_FEATURE_TAG;
return;
}
if ((STATE == STATE_WAIT_FEATURE_TAG) &&
(qName.compareToIgnoreCase(GMLinput.featureTag) == 0)) {
//found the feature tag
//System.out.println("found feature");
currentFeature = new BasicFeature(fcmd);
STATE = STATE_GET_COLUMNS;
SRID = 0 ;// default SRID (reset for each feature, but should be constant for a featurecollection)
if (geometryFactory.getSRID() != SRID)
geometryFactory = new GeometryFactory(new PrecisionModel(), SRID);
return;
}
if ((STATE == STATE_GET_COLUMNS) &&
GMLinput.isGeometryElement(qName)) {
//found the geom tag
//System.out.println("found geom #"+currentGeometryNumb );
recursivegeometry = new ArrayList();
geometry = new ArrayList();
recursivegeometry.add(geometry);
// recursivegeometry[0] = geometry
finalGeometry = null;
STATE = STATE_PARSE_GEOM_SIMPLE;
return;
}
if (parseSRID && (STATE >= STATE_PARSE_GEOM_SIMPLE) && isGeometryTag(qName) )
{
//System.out.println("src="+atts.getValue("srsName"));
//System.out.println("srid="+ parseSRID(atts.getValue("srsName")));
int newSRID = parseSRID(atts.getValue("srsName"));
//NOTE: if parseSRID it usually means that there was an error parsing
// but, it could actually be specified as 'EPGS:0'. Thats not
// a problem because we've already defaulted to srid 0.
if (newSRID != 0)
{
SRID = newSRID;
if (geometryFactory.getSRID() != SRID)
geometryFactory = new GeometryFactory(new PrecisionModel(), SRID);
}
}
if ((STATE >= STATE_PARSE_GEOM_SIMPLE) &&
((qName.compareToIgnoreCase("coord") == 0) ||
(qName.compareToIgnoreCase("gml:coord") == 0))) {
singleCoordinate.x = Double.NaN;
singleCoordinate.y = Double.NaN;
singleCoordinate.z = Double.NaN;
}
if ((STATE >= STATE_PARSE_GEOM_SIMPLE) &&
(!((qName.compareToIgnoreCase("X") == 0) ||
(qName.compareToIgnoreCase("gml:x") == 0) ||
(qName.compareToIgnoreCase("y") == 0) ||
(qName.compareToIgnoreCase("gml:y") == 0) ||
(qName.compareToIgnoreCase("z") == 0) ||
(qName.compareToIgnoreCase("gml:z") == 0) ||
(qName.compareToIgnoreCase("coord") == 0) ||
(qName.compareToIgnoreCase("gml:coord") == 0)))) {
pointList.clear(); //clear out any accumulated points
}
if ((STATE >= STATE_PARSE_GEOM_SIMPLE) &&
((qName.compareToIgnoreCase("polygon") == 0) ||
(qName.compareToIgnoreCase("gml:polygon") == 0))) {
innerBoundaries.clear(); //polygon just started - clear out the last one
}
if ((STATE > STATE_GET_COLUMNS) && (isMultiGeometryTag(qName))) {
//in state 4 or a 1000 state and found a start GC (or Multi-geom) event
if (STATE == STATE_PARSE_GEOM_SIMPLE) {
// geometry already = recursivegeometry[0]
STATE = STATE_PARSE_GEOM_NESTED;
} else {
STATE++;
geometry = new ArrayList();
recursivegeometry.add(geometry);
}
}
} catch (Exception e) {
throw new SAXException(e.getMessage());
}
}
////////////////////////////////////////////////////////////////////
// Error handlers.
////////////////////////////////////////////////////////////////////
public void warning(SAXParseException exception) throws SAXException {
throw exception;
}
/**
* returns true if the the string represents a geometry type
* ie. "gml:linestring" or "linestring"
*
*@param s Description of the Parameter
*@return true if this is a geometry tag
*/
private boolean isGeometryTag(String s) {
//remove the "gml:" if its there
if ((s.length() > 5) &&
(s.substring(0, 4).compareToIgnoreCase("gml:") == 0)) {
s = s.substring(4);
}
if ( (s.compareToIgnoreCase("multigeometry") == 0) ||
(s.compareToIgnoreCase("multipoint") == 0) ||
(s.compareToIgnoreCase("multilinestring") == 0) ||
(s.compareToIgnoreCase("multipolygon") == 0) ||
(s.compareToIgnoreCase("polygon") == 0) ||
(s.compareToIgnoreCase("linestring") == 0) ||
(s.compareToIgnoreCase("point") == 0)||
(s.compareToIgnoreCase("geometrycollection") == 0)
)
{
return true;
}
return false;
}
/**
* returns true if the the string represents a multi* geometry type
*
*@param s Description of the Parameter
*@return The multiGeometryTag value
*/
private boolean isMultiGeometryTag(String s) {
//remove the "gml:" if its there
if ((s.length() > 5) &&
(s.substring(0, 4).compareToIgnoreCase("gml:") == 0)) {
s = s.substring(4);
}
if ((s.compareToIgnoreCase("multigeometry") == 0) ||
(s.compareToIgnoreCase("multipoint") == 0) ||
(s.compareToIgnoreCase("multilinestring") == 0) ||
(s.compareToIgnoreCase("multipolygon") == 0)) {
return true;
}
return false;
}
private GMLInputTemplate inputTemplateFromFile(InputStream in)
throws ParseException, FileNotFoundException, IOException {
GMLInputTemplate result;
java.io.Reader r = new BufferedReader(new InputStreamReader(in));
result = inputTemplate(r);
r.close();
return result;
}
private GMLInputTemplate inputTemplateFromFile(String filename)
throws ParseException, FileNotFoundException, IOException {
GMLInputTemplate result;
java.io.Reader r = new BufferedReader(new FileReader(filename));
result = inputTemplate(r);
r.close();
return result;
}
/**
* Parse a bunch of points - stick them in pointList. Handles 2d and 3d.
*
*@param ptString string containing a bunch of coordinates
*@param geometryFactory JTS point/coordinate factory
*/
private void parsePoints(String ptString, GeometryFactory geometryFactory) {
String aPoint;
StringTokenizer stokenizerPoint;
Coordinate coord = new Coordinate();
int dim;
String numb;
StringBuffer sb;
int t;
char ch;
//remove \n and \r and replace with spaces
sb = new StringBuffer(ptString);
for (t = 0; t < sb.length(); t++) {
ch = sb.charAt(t);
if ((ch == '\n') || (ch == '\r')) {
sb.setCharAt(t, ' ');
}
}
StringTokenizer stokenizer = new StringTokenizer(new String(sb), " ",
false);
while (stokenizer.hasMoreElements()) {
//have a point in memory - handle the single point
aPoint = stokenizer.nextToken();
stokenizerPoint = new StringTokenizer(aPoint, ",", false);
coord.x = coord.y = coord.z = Double.NaN;
dim = 0;
while (stokenizerPoint.hasMoreElements()) {
numb = stokenizerPoint.nextToken();
if (dim == 0) {
coord.x = Double.parseDouble(numb);
} else if (dim == 1) {
coord.y = Double.parseDouble(numb);
} else if (dim == 2) {
coord.z = Double.parseDouble(numb);
}
dim++;
}
if ( (coord.x != coord.x) || (coord.y != coord.y) ) //one (x,y) is NaN
{
throw new IllegalArgumentException("GML error - coordinate list isnt valid GML. Watch your spaces and commas!");
}
pointList.add(coord); //remember it
coord = new Coordinate();
stokenizerPoint = null;
}
}
private GMLInputTemplate inputTemplate(java.io.Reader r)
throws IOException, ParseException {
GMLInputTemplate gmlTemplate = new GMLInputTemplate();
gmlTemplate.load(r);
r.close();
if (!(gmlTemplate.loaded)) {
throw new ParseException("Failed to load GML input template");
}
return gmlTemplate;
}
/**
* parses the given srs text and returns the SRID
* @param srsName srsName of the type "EPSG:<number>"
* @return srid or 0 if there is a problem
*/
private int parseSRID(String srsName)
{
try{
int semicolonLoc = srsName.lastIndexOf(':');
if (semicolonLoc == -1)
return 0;
srsName = srsName.substring(semicolonLoc+1).trim();
return Integer.parseInt(srsName);
}
catch (Exception e)
{
return 0;
}
}
}