// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugins.osmrec.parsers; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.parsers.ParserConfigurationException; import org.geotools.geometry.jts.JTS; import org.geotools.referencing.CRS; import org.geotools.referencing.crs.DefaultGeocentricCRS; import org.geotools.referencing.crs.DefaultGeographicCRS; import org.opengis.geometry.MismatchedDimensionException; import org.opengis.referencing.FactoryException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.TransformException; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.plugins.osmrec.container.OSMNode; import org.openstreetmap.josm.plugins.osmrec.container.OSMRelation; import org.openstreetmap.josm.plugins.osmrec.container.OSMWay; import org.openstreetmap.josm.tools.Utils; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.LinearRing; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; /** * Parses OSM xml file and constructs additional nodes of the OSM map into appropriate objects with attributes. * * @author imis-nkarag */ public class OSMParser extends DefaultHandler { //private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(OSMParser.class); //change from wgs84 to cartesian for later processing of the geometry private static final CoordinateReferenceSystem sourceCRS = DefaultGeographicCRS.WGS84; private static final CoordinateReferenceSystem targetCRS = DefaultGeocentricCRS.CARTESIAN; private final GeometryFactory geometryFactory = new GeometryFactory(); private static MathTransform transform = null; private final List<OSMNode> nodeList; //will be populated with nodes private final List<OSMRelation> relationList; private final Map<String, OSMNode> nodesWithIDs; //map containing IDs as Strings and the corresponding OSMNode objects private final List<OSMWay> wayList; //populated with ways of the OSM file private final String osmXmlFileName; private OSMNode nodeTmp; //variable to hold the node object private OSMWay wayTmp; //variable to hold the way object private OSMRelation relationTmp; private boolean inWay = false; //when parser is in a way node becomes true in order to track the parser position private boolean inNode = false; //becomes true when the parser is in a simple node private boolean inRelation = false; //becomes true when the parser is in a relarion node public OSMParser(String osmXmlFileName) { this.osmXmlFileName = osmXmlFileName; nodeList = new ArrayList<>(); wayList = new ArrayList<>(); relationList = new ArrayList<>(); nodesWithIDs = new HashMap<>(); try { transform = CRS.findMathTransform(sourceCRS, targetCRS, true); } catch (FactoryException ex) { Logger.getLogger(OSMParser.class.getName()).log(Level.SEVERE, null, ex); } } public void parseDocument() { try { Utils.newSafeSAXParser().parse(osmXmlFileName, this); } catch (ParserConfigurationException | SAXException | IOException e) { Main.error(e); } } @Override public void startElement(String s, String s1, String elementName, Attributes attributes) throws SAXException { // if current element is an OSMNode , create new node and populate with the appropriate values if (elementName.equalsIgnoreCase("node")) { nodeTmp = new OSMNode(); nodeTmp.setID(attributes.getValue("id")); nodeTmp.setUser(attributes.getValue("user")); //parse geometry double longitude = Double.parseDouble(attributes.getValue("lon")); double latitude = Double.parseDouble(attributes.getValue("lat")); Coordinate targetGeometry = null; Coordinate sourceCoordinate = new Coordinate(longitude, latitude); try { targetGeometry = JTS.transform(sourceCoordinate, null, transform); } catch (MismatchedDimensionException | TransformException ex) { Logger.getLogger(OSMParser.class.getName()).log(Level.SEVERE, null, ex); } //create geometry object Geometry geom = geometryFactory.createPoint(new Coordinate(targetGeometry)); nodeTmp.setGeometry(geom); inNode = true; inWay = false; inRelation = false; } else if (elementName.equalsIgnoreCase("way")) { wayTmp = new OSMWay(); wayTmp.setID(attributes.getValue("id")); if (attributes.getValue("user") != null) { wayTmp.setUser(attributes.getValue("user")); } else { wayTmp.setUser("undefined"); } inWay = true; inNode = false; inRelation = false; } else if (elementName.equalsIgnoreCase("relation")) { relationTmp = new OSMRelation(); relationTmp.setID(attributes.getValue("id")); inRelation = true; inWay = false; inNode = false; } else if (elementName.equalsIgnoreCase("nd")) { wayTmp.addNodeReference(attributes.getValue("ref")); } else if (elementName.equalsIgnoreCase("tag")) { if (inNode) { //if the path is in an OSMNode set tagKey and value to the corresponding node nodeTmp.setTagKeyValue(attributes.getValue("k"), attributes.getValue("v")); } else if (inWay) { //else if the path is in an OSM way set tagKey and value to the corresponding way wayTmp.setTagKeyValue(attributes.getValue("k"), attributes.getValue("v")); } else if (inRelation) { //set the key-value pairs of relation tags relationTmp.setTagKeyValue(attributes.getValue("k"), attributes.getValue("v")); } } else if (elementName.equalsIgnoreCase("member")) { relationTmp.addMemberReference(attributes.getValue("ref")); } } @Override public void endElement(String s, String s1, String element) throws SAXException { // if end of node element, add to appropriate list if (element.equalsIgnoreCase("node")) { nodeList.add(nodeTmp); nodesWithIDs.put(nodeTmp.getID(), nodeTmp); } if (element.equalsIgnoreCase("way")) { //construct the Way geometry from each node of the node references List<String> references = wayTmp.getNodeReferences(); for (String entry: references) { Geometry geometry = nodesWithIDs.get(entry).getGeometry(); //get the geometry of the node with ID=entry wayTmp.addNodeGeometry(geometry); //add the node geometry in this way } Geometry geom = geometryFactory.buildGeometry(wayTmp.getNodeGeometries()); if ((wayTmp.getNumberOfNodes() > 3) && wayTmp.getNodeGeometries().get(0).equals(wayTmp.getNodeGeometries() .get(wayTmp.getNodeGeometries().size()-1))) { //checks if the beginning and ending node are the same and the number of nodes are more than 3. //the nodes must be more than 3, because jts does not allow a construction of a linear ring with less points. if (!((wayTmp.getTagKeyValue().containsKey("barrier")) || wayTmp.getTagKeyValue().containsKey("highway"))) { //this is not a barrier nor a road, so construct a polygon geometry LinearRing linear = geometryFactory.createLinearRing(geom.getCoordinates()); Polygon poly = new Polygon(linear, null, geometryFactory); wayTmp.setGeometry(poly); } else { //it is either a barrier or a road, so construct a linear ring geometry LinearRing linear = geometryFactory.createLinearRing(geom.getCoordinates()); wayTmp.setGeometry(linear); } } else if (wayTmp.getNumberOfNodes() > 1) { //it is an open geometry with more than one nodes, make it linestring LineString lineString = geometryFactory.createLineString(geom.getCoordinates()); wayTmp.setGeometry(lineString); } else { //we assume all the rest geometries are points //some ways happen to have only one point. Construct a Point. Point point = geometryFactory.createPoint(geom.getCoordinate()); wayTmp.setGeometry(point); } wayList.add(wayTmp); } if (element.equalsIgnoreCase("relation")) { relationList.add(relationTmp); } } public List<OSMNode> getNodeList() { return nodeList; } public List<OSMWay> getWayList() { return wayList; } public List<OSMRelation> getRelationList() { return relationList; } public Map<String, OSMNode> getNodesWithIDs() { return nodesWithIDs; } }