Commits (3)
......@@ -71,12 +71,13 @@ public Void call() throws IOException {
public void downloadRegionFromCityGML(String wktPolygon, String project, String citygml, String srsName)
throws IOException, ParseException, XPathParseException, NavException, XPathEvalException {
StringBuilder sb = RegionExtractor.selectRegionDirectlyFromCityGML(citygmlPath(project, citygml), wktPolygon,
srsName);
StringBuilder sb = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, srsName,
citygmlPath(project, citygml));
File buildingIdsFile = selectSaveFileWithDialog(project, citygml, "selected_region");
if (buildingIdsFile != null) {
try (BufferedWriter writer = Files.newBufferedWriter(buildingIdsFile.toPath())) {
//NOTE: isn't there a better way??
writer.write(sb.toString());
}
}
......
......@@ -22,6 +22,7 @@
public static final CoordinateReferenceSystem WGS84 = CRS_FACTORY.createFromName("EPSG:4326");
private static final Pattern srsNamePattern = Pattern.compile("(?i)(?<=srsName=[\"'])[^\"']+(?=[\"'])");
private static final int CITYGML_HEADER_LENGTH = 50;
private static final String EPSG = "EPSG:";
private RegionChooserUtils() {
// only static use
......@@ -56,27 +57,27 @@ private static CoordinateReferenceSystem crsFromSrsName(String srsName) {
Pattern pOGC = Pattern.compile("urn:ogc:def:crs(?:,crs)?:EPSG:[\\d\\.]*:([\\d]+)\\D*");
Matcher mOGC = pOGC.matcher(srsName);
if (mOGC.find()) {
return CRS_FACTORY.createFromName("EPSG:" + mOGC.group(1));
return CRS_FACTORY.createFromName(EPSG + mOGC.group(1));
}
// urn:adv:crs:DE_DHDN_3GK3*DE_DHHN92_NH
// urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH
Pattern pURN = Pattern.compile("urn:adv:crs:([^\\*]+)");
Matcher mURN = pURN.matcher(srsName);
//NOTE: Could use a HashMap if the switch/case becomes too long.
if (mURN.find()) {
switch (mURN.group(1)) {
case "DE_DHDN_3GK2":
return CRS_FACTORY.createFromName("EPSG:31466");
case "DE_DHDN_3GK3":
return CRS_FACTORY.createFromName("EPSG:31467");
case "DE_DHDN_3GK4":
return CRS_FACTORY.createFromName("EPSG:31468");
case "DE_DHDN_3GK5":
return CRS_FACTORY.createFromName("EPSG:31469");
case "ETRS89_UTM32":
return CRS_FACTORY.createFromName("EPSG:25832");
default:
// nothing found
String shortSrsName = mURN.group(1);
// Gauss Krueger:
if (shortSrsName.startsWith("DE_DHDN_3GK")) {
int gaussKruegerBaseEPSG = 31464;
int gaussKruegerId = Integer.parseInt(shortSrsName.substring(11));
return CRS_FACTORY.createFromName(EPSG + (gaussKruegerBaseEPSG + gaussKruegerId));
}
// UTM North:
if (shortSrsName.startsWith("ETRS89_UTM")) {
int utmBaseEPSG = 25800;
int utmId = Integer.parseInt(shortSrsName.substring(10));
return CRS_FACTORY.createFromName(EPSG + (utmBaseEPSG + utmId));
}
}
throw new IllegalArgumentException("Unknown srsName format: " + srsName);
......
......@@ -27,15 +27,16 @@
private static final GeometryFactory gf = new GeometryFactory();
/**
* Main method behind RegionChooser. Given a CityGML (as Path) and a geometry (as Well-known text POLYGON, in the
* Main method behind RegionChooser. Given CityGMLs (as Path[]) and a geometry (as Well-known text POLYGON, in the
* same coordinate system as the CityGML), it iterates over each Building and checks if the building is inside the
* geometry. It only works with CityGML files smaller than 2GB. It uses VTD-XML parser instead of a whole
* Simstadt/Citydoctor/Citygml model.
*
*
* @param citygmlPath
* @param wktPolygon
* @param string
* @param srsName
* @param citygmlPaths
*
*
* @return a StringBuffer, full with the extracted Citygml, including header, buildings and footer.
* @throws ParseException
* @throws IOException
......@@ -44,32 +45,41 @@
* @throws XPathParseException
* @throws NumberFormatException
*/
static StringBuilder selectRegionDirectlyFromCityGML(Path citygmlPath, String wktPolygon, String srsName)
throws ParseException, XPathParseException, NavException, XPathEvalException,
IOException {
static StringBuilder selectRegionDirectlyFromCityGML(String wktPolygon, String srsName, Path... citygmlPaths)
throws ParseException, XPathParseException, NavException, IOException {
int buildingsCount = 0;
int foundBuildingsCount = 0;
StringBuilder sb = new StringBuilder();
Geometry poly = wktReader.read(wktPolygon);
CityGmlIterator citygml = new CityGmlIterator(citygmlPath);
for (BuildingXmlNode buildingXmlNode : citygml) {
if (buildingsCount == 0) {
sb.append(replaceEnvelopeInHeader(citygml.getHeader(), poly.getEnvelopeInternal(), srsName));
}
buildingsCount += 1;
if (buildingXmlNode.hasCoordinates()) {
Coordinate coord = new Coordinate(buildingXmlNode.x, buildingXmlNode.y);
Point point = gf.createPoint(coord);
if (point.within(poly)) {
foundBuildingsCount++;
sb.append(buildingXmlNode.toString());
CityGmlIterator citygml = null;
for (int i = 0; i < citygmlPaths.length; i++) {
Path citygmlPath = citygmlPaths[i];
LOGGER.info("Parsing " + citygmlPath);
citygml = new CityGmlIterator(citygmlPath);
for (BuildingXmlNode buildingXmlNode : citygml) {
if (buildingsCount == 0) {
sb.append(replaceEnvelopeInHeader(citygml.getHeader(), poly.getEnvelopeInternal(), srsName));
}
buildingsCount += 1;
if (buildingXmlNode.hasCoordinates()) {
Coordinate coord = new Coordinate(buildingXmlNode.x, buildingXmlNode.y);
Point point = gf.createPoint(coord);
if (point.within(poly)) {
foundBuildingsCount++;
sb.append(buildingXmlNode.toString());
}
}
if (buildingsCount % 1000 == 0) {
LOGGER.info("1000 buildings parsed");
}
}
if (buildingsCount % 1000 == 0) {
LOGGER.info("1000 buildings parsed");
}
}
if (citygml == null) {
throw new IllegalArgumentException("There should be at least one citygml");
}
LOGGER.info("Buildings found in selected region " + foundBuildingsCount);
......
......@@ -8,7 +8,7 @@
import org.junit.jupiter.api.Test;
public class CRSfromCityGMLHeaderTests
class CRSfromCityGMLHeaderTests
{
private Path repo = Paths.get("src/test/resources/testdata");
......@@ -17,39 +17,39 @@ private void testExtractCSRNameFromHeader(Path citygmlPath, String crsName) thro
}
@Test
public void testExtractCRSFromStuttgart() throws IOException {
void testExtractCRSFromStuttgart() throws IOException {
testExtractCSRNameFromHeader(repo.resolve("Stuttgart.proj/Stuttgart_LOD0_LOD1_buildings_and_trees.gml"),
"EPSG:31463");
}
@Test
public void testExtractCRSFromGruenbuehl() throws IOException {
void testExtractCRSFromGruenbuehl() throws IOException {
testExtractCSRNameFromHeader(repo.resolve("Gruenbuehl.proj/20140218_Gruenbuehl_LOD2_1building.gml"),
"EPSG:31467");
}
@Test
public void testExtractCRSFromMunich() throws IOException {
void testExtractCRSFromMunich() throws IOException {
testExtractCSRNameFromHeader(repo.resolve("Muenchen.proj/Munich_v_1_0_0.gml"), "EPSG:32632");
}
@Test
public void testExtractCRSFromNYC() throws IOException {
void testExtractCRSFromNYC() throws IOException {
testExtractCSRNameFromHeader(repo.resolve("NewYork.proj/ManhattanSmall.gml"), "EPSG:32118");
}
@Test
public void testExtractCRSFromAachen() throws IOException {
void testExtractCRSFromAachen() throws IOException {
testExtractCSRNameFromHeader(repo.resolve("Others.proj/Aachen_LoD2_293_5623_1_NW.gml"), "EPSG:25832");
}
@Test
public void testExtractCRSFromValladolid() throws IOException {
void testExtractCRSFromValladolid() throws IOException {
testExtractCSRNameFromHeader(repo.resolve("Others.proj/Valladolid_Spain_only_header.gml"), "EPSG:25830");
}
@Test
public void testDontExtractCRSFromBrokenCityGML() throws IOException {
void testDontExtractCRSFromBrokenCityGML() throws IOException {
Path citygmlPath = repo.resolve("Others.proj/SimpleSolid_MSBS.gml");
assertThrows(IllegalArgumentException.class, () -> {
testExtractCSRNameFromHeader(citygmlPath, "Nothing found. Should throw an exception");
......
......@@ -10,7 +10,7 @@
import org.junit.jupiter.api.Test;
public class RegionExtractorTests
class RegionExtractorTests
{
private static final String EPSG_32118 = "EPSG:32118";
private static final String CITY_OBJECT_MEMBER_REGEX = "<(core:)?cityObjectMember";
......@@ -20,7 +20,7 @@
private static final String CORE_CITY_MODEL_FOOTER = "</core:CityModel";
private static final Path TEST_REPOSITORY = Paths.get("src/test/resources/testdata/");
public static int countRegexMatches(String str, String subStr) {
static int countRegexMatches(String str, String subStr) {
Pattern pattern = Pattern.compile(subStr);
Matcher matcher = pattern.matcher(str);
int count = 0;
......@@ -31,11 +31,11 @@ public static int countRegexMatches(String str, String subStr) {
}
@Test
public void testExtract3BuildingsFromGSK3Model() throws Throwable {
void testExtract3BuildingsFromGSK3Model() throws Throwable {
//NOTE: Small region around Martinskirche in Grünbühl
String wktPolygon = "POLYGON((3515848.896028535 5415823.108586172,3515848.9512289143 5415803.590347393,3515829.0815150724 5415803.338023346,3515830.9784850604 5415793.437034622,3515842.0946056456 5415793.272282251,3515843.3515515197 5415766.204935087,3515864.1064344468 5415766.557899496,3515876.489172751 5415805.433782301,3515876.343844858 5415822.009293416,3515848.896028535 5415823.108586172))";
Path citygmlPath = TEST_REPOSITORY.resolve("Gruenbuehl.proj/20140218_Gruenbuehl_LOD2.gml");
String churchGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(citygmlPath, wktPolygon, "EPSG:31467")
String churchGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, "EPSG:31467", citygmlPath)
.toString();
assertEquals(3, countRegexMatches(churchGMLString, CITY_OBJECT_MEMBER_REGEX));
assertTrue(churchGMLString.contains("Donaustr"));
......@@ -53,11 +53,11 @@ public void testExtract3BuildingsFromGSK3Model() throws Throwable {
}
@Test
public void testExtractBuildingsWithoutCommentsInBetween() throws Throwable {
void testExtractBuildingsWithoutCommentsInBetween() throws Throwable {
//NOTE: Small region around WashingtonSquare
String wktPolygon = "POLYGON((300259.78663489706 62835.835907766595,300230.33294975647 62792.0482567884,300213.5667431851 62770.83143720031,300183.6592861123 62730.20347659383,300252.9947486632 62676.938468840905,300273.3862256562 62701.767105345614,300257.5250407747 62715.760413539596,300308.2754543957 62805.14198211394,300259.78663489706 62835.835907766595))";
Path citygmlPath = TEST_REPOSITORY.resolve("NewYork.proj/ManhattanSmall.gml");
String archGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(citygmlPath, wktPolygon, EPSG_32118)
String archGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, EPSG_32118, citygmlPath)
.toString();
assertEquals(countRegexMatches(archGMLString, CITY_OBJECT_MEMBER_REGEX), 2);
assertTrue(archGMLString.contains("WASHINGTON SQUARE"));
......@@ -72,11 +72,11 @@ public void testExtractBuildingsWithoutCommentsInBetween() throws Throwable {
}
@Test
public void testExtractBuildingsAndChangeEnvelope() throws Throwable {
void testExtractBuildingsAndChangeEnvelope() throws Throwable {
String wktPolygon = "POLYGON((299761.8123557725 61122.68126771413,299721.46983062755 61058.11626595352,299780.84627343423 61021.99295737501,299823.9079725632 61083.3979344517,299761.8123557725 61122.68126771413))";
Path citygmlPath = TEST_REPOSITORY.resolve("NewYork.proj/FamilyCourt_LOD2_with_PLUTO_attributes.gml");
String familyCourtBuilding = RegionExtractor
.selectRegionDirectlyFromCityGML(citygmlPath, wktPolygon, EPSG_32118).toString();
.selectRegionDirectlyFromCityGML(wktPolygon, EPSG_32118, citygmlPath).toString();
assertEquals(1, countRegexMatches(familyCourtBuilding, CITY_OBJECT_MEMBER_REGEX));
assertTrue(familyCourtBuilding.contains("Bldg_12210021066"));
assertFalse(
......@@ -96,11 +96,11 @@ public void testExtractBuildingsAndChangeEnvelope() throws Throwable {
}
@Test
public void testExtract0BuildingsWithWrongCoordinates() throws Throwable {
void testExtract0BuildingsWithWrongCoordinates() throws Throwable {
//NOTE: Small region, far away from NYC
String wktPolygon = "POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))";
Path citygmlPath = TEST_REPOSITORY.resolve("NewYork.proj/ManhattanSmall.gml");
String emptyGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(citygmlPath, wktPolygon, EPSG_32118)
String emptyGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, EPSG_32118, citygmlPath)
.toString();
assertEquals(0, countRegexMatches(emptyGMLString, CITY_OBJECT_MEMBER_REGEX));
assertTrue(emptyGMLString.contains(CITY_MODEL_HEADER));
......@@ -108,11 +108,11 @@ public void testExtract0BuildingsWithWrongCoordinates() throws Throwable {
}
@Test
public void testExtract0BuildingsFromEmptyGML() throws Throwable {
void testExtract0BuildingsFromEmptyGML() throws Throwable {
//NOTE: Small region, with too many spaces between coordinates
String wktPolygon = "POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))";
Path citygmlPath = TEST_REPOSITORY.resolve("NewYork.proj/empty_model.gml");
String emptyGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(citygmlPath, wktPolygon, EPSG_32118)
String emptyGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, EPSG_32118, citygmlPath)
.toString();
assertEquals(0, countRegexMatches(emptyGMLString, CITY_OBJECT_MEMBER_REGEX));
assertTrue(emptyGMLString.contains(CORE_CITY_MODEL_HEADER));
......@@ -120,11 +120,11 @@ public void testExtract0BuildingsFromEmptyGML() throws Throwable {
}
@Test
public void testExtract0BuildingsFromWeirdGML() throws Throwable {
void testExtract0BuildingsFromWeirdGML() throws Throwable {
//NOTE: Small region, with too many spaces between coordinates
String wktPolygon = "POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))";
Path citygmlPath = TEST_REPOSITORY.resolve("NewYork.proj/broken_nyc_lod2.gml");
String emptyGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(citygmlPath, wktPolygon, EPSG_32118)
String emptyGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, EPSG_32118, citygmlPath)
.toString();
assertEquals(0, countRegexMatches(emptyGMLString, CITY_OBJECT_MEMBER_REGEX));
assertTrue(emptyGMLString.contains(CORE_CITY_MODEL_HEADER));
......@@ -132,11 +132,24 @@ public void testExtract0BuildingsFromWeirdGML() throws Throwable {
}
@Test
public void testExtractBuildingsFromCitygmlWithoutZinEnvelope() throws Throwable {
void testExtractBuildingsFromCitygmlWithoutZinEnvelope() throws Throwable {
String wktPolygon = "POLYGON((3512683.1280912133 5404783.732132129,3512719.1608604863 5404714.627650777,3512831.40076119 5404768.344155442,3512790.239106708 5404838.614891164,3512683.1280912133 5404783.732132129))";
Path citygmlPath = TEST_REPOSITORY.resolve("Stuttgart.proj/Stuttgart_LOD0_LOD1_small.gml");
String emptyGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(citygmlPath, wktPolygon, "EPSG:31463")
String emptyGMLString = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, "EPSG:31463", citygmlPath)
.toString();
assertEquals(2, countRegexMatches(emptyGMLString, "<bldg:Building gml:id"));
}
@Test
void testExtractBuildingsFrom2Citygmls() throws Throwable {
String wktPolygon = "POLYGON((3512984.7003764412 5405148.310572891,3513038.6360455155 5405010.072163861,3513142.7277745553 5405004.02571992,3514204.1661769524 5405563.192081669,3514399.2818417274 5405720.905457244,3514291.6158155007 5405896.706492759,3512984.7003764412 5405148.310572891))";
Path citygml1 = TEST_REPOSITORY.resolve("Stuttgart.proj/Stuttgart_LOD0_LOD1_small.gml");
Path citygml2 = TEST_REPOSITORY.resolve("Stuttgart.proj/Stöckach_überarbeitete GML-NoBuildingPart.gml");
String emptyGMLString = RegionExtractor
.selectRegionDirectlyFromCityGML(wktPolygon, "EPSG:31463", citygml1, citygml2).toString();
assertEquals(17 + 3, countRegexMatches(emptyGMLString, "<bldg:Building gml:id"));
}
}
......@@ -34,7 +34,7 @@ public void testExtractRegionWithLocalCRS()
Path citygmlPath = project.resolve(citygml);
CoordinateReferenceSystem localCRS = RegionChooserUtils.crsFromCityGMLHeader(citygmlPath);
StringBuilder sb = RegionExtractor.selectRegionDirectlyFromCityGML(citygmlPath, wktPolygon, localCRS.getName());
StringBuilder sb = RegionExtractor.selectRegionDirectlyFromCityGML(wktPolygon, localCRS.getName(), citygmlPath);
assertTrue(sb.toString().contains("gml_ZVHMQQ6BZGRT0O3Q6RGXF12BDOV49QIZ58XB"),
"One weird shaped roof should be inside the region");
}
......@@ -53,8 +53,8 @@ public void testExtractRegionWithWGS84()
CoordinateReferenceSystem localCRS = RegionChooserUtils.crsFromCityGMLHeader(citygmlPath);
String localWktPolygon = WKT_WRITER
.write(RegionChooserUtils.changePolygonCRS(wgs84Polygon, RegionChooserUtils.WGS84, localCRS));
StringBuilder sb = RegionExtractor.selectRegionDirectlyFromCityGML(citygmlPath, localWktPolygon,
localCRS.getName());
StringBuilder sb = RegionExtractor.selectRegionDirectlyFromCityGML(localWktPolygon, localCRS.getName(),
citygmlPath);
assertTrue(sb.toString().contains("gml_ZVHMQQ6BZGRT0O3Q6RGXF12BDOV49QIZ58XB"),
"One weird shaped roof should be inside the region");
}
......@@ -102,8 +102,8 @@ public void testExtractRegionWithOldCoordinates()
CoordinateReferenceSystem localCRS = RegionChooserUtils.crsFromCityGMLHeader(citygmlPath);
String localWktPolygon = WKT_WRITER
.write(RegionChooserUtils.changePolygonCRS(wgs84Polygon, RegionChooserUtils.WGS84, localCRS));
StringBuilder sb = RegionExtractor.selectRegionDirectlyFromCityGML(citygmlPath, localWktPolygon,
localCRS.getName());
StringBuilder sb = RegionExtractor.selectRegionDirectlyFromCityGML(localWktPolygon, localCRS.getName(),
citygmlPath);
assertTrue(sb.toString().contains("gml_ZVHMQQ6BZGRT0O3Q6RGXF12BDOV49QIZ58XB"),
"One weird shaped roof should be inside the region");
......