package eu.simstadt.regionchooser.fast_xml_parser; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Iterator; import java.util.logging.Logger; import com.ximpleware.AutoPilot; import com.ximpleware.NavException; import com.ximpleware.VTDGen; import com.ximpleware.VTDNav; import com.ximpleware.XPathEvalException; import com.ximpleware.XPathParseException; public class CityGmlIterator implements Iterable { private static final Logger LOGGER = Logger.getLogger(CityGmlIterator.class.getName()); private AutoPilot buildingsFinder; private VTDNav navigator; private int buildingOffset = 0; private int buildingLength = 0; private Path citygmlPath; /* * Simple class to parse a CityGML and extract cityObjectMember XML nodes and their coordinates. No other attribute * is extracted. Not suitable for building simulation, perfect for quick and dirty extract of coordinates (e.g. for * RegionChooser or HullExtractor). It should be fast and not use much memory. A SaxParser would use even less memory * but might be harder to code and possibly slower to run. * * For a more complete and more robust (but slower) implementation, use eu.simstadt.geo.GeoCoordinatesAccessor in * SimStadt geo-libs. * * Based on VTD XML, it provides a Building iterator. * */ public CityGmlIterator(Path citygmlPath) throws XPathParseException { this.citygmlPath = citygmlPath; VTDGen parser = new VTDGen(); parser.parseFile(citygmlPath.toString(), false); this.navigator = parser.getNav(); this.buildingsFinder = new AutoPilot(navigator); buildingsFinder.selectXPath("/CityModel/cityObjectMember[Building]"); //TODO: Check it's the only correct possibility. //FIXME: BuildingPart too! } @Override public Iterator iterator() { return new Iterator() { @Override public boolean hasNext() { try { return buildingsFinder.evalXPath() != -1; } catch (XPathEvalException | NavException ex) { LOGGER.warning("Error while parsing " + citygmlPath); return false; } } @Override public BuildingXmlNode next() { try { long offsetAndLength = navigator.getElementFragment(); buildingOffset = (int) offsetAndLength; buildingLength = (int) (offsetAndLength >> 32); return new BuildingXmlNode(navigator, buildingOffset, buildingLength); } catch (NavException | NumberFormatException | XPathParseException | XPathEvalException ex) { LOGGER.warning("Error while parsing " + citygmlPath); } return null; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } /** * Returns Header of the Citygml, from the start of the file to the current building. This method needs to be called * directly after the first building has been found. * * @return Citygml header * @throws NavException */ public String getHeader() throws NavException { return navigator.toRawString(0, buildingOffset); } /** * Returns footer of the Citygml, from the end of the last building to the end of file. This method needs to be * called after the last building has been found. * * @return Citygml footer * @throws NavException */ public String getFooter() throws IOException, NavException { int footerOffset = buildingOffset + buildingLength; int footerLength = (int) (Files.size(citygmlPath) - footerOffset); return navigator.toRawString(footerOffset, footerLength); } }