CityGmlIterator.java 3.45 KB
Newer Older
1
package eu.simstadt.regionchooser.fast_xml_parser;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

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<BuildingXmlNode>
{

	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.
32
33
34
35
	 *
	 * For a more complete and more robust (but slower) implementation, use eu.simstadt.geo.GeoCoordinatesAccessor in
	 * SimStadt geo-libs.
	 *
36
	 * Based on VTD XML, it provides a Building iterator.
37
	 *
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
	 */
	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<BuildingXmlNode> iterator() {
		return new Iterator<BuildingXmlNode>() {

			@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.
85
	 *
86
87
88
89
90
91
92
93
94
95
	 * @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.
96
	 *
97
98
99
	 * @return Citygml footer
	 * @throws NavException
	 */
Eric Duminil's avatar
Eric Duminil committed
100
	public String getFooter() throws IOException, NavException {
101
102
103
104
105
106
		int footerOffset = buildingOffset + buildingLength;
		int footerLength = (int) (Files.size(citygmlPath) - footerOffset);
		return navigator.toRawString(footerOffset, footerLength);
	}

}