RegionChooserUtils.java 5.34 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package eu.simstadt.geo;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Polygon;
import org.osgeo.proj4j.BasicCoordinateTransform;
import org.osgeo.proj4j.CRSFactory;
import org.osgeo.proj4j.CoordinateReferenceSystem;
import org.osgeo.proj4j.ProjCoordinate;


19
public class RegionChooserUtils
20
21
22
23
24
{
	private static final CRSFactory CRS_FACTORY = new CRSFactory();
	public static final CoordinateReferenceSystem WGS84 = CRS_FACTORY.createFromName("EPSG:4326");
	private static final Pattern srsNamePattern = Pattern.compile("(?i)(?<=srsName=[\"'])[^\"']+(?=[\"'])");

25
	private RegionChooserUtils() {
26
27
28
29
30
31
32
33
34
35
36
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
		// only static use
	}

	/**
	 * The srsName (The name by which this reference system is identified) inside the CityGML file can have multiple
	 * formats. This method tries to parse the string and detect the corresponding reference system. If it is found, it
	 * returns a proj4j.CoordinateReferenceSystem. It throws an IllegalArgumentException otherwise.
	 * 
	 * This method should be able to parse any EPSG id : e.g. "EPSG:1234". German Citygmls might also have "DE_DHDN_3GK3"
	 * or "ETRS89_UTM32" as srsName, so those are also included. It isn't guaranteed that those formats are correctly
	 * parsed, though.
	 * 
	 * The EPSG ids and parameters are defined in resources ('nad/epsg') inside proj4j-0.1.0.jar. Some EPSG ids are
	 * missing though, e.g. 7415
	 * 
	 * @param srsName
	 * @return CoordinateReferenceSystem
	 */
	public static CoordinateReferenceSystem crsFromSrsName(String srsName) {
		// EPSG:31467
		Pattern pEPSG = Pattern.compile("^(EPSG:\\d+)$");
		Matcher mEPSG = pEPSG.matcher(srsName);
		if (mEPSG.find()) {
			return CRS_FACTORY.createFromName(srsName);
		}

		// urn:ogc:def:crs,crs:EPSG:6.12:31467,crs:EPSG:6.12:5783
		//   or
		// urn:ogc:def:crs,crs:EPSG::28992
		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));
		}
		// 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
			}
		}
		throw new IllegalArgumentException("Unknown srsName format: " + srsName);
	}

	/**
	 * Converts a jts.geom.Polygon from one CoordinateReferenceSystem to another.
	 * 
	 * NOTE: It would be easier with org.geotools.referencing.CRS instead of Proj4J
	 * 
	 * @param polygonInOriginalCRS
	 * @param originalCRS
	 * @param newCRS
	 * @return
	 */
	public static Polygon changePolygonCRS(Polygon polygonInOriginalCRS, CoordinateReferenceSystem originalCRS,
			CoordinateReferenceSystem newCRS) {
		GeometryFactory geometryFactory = new GeometryFactory();
		Coordinate[] convexHullcoordinates = polygonInOriginalCRS.getCoordinates();
		BasicCoordinateTransform transformToWgs84 = new BasicCoordinateTransform(originalCRS, newCRS);
		for (int i = 0; i < convexHullcoordinates.length; i++) {
			ProjCoordinate wgs84Coordinate = transformToWgs84.transform(
					new ProjCoordinate(convexHullcoordinates[i].x, convexHullcoordinates[i].y), new ProjCoordinate());
			convexHullcoordinates[i] = new Coordinate(wgs84Coordinate.x, wgs84Coordinate.y);
		}

		return geometryFactory.createPolygon(convexHullcoordinates);
	}

	/**
	 * 
	 * Fast scan of the 50 first lines of a Citygml file to look for srsName. It might not be as reliable as parsing the
	 * whole CityGML, but it should be much faster and use much less memory. For a more reliable solution, use
	 * GeoCoordinatesAccessor. This solution can be convenient for Web services, RegionChooser or HullExtractor.
	 * 
	 * @param citygmlPath
	 * @return
	 * @throws IOException
	 */
	public static CoordinateReferenceSystem crsFromCityGMLHeader(Path citygmlPath) throws IOException {
		Optional<String> line = Files.lines(citygmlPath).limit(50).filter(srsNamePattern.asPredicate()).findFirst();
		if (line.isPresent()) {
			Matcher matcher = srsNamePattern.matcher(line.get());
			matcher.find();
			return crsFromSrsName(matcher.group());
		} else {
			throw new IllegalArgumentException("No srsName found in the header of " + citygmlPath);
		}
	}

	/**
	 * Finds every CityGML in every .proj folder in a repository.
	 * 
	 * @param repository
	 * @return a stream of CityGML Paths
	 * @throws IOException
	 */
	public static Stream<Path> everyCityGML(Path repository) throws IOException {
		return Files.walk(repository, 2)
				.sorted()
				.filter(
						p -> Files.isRegularFile(p) &&
								p.getParent().toString().endsWith(".proj") &&
								p.toString().toLowerCase().endsWith(".gml"));
	}

}