Commit 0f66af82 authored by Matthias Betz's avatar Matthias Betz
Browse files

initial viewer release

parent 364470b6
/*-
* Copyright 2021 Hochschule für Technik Stuttgart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.hft.stuttgart.citygml.viewer.datastructure;
import java.util.ArrayList;
import java.util.List;
import com.jogamp.opengl.GL;
import de.hft.stuttgart.citygml.viewer.math.Vector3d;
/**
* Result of the tesselation process. Primitives can be converted to triangles.
*
* @author Matthias Betz
*
*/
public class Primitive {
private static final double AREA_EPSILON = 0.00001;
private final int type;
private final List<Integer> pIndices = new ArrayList<>();
private final List<Vector3d> vertices;
public Primitive(int type, List<Vector3d> vertices) {
this.vertices = vertices;
this.type = type;
}
public void fillListWithIndices(List<Integer> indices) {
switch (type) {
case GL.GL_TRIANGLES:
for (int i = 0; i < pIndices.size(); i += 3) {
addToListIfAreaNonNull(pIndices.get(i), pIndices.get(i + 1), pIndices.get(i + 2), indices);
}
break;
case GL.GL_TRIANGLE_STRIP:
for (int i = 0; i < pIndices.size() - 2; i++) {
if (i % 2 == 0) {
addToListIfAreaNonNull(pIndices.get(i), pIndices.get(i + 1), pIndices.get(i + 2), indices);
} else {
addToListIfAreaNonNull(pIndices.get(i), pIndices.get(i + 2), pIndices.get(i + 1), indices);
}
}
break;
case GL.GL_TRIANGLE_FAN:
Integer first = pIndices.get(0);
for (int i = 0; i < pIndices.size() - 2; i++) {
addToListIfAreaNonNull(first, pIndices.get(i + 1), pIndices.get(i + 2), indices);
}
break;
default:
throw new IllegalStateException("unknown type found: " + type);
}
}
public void addIndex(Integer i) {
pIndices.add(i);
}
private void addToListIfAreaNonNull(Integer i1, Integer i2, Integer i3, List<Integer> indices) {
Vector3d v1 = vertices.get(i1);
Vector3d v2 = vertices.get(i2);
Vector3d v3 = vertices.get(i3);
double area = Math.abs(calculateAreaOfTriangle(v1, v2, v3));
if (area > AREA_EPSILON) {
indices.add(i1);
indices.add(i2);
indices.add(i3);
}
}
/**
* Calculates the area of a triangle given by the 3 points
*
* @param v1 the first point of the triangle
* @param v2 the second point of the triangle
* @param v3 the last point of the triangle
* @return the area of the triangle
*/
private static double calculateAreaOfTriangle(Vector3d v1, Vector3d v2, Vector3d v3) {
double a = v1.getDistance(v2);
double b = v2.getDistance(v3);
double c = v1.getDistance(v3);
double s = 0.5d * (a + b + c);
return Math.sqrt(s * (s - a) * (s - b) * (s - c));
}
}
/*-
* Copyright 2021 Hochschule für Technik Stuttgart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.hft.stuttgart.citygml.viewer.datastructure;
import java.util.ArrayList;
import java.util.List;
import de.hft.stuttgart.citygml.viewer.math.Vector3d;
public class Ring {
private List<Vector3d> vertices = new ArrayList<>();
public List<Vector3d> getVertices() {
return vertices;
}
public void addPoint(Vector3d v) {
vertices.add(v);
}
public Vector3d calculateNormal() {
double[] coords = new double[3];
for (int i = 0; i < vertices.size() - 1; i++) {
Vector3d current = vertices.get(i + 0);
Vector3d next = vertices.get(i + 1);
coords[0] += (current.getZ() + next.getZ()) * (current.getY() - next.getY());
coords[1] += (current.getX() + next.getX()) * (current.getZ() - next.getZ());
coords[2] += (current.getY() + next.getY()) * (current.getX() - next.getX());
}
if (coords[0] == 0 && coords[1] == 0 && coords[2] == 0) {
// no valid normal vector found
if (vertices.size() < 3) {
// no three points, return x-axis
return new Vector3d(1, 0, 0);
}
Vector3d v1 = vertices.get(0);
Vector3d v2 = vertices.get(1);
Vector3d v3 = vertices.get(2);
return calculateNormalWithCross(v1, v2, v3);
}
Vector3d v = new Vector3d(coords);
v.normalize();
return v;
}
private Vector3d calculateNormalWithCross(Vector3d v1, Vector3d v2, Vector3d v3) {
Vector3d dir1 = v2.minus(v1);
Vector3d dir2 = v3.minus(v1);
Vector3d cross = dir1.cross(dir2);
return cross.normalize();
}
}
/*-
* Copyright 2021 Hochschule für Technik Stuttgart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.hft.stuttgart.citygml.viewer.datastructure;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import de.hft.stuttgart.citygml.viewer.math.Vector3d;
public class TesselatedPolygon {
private List<Triangle3d> triangles;
private Vector3d normal;
private Color color;
public static TesselatedPolygon of(Polygon p) {
TesselatedPolygon tessP = JoglTesselator.tesselatePolygon(p);
tessP.normal = p.calculateNormal();
tessP.color = p.getColor();
return tessP;
}
public TesselatedPolygon(List<Triangle3d> triangles) {
this.triangles = triangles;
}
public TesselatedPolygon(List<Vector3d> vertices, List<Integer> indices) {
if (indices.size() % 3 != 0) {
throw new IllegalArgumentException("Number of indices is not a multiple of 3");
}
triangles = new ArrayList<>();
for (int i = 0; i < indices.size(); i = i + 3) {
Vector3d v1 = vertices.get(indices.get(i + 0));
Vector3d v2 = vertices.get(indices.get(i + 1));
Vector3d v3 = vertices.get(indices.get(i + 2));
triangles.add(new Triangle3d(v1, v2, v3));
}
}
public List<Triangle3d> getTriangles() {
return triangles;
}
public Vector3d getNormal() {
return normal;
}
public Color getColor() {
return color;
}
}
/*-
* Copyright 2021 Hochschule für Technik Stuttgart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.hft.stuttgart.citygml.viewer.datastructure;
/**
* Thrown when something went wrong with the tesselation process.
*
* @author Matthias Betz
*
*/
public class TesselationException extends RuntimeException {
public TesselationException() {
super();
}
public TesselationException(String message, Throwable cause, boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public TesselationException(String message, Throwable cause) {
super(message, cause);
}
public TesselationException(Throwable cause) {
super(cause);
}
private static final long serialVersionUID = -2010522579830781136L;
public TesselationException(String message) {
super(message);
}
}
/*-
* Copyright 2021 Hochschule für Technik Stuttgart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.hft.stuttgart.citygml.viewer.datastructure;
import de.hft.stuttgart.citygml.viewer.math.Vector3d;
/**
* A three dimensional triangle described by three points
*
* @author Matthias Betz
*
*/
public class Triangle3d {
private Vector3d p1;
private Vector3d p2;
private Vector3d p3;
public Triangle3d(Vector3d p1, Vector3d p2, Vector3d p3) {
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
}
public Vector3d getP1() {
return p1;
}
public Vector3d getP2() {
return p2;
}
public Vector3d getP3() {
return p3;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((p1 == null) ? 0 : p1.hashCode());
result = prime * result + ((p2 == null) ? 0 : p2.hashCode());
result = prime * result + ((p3 == null) ? 0 : p3.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Triangle3d other = (Triangle3d) obj;
if (p1 == null) {
if (other.p1 != null) {
return false;
}
} else if (!p1.equals(other.p1)) {
return false;
}
if (p2 == null) {
if (other.p2 != null) {
return false;
}
} else if (!p2.equals(other.p2)) {
return false;
}
if (p3 == null) {
if (other.p3 != null) {
return false;
}
} else if (!p3.equals(other.p3)) {
return false;
}
return true;
}
@Override
public String toString() {
return "Triangle3d [p1=" + p1 + ", p2=" + p2 + ", p3=" + p3 + "]";
}
}
/*-
* Copyright 2021 Hochschule für Technik Stuttgart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.hft.stuttgart.citygml.viewer.math;
import java.util.ArrayList;
import java.util.List;
import de.hft.stuttgart.citygml.viewer.datastructure.BoundingBox;
import de.hft.stuttgart.citygml.viewer.datastructure.Polygon;
import de.hft.stuttgart.citygml.viewer.parser.FeatureMapper;
/**
* Utility class for calculating axis aligned bounding boxes for different
* inputs.
*
* @author Matthias Betz
*
*/
public class BoundingBoxCalculator {
private BoundingBoxCalculator() {
// only static use
}
/**
* This will calculate two points where every other point in the geometry is
* between those two. If the geometry does not contain points or is null, an
* empty array is returned. This is considered an axis aligned bounding box.
* Only the exterior rings of the polygons is used as inner rings should be
* within the exterior or they are faulty.
*
* @param a list of polygons containing the vertices
* @return an BoundingBox containing the two end points of the bounding box.
*/
public static BoundingBox calculateBoundingBox(List<Polygon> polygons) {
double lowX = Double.MAX_VALUE;
double highX = Double.NEGATIVE_INFINITY;
double lowY = Double.MAX_VALUE;
double highY = Double.NEGATIVE_INFINITY;
double lowZ = Double.MAX_VALUE;
double highZ = Double.NEGATIVE_INFINITY;
for (Polygon p : polygons) {
// only need to check exterior rings
for (Vector3d v : p.getExteriorRing().getVertices()) {
if (v.getX() < lowX) {
lowX = v.getX();
}
if (v.getX() > highX) {
highX = v.getX();
}
if (v.getY() < lowY) {
lowY = v.getY();
}
if (v.getY() > highY) {
highY = v.getY();
}
if (v.getZ() < lowZ) {
lowZ = v.getZ();
}
if (v.getZ() > highZ) {
highZ = v.getZ();
}
}
}
Vector3d[] result = new Vector3d[2];
result[0] = new Vector3d(lowX, lowY, lowZ);
result[1] = new Vector3d(highX, highY, highZ);
return BoundingBox.of(result);
}
public static BoundingBox calculateBoundingBoxFromPoints(List<? extends Vector3d> points) {
double lowX = Double.MAX_VALUE;
double highX = Double.NEGATIVE_INFINITY;
double lowY = Double.MAX_VALUE;
double highY = Double.NEGATIVE_INFINITY;
double lowZ = Double.MAX_VALUE;
double highZ = Double.NEGATIVE_INFINITY;
// only need to check exterior rings
for (Vector3d v : points) {
if (v.getX() < lowX) {
lowX = v.getX();
}
if (v.getX() > highX) {
highX = v.getX();
}
if (v.getY() < lowY) {
lowY = v.getY();
}
if (v.getY() > highY) {
highY = v.getY();
}
if (v.getZ() < lowZ) {
lowZ = v.getZ();
}
if (v.getZ() > highZ) {
highZ = v.getZ();
}
}
Vector3d[] result = new Vector3d[2];
result[0] = new Vector3d(lowX, lowY, lowZ);
result[1] = new Vector3d(highX, highY, highZ);
return BoundingBox.of(result);
}
public static BoundingBox calculateBoundingBox(FeatureMapper mapper) {
List<List<Polygon>> polygonList = new ArrayList<>();
polygonList.add(mapper.getLod1Polygons());
polygonList.add(mapper.getLod2Polygons());
polygonList.add(mapper.getLod3Polygons());
polygonList.add(mapper.getLod4Polygons());
double lowX = Double.MAX_VALUE;
double highX = Double.NEGATIVE_INFINITY;
double lowY = Double.MAX_VALUE;
double highY = Double.NEGATIVE_INFINITY;
double lowZ = Double.MAX_VALUE;
double highZ = Double.NEGATIVE_INFINITY;
for (List<Polygon> polygons : polygonList) {
for (Polygon p : polygons) {
// only need to check exterior rings
for (Vector3d v : p.getExteriorRing().getVertices()) {
if (v.getX() < lowX) {
lowX = v.getX();
}
if (v.getX() > highX) {
highX = v.getX();
}
if (v.getY() < lowY) {
lowY = v.getY();
}
if (v.getY() > highY) {
highY = v.getY();
}
if (v.getZ() < lowZ) {
lowZ = v.getZ();
}
if (v.getZ() > highZ) {
highZ = v.getZ();
}
}
}
}
Vector3d[] result = new Vector3d[2];
result[0] = new Vector3d(lowX, lowY, lowZ);
result[1] = new Vector3d(highX, highY, highZ);
return BoundingBox.of(result);
}
}
/*-
* Copyright 2021 Hochschule für Technik Stuttgart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.hft.stuttgart.citygml.viewer.math;
import java.io.Serializable;
import java.util.Arrays;
public class Vector3d implements Serializable {
private static final long serialVersionUID = 3495650092142761365L;
private static final Vector3d ORIGIN = new Vector3d();
public static final int X = 0;
public static final int Y = 1;
public static final int Z = 2;
private double[] coords;
public Vector3d() {
this(0d, 0d, 0d);
}
public Vector3d(double x, double y, double z) {
coords = new double[3];
coords[0] = x;
coords[1] = y;
coords[2] = z;
}
public Vector3d(double[] coords) {
if (coords == null) {
throw new IllegalArgumentException("Coordinates can not be null");
}
this.coords = coords;
}
public Vector3d(Vector3d vec) {
coords = new double[3];
coords[0] = vec.getX();
coords[1] = vec.getY();
coords[2] = vec.getZ();
}
public double getX() {
return coords[0];
}
public double getY() {
return coords[1];
}
public double getZ() {
return coords[2];
}
public void setX(double x) {
coords[0] = x;
}
public void setY(double y) {
coords[1] = y;
}
public void setZ(double z) {
coords[2] = z;
}
/**
* Getter for all coordinates. The changes made to this array are reflected in
* this vector.
*
* @return the coordiantes array
*/
public double[] getCoordinates() {
return coords;
}
public void setCoordinates(double[] coordiantes) {
if (coordiantes == null || coordiantes.length != 3) {
throw new IllegalArgumentException("Vector must have exactly 3 coordinates");
}
this.coords = coordiantes;
}
public double getLength() {
return getDistance(ORIGIN);
}
public double getSquaredLength() {
double x = coords[0];
double y = coords[1];
double z = coords[2];
return x * x + y * y + z * z;
}
public double getDistance(Vector3d other) {
return Math.sqrt(getDistanceSquare(other));
}
public double getDistanceSquare(Vector3d other) {
double x = coords[0] - other.getX();
double y = coords[1] - other.getY();
double z = coords[2] - other.getZ();
return x * x + y * y + z * z;
}
/**
* adds another vector to this one. The result is
* <code><br>(a1 + b1)<br>(a2 + b2)<br>(a3 + b3)</code>
*
* @param b the other vector
* @return the sum of both vectors as a new vector. Values in this instance
* remain unchanged.
*/
public Vector3d plus(Vector3d b) {
double x = coords[0] + b.getX();
double y = coords[1] + b.getY();
double z = coords[2] + b.getZ();
return new Vector3d(x, y, z);
}
/**
* add a value to a coordinate. This will change the coordinates of this vector.
*
* @param coordinateIndex one of <code>Vector3d.X, Vector3d.Y, Vector3d.Z</code>
* @param value the added value
* @return the current instance for chaining commands
*/
public Vector3d plus(int coordinateIndex, double value) {
if (coordinateIndex < 0 || coordinateIndex > 2) {
throw new IllegalArgumentException("coordinateIndex has to be between 0 and 2");
}
coords[coordinateIndex] += value;
return this;
}
/**
* subtract a value to a coordinate. This will change the coordinates of this
* vector.
*
* @param coordinateIndex one of <code>Vector3d.X, Vector3d.Y, Vector3d.Z</code>
* @param value the subtracted value
* @return the current instance for chaining commands
*/
public Vector3d minus(int coordinateIndex, double value) {
if (coordinateIndex < 0 || coordinateIndex > 2) {
throw new IllegalArgumentException("coordinateIndex has to be between 0 and 2");
}
coords[coordinateIndex] -= value;
return this;
}
/**
* subtract another vector from this one. The result is
* <code><br>(a1 - b1)<br>(a2 - b2)<br>(a3 - b3)</code>
*
* @param b the other vector
* @return the result as a new vector. Values in this instance remain unchanged.
*/
public Vector3d minus(Vector3d b) {
double x = coords[0] - b.getX();
double y = coords[1] - b.getY();
double z = coords[2] - b.getZ();
return new Vector3d(x, y, z);
}
/**
* multiply this vector with a scalar.
*
* @param scalar the scalar
* @return the result as a new vector. Values in this instance remain unchanged.
*/
public Vector3d mult(double scalar) {
double x = coords[0] * scalar;
double y = coords[1] * scalar;
double z = coords[2] * scalar;
return new Vector3d(x, y, z);
}
/**
* normalizes this vector. This method changes the coordinates of this instance.
*/
public Vector3d normalize() {
double length = getLength();
// if the length is already 1, do nothing
final double epsilon = 0.0000001;
if (Math.abs(1 - length) < epsilon) {
return this;
}
coords[0] /= length;
coords[1] /= length;
coords[2] /= length;
return this;
}
/**
* calculates the cross product. The result is
* <code><br>(a2 * b3 - a3 * b2)<br>(a3 * b1 - a1 * b3)<br>(a1 * b2 - a2 * b1)</code>
*
* @param b the other vector
* @return the result as a new vector. Values in this instance remain unchanged.
*/
public Vector3d cross(Vector3d b) {
double x = coords[1] * b.getZ() - coords[2] * b.getY();
double y = coords[2] * b.getX() - coords[0] * b.getZ();
double z = coords[0] * b.getY() - coords[1] * b.getX();
return new Vector3d(x, y, z);
}
/**
* Calculates the dot product. The result is
* <code><br>(a1 * b1) + (a2 * b2) + (a3 * b3)<br></code>
*
* @param b the other vector
* @return the dot product
*/
public double dot(Vector3d b) {
return coords[0] * b.getX() + coords[1] * b.getY() + coords[2] * b.getZ();
}
/**
* creates a new copy of this vector.
*
* @return a copy of this vector
*/
public Vector3d copy() {
return new Vector3d(coords[0], coords[1], coords[2]);
}
@Override
public String toString() {
final int maxLen = 5;
StringBuilder builder = new StringBuilder();
builder.append("Vector3d [coords=");
builder.append(coords != null ? Arrays.toString(Arrays.copyOf(coords, Math.min(coords.length, maxLen))) : null);
builder.append("]");
return builder.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(coords);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Vector3d other = (Vector3d) obj;
return Arrays.equals(coords, other.coords);
}
/**
* Two vectors are equals, when the distance between both is less than epsilon
*
* @param other the other vector
* @param epsilon the distance where the vectors are considered the same
* @return true if they are within epsilon distance, false otherwise.
*/
public boolean equalsWithEpsilon(Vector3d other, double epsilon) {
if (other == this) {
return true;
}
Vector3d dif = this.minus(other);
double sqLength = dif.getSquaredLength();
return sqLength < (epsilon * epsilon);
}
public double getCoordinate(int axis) {
return coords[axis];
}
public void plus(double radius) {
coords[0] += radius;
coords[1] += radius;
coords[2] += radius;
}
}
/*-
* Copyright 2021 Hochschule für Technik Stuttgart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.hft.stuttgart.citygml.viewer.parser;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.XMLConstants;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.locationtech.proj4j.BasicCoordinateTransform;
import org.locationtech.proj4j.CRSFactory;
import org.locationtech.proj4j.CoordinateReferenceSystem;
import org.locationtech.proj4j.ProjCoordinate;
import org.locationtech.proj4j.units.Units;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import de.hft.stuttgart.citygml.viewer.CityGmlHandler;
import de.hft.stuttgart.citygml.viewer.math.Vector3d;
public class CityGMLParser {
private static final Logger log = Logger.getLogger(CityGMLParser.class.getName());
private static final SAXParserFactory FACTORY;
private static final CRSFactory CRS_FACTORY = new CRSFactory();
// EPSG:31467
private static final Pattern P_EPSG = Pattern.compile("^(EPSG:\\d+)$");
// urn:ogc:def:crs,crs:EPSG:6.12:31467,crs:EPSG:6.12:5783
// or
// urn:ogc:def:crs,crs:EPSG::28992
private static final Pattern P_OGC = Pattern.compile("urn:ogc:def:crs,crs:EPSG:[\\d\\.]*:([\\d]+)\\D*");
private static final Pattern P_OGC2 = Pattern.compile("urn:ogc:def:crs:EPSG:[\\d\\.]*:([\\d]+)\\D*");
// urn:adv:crs:DE_DHDN_3GK3*DE_DHHN92_NH
// urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH
private static final Pattern P_URN = Pattern.compile("urn:adv:crs:([^\\*]+)");
private static final String WGS_84 = "EPSG:4326";
static {
FACTORY = SAXParserFactory.newInstance();
try {
FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
} catch (SAXNotRecognizedException | SAXNotSupportedException | ParserConfigurationException e) {
e.printStackTrace();
}
}
public static void parseEpsgCodeFromFile(File file, ParserConfiguration config)
throws IOException, ParserConfigurationException, SAXException {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
parseEpsgCodeFromStream(bis, config);
}
}
private static void parseEpsgCodeFromStream(InputStream is, ParserConfiguration config)
throws ParserConfigurationException, SAXException {
SAXParser parser = FACTORY.newSAXParser();
CityGmlHandler handler = new CityGmlHandler();
try {
parser.parse(new InputSource(is), handler);
} catch (EnvelopeFoundException e) {
try {
parseCoordinateSystem(config, handler);
} catch (Exception e2) {
log.log(Level.WARNING, "Caught unknown error while parsing EPSG, assuming metric coordinate system", e2);
}
} catch (Exception e) {
log.log(Level.WARNING, "Caught unknown error while parsing EPSG, assuming metric coordinate system", e);
}
}
private static void parseCoordinateSystem(ParserConfiguration config, CityGmlHandler handler) {
if (handler.getEpsg() == null) {
return;
}
CoordinateReferenceSystem crs = crsFromSrsName(handler.getEpsg());
if (crs == null) {
// could not find a coordinate system for srsName
// assuming metric system
return;
}
if (crs.getProjection().getUnits() == Units.METRES) {
// coordinate system is in meters, do not convert
return;
}
Vector3d low = handler.getLowerCorner();
Vector3d up = handler.getUpperCorner();
double centerLong = low.getX() + ((up.getX() - low.getX()) / 2);
double centerLat = low.getY() + ((up.getY() - low.getY()) / 2);
if (!crs.getName().equals(WGS_84)) {
// need to convert coordinates first to WGS84, then find UTM Zone
CoordinateReferenceSystem wgs84 = crsFromSrsName(WGS_84);
ProjCoordinate p1 = new ProjCoordinate();
p1.setValue(centerLong, centerLat);
ProjCoordinate p2 = new ProjCoordinate();
BasicCoordinateTransform bct = new BasicCoordinateTransform(crs, wgs84);
bct.transform(p1, p2);
centerLong = p2.x;
centerLat = p2.y;
}
int zone = (int) (31 + Math.round(centerLong / 6));
CoordinateReferenceSystem utm;
if (centerLat < 0) {
// south
utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone + " +south");
} else {
// north
utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone);
}
config.setCoordinateSystem(crs, utm);
}
/**
* 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
*/
private static CoordinateReferenceSystem crsFromSrsName(String srsName) {
srsName = srsName.trim();
Matcher mEPSG = P_EPSG.matcher(srsName);
if (mEPSG.find()) {
if ("EPSG:4979".contentEquals(srsName)) {
srsName = "EPSG:4236";
} else if ("EPSG:7415".contentEquals(srsName)) {
return CRS_FACTORY.createFromParameters("EPSG:7415",
"+proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel +towgs84=565.417,50.3319,465.552,-0.398957,0.343988,-1.8774,4.0725 +units=m +no_defs");
}
return CRS_FACTORY.createFromName(srsName);
}
Matcher mOGC = P_OGC.matcher(srsName);
if (mOGC.find()) {
return CRS_FACTORY.createFromName("EPSG:" + mOGC.group(1));
}
Matcher mOGC2 = P_OGC2.matcher(srsName);
if (mOGC2.find()) {
return CRS_FACTORY.createFromName("EPSG:" + mOGC2.group(1));
}
Matcher mURN = P_URN.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:
return null;
}
}
if (srsName.equals("http://www.opengis.net/def/crs/EPSG/0/6697")) {
return CRS_FACTORY.createFromParameters("EPSG:6697", "+proj=longlat +ellps=GRS80 +no_defs +axis=neu");
}
return null;
}
}
/*-
* Copyright 2021 Hochschule für Technik Stuttgart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.hft.stuttgart.citygml.viewer.parser;
import org.xml.sax.SAXException;
/**
* To stop the SAXParser from further parsing when the relevant section has
* already been found.
*
* @author Matthias Betz
*
*/
public class EnvelopeFoundException extends SAXException {
private static final long serialVersionUID = -9188617211115043815L;
public EnvelopeFoundException() {
super();
}
public EnvelopeFoundException(Exception e) {
super(e);
}
public EnvelopeFoundException(String message, Exception e) {
super(message, e);
}
public EnvelopeFoundException(String message) {
super(message);
}
}
/*-
* Copyright 2021 Hochschule für Technik Stuttgart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.hft.stuttgart.citygml.viewer.parser;
import java.awt.Color;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.citygml4j.model.bridge.AbstractBridge;
import org.citygml4j.model.building.AbstractBuilding;
import org.citygml4j.model.building.BuildingInstallation;
import org.citygml4j.model.construction.AbstractFillingElement;
import org.citygml4j.model.construction.Door;
import org.citygml4j.model.construction.GroundSurface;
import org.citygml4j.model.construction.RoofSurface;
import org.citygml4j.model.construction.Window;
import org.citygml4j.model.core.AbstractThematicSurface;
import org.citygml4j.model.core.ImplicitGeometry;
import org.citygml4j.model.core.ImplicitGeometryProperty;
import org.citygml4j.model.deprecated.bridge.DeprecatedPropertiesOfAbstractBridge;
import org.citygml4j.model.deprecated.building.DeprecatedPropertiesOfAbstractBuilding;
import org.citygml4j.model.deprecated.building.DeprecatedPropertiesOfBuildingInstallation;
import org.citygml4j.model.deprecated.core.DeprecatedPropertiesOfAbstractThematicSurface;
import org.citygml4j.model.deprecated.transportation.DeprecatedPropertiesOfAbstractTransportationSpace;
import org.citygml4j.model.deprecated.transportation.TransportationComplex;
import org.citygml4j.model.deprecated.vegetation.DeprecatedPropertiesOfPlantCover;
import org.citygml4j.model.deprecated.vegetation.DeprecatedPropertiesOfSolitaryVegetationObject;
import org.citygml4j.model.deprecated.waterbody.DeprecatedPropertiesOfWaterBody;
import org.citygml4j.model.landuse.LandUse;
import org.citygml4j.model.relief.TINRelief;
import org.citygml4j.model.transportation.AuxiliaryTrafficArea;
import org.citygml4j.model.transportation.TrafficArea;
import org.citygml4j.model.vegetation.PlantCover;
import org.citygml4j.model.vegetation.SolitaryVegetationObject;
import org.citygml4j.model.waterbody.WaterBody;
import org.citygml4j.visitor.ObjectWalker;
import org.locationtech.proj4j.BasicCoordinateTransform;
import org.locationtech.proj4j.ProjCoordinate;
import org.xmlobjects.gml.model.geometry.AbstractGeometry;
import org.xmlobjects.gml.model.geometry.GeometricPositionList;
import org.xmlobjects.gml.model.geometry.GeometryProperty;
import org.xmlobjects.gml.model.geometry.primitives.AbstractRing;
import org.xmlobjects.gml.model.geometry.primitives.AbstractRingProperty;
import org.xmlobjects.gml.model.geometry.primitives.LinearRing;
import org.xmlobjects.gml.model.geometry.primitives.Triangle;
import org.xmlobjects.gml.model.geometry.primitives.TriangulatedSurface;
import de.hft.stuttgart.citygml.viewer.datastructure.Polygon;
import de.hft.stuttgart.citygml.viewer.datastructure.Ring;
import de.hft.stuttgart.citygml.viewer.math.Vector3d;
public class FeatureMapper extends ObjectWalker {
private Color groundColor = new Color(0.9411765f, 0.9019608f, 0.54901963f);
private Color roofColor = Color.RED;
private Color doorColor = Color.ORANGE;
private Color windowColor = new Color(0.0f, 0.5019608f, 0.5019608f);
private Color wallColor = Color.WHITE;
private Color bridgeColor = new Color(1.0f, 0.49803922f, 0.3137255f);
private Color landColor = new Color(0.64705884f, 0.16470589f, 0.16470589f);
private Color transportationColor = new Color(1.0f, 1.0f, 0.0f);
private Color vegetationColor = new Color(0.5647059f, 0.93333334f, 0.5647059f);
private Color waterColor = new Color(0.5294118f, 0.80784315f, 0.98039216f);
private ParserConfiguration config;
private List<Polygon> lod1Polygons;
private List<Polygon> lod2Polygons;
private List<Polygon> lod3Polygons;
private List<Polygon> lod4Polygons;
private List<Polygon> currentPolygons = null;
private Ring currentRing;
private Color currentColor;
private ProjCoordinate p1 = new ProjCoordinate();
private ProjCoordinate p2 = new ProjCoordinate();
public FeatureMapper(ParserConfiguration config) throws IOException {
try (FileReader reader = new FileReader("color.properties")) {
Properties props = new Properties();
props.load(reader);
Color parsedGroundColor = parseColor(props.getProperty("groundColor"));
if (parsedGroundColor != null) {
groundColor = parsedGroundColor;
}
Color parsedRoofColor = parseColor(props.getProperty("roofColor"));
if (parsedRoofColor != null) {
roofColor = parsedRoofColor;
}
Color parsedDoorColor = parseColor(props.getProperty("doorColor"));
if (parsedDoorColor != null) {
doorColor = parsedDoorColor;
}
Color parsedWindowColor = parseColor(props.getProperty("windowColor"));
if (parsedWindowColor != null) {
windowColor = parsedWindowColor;
}
Color parsedWallColor = parseColor(props.getProperty("wallColor"));
if (parsedWallColor != null) {
wallColor = parsedWallColor;
}
Color parsedBridgeColor = parseColor(props.getProperty("bridgeColor"));
if (parsedBridgeColor != null) {
bridgeColor = parsedBridgeColor;
}
Color parsedLandColor = parseColor(props.getProperty("landColor"));
if (parsedLandColor != null) {
landColor = parsedLandColor;
}
Color parsedTransportationColor = parseColor(props.getProperty("transportationColor"));
if (parsedTransportationColor != null) {
transportationColor = parsedTransportationColor;
}
Color parsedVegetationColor = parseColor(props.getProperty("vegetationColor"));
if (parsedVegetationColor != null) {
vegetationColor = parsedVegetationColor;
}
Color parsedWaterColor = parseColor(props.getProperty("waterColor"));
if (parsedWaterColor != null) {
waterColor = parsedWaterColor;
}
}
this.config = config;
lod1Polygons = new ArrayList<>();
lod2Polygons = new ArrayList<>();
lod3Polygons = new ArrayList<>();
lod4Polygons = new ArrayList<>();
}
private Color parseColor(String colorString) {
if (colorString == null) {
return null;
}
try {
String[] split = colorString.split(" ");
float r = Float.parseFloat(split[0]);
float g = Float.parseFloat(split[1]);
float b = Float.parseFloat(split[2]);
return new Color(r, g, b);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
public void visit(org.xmlobjects.gml.model.geometry.primitives.Polygon gmlPoly) {
if (currentPolygons == null) {
return;
}
Polygon viewerPoly = new Polygon(currentColor);
// parse rings
Ring extRing = new Ring();
viewerPoly.setExteriorRing(extRing);
mapRing(gmlPoly.getExterior(), extRing);
if (gmlPoly.getInterior() != null) {
for (AbstractRingProperty arp : gmlPoly.getInterior()) {
if (arp.getObject() != null) {
Ring innerRing = new Ring();
viewerPoly.addInteriorRing(innerRing);
mapRing(arp, innerRing);
}
}
}
currentPolygons.add(viewerPoly);
}
@Override
public void visit(Triangle triangle) {
if (currentPolygons == null) {
return;
}
// triangle is only a special case of a polygon
// so use a polygon
Polygon viewerPoly = new Polygon(currentColor);
// parse rings
Ring extRing = new Ring();
viewerPoly.setExteriorRing(extRing);
mapRing(triangle.getExterior(), extRing);
currentPolygons.add(viewerPoly);
}
private void mapRing(AbstractRingProperty gmlRing, Ring viewerRing) {
if (gmlRing == null || gmlRing.getObject() == null) {
return;
}
AbstractRing ringGeometry = gmlRing.getObject();
currentRing = viewerRing;
// jump to LinearRing or Ring visit
ringGeometry.accept(this);
}
@Override
public void visit(LinearRing linearRing) {
if (linearRing.getControlPoints() != null) {
int dimension = getDimension(linearRing);
parseControlPoints(linearRing.getControlPoints(), dimension);
}
}
private int getDimension(LinearRing linearRing) {
int dimension = 3;
if (linearRing.getSrsDimension() != null) {
dimension = linearRing.getSrsDimension();
}
return dimension;
}
private void parseControlPoints(GeometricPositionList controlPoints, int dimension) {
List<Double> coords = controlPoints.toCoordinateList3D();
switch (dimension) {
case 1:
for (int i = 0; i < coords.size(); i++) {
createVertex(coords.get(i), 0, 0);
}
break;
case 2:
for (int i = 0; i < coords.size(); i = i + 2) {
createVertex(coords.get(i + 0), coords.get(i + 1), 0);
}
break;
case 3:
for (int i = 0; i < coords.size(); i = i + 3) {
createVertex(coords.get(i + 0), coords.get(i + 1), coords.get(i + 2));
}
break;
default:
throw new UnsupportedOperationException("Cannot parse Coordinates with dimension:" + dimension);
}
}
private void createVertex(double x, double y, double z) {
// transform into utm, if available
BasicCoordinateTransform trans = config.getTargetTransform();
if (trans != null) {
p1.setValue(x, y);
trans.transform(p1, p2);
x = p2.x;
y = p2.y;
}
Vector3d v = new Vector3d(x, y, z);
currentRing.addPoint(v);
}
@Override
public void visit(AbstractBuilding ab) {
super.visit(ab);
DeprecatedPropertiesOfAbstractBuilding deprecatedProperties = ab.getDeprecatedProperties();
parseAbstractGeometry(deprecatedProperties.getLod1MultiSurface(), lod1Polygons, wallColor);
parseAbstractGeometry(ab.getLod2MultiSurface(), lod2Polygons, wallColor);
parseAbstractGeometry(ab.getLod3MultiSurface(), lod3Polygons, wallColor);
parseAbstractGeometry(deprecatedProperties.getLod4MultiSurface(), lod4Polygons, wallColor);
parseAbstractGeometry(ab.getLod1Solid(), lod1Polygons, wallColor);
parseAbstractGeometry(ab.getLod2Solid(), lod2Polygons, wallColor);
parseAbstractGeometry(ab.getLod3Solid(), lod3Polygons, wallColor);
parseAbstractGeometry(deprecatedProperties.getLod4Solid(), lod4Polygons, wallColor);
}
@Override
public void visit(TINRelief tinRelief) {
super.visit(tinRelief);
if (tinRelief.getTin() == null || tinRelief.getTin().getObject() == null) {
return;
}
TriangulatedSurface object = tinRelief.getTin().getObject();
currentPolygons = lod1Polygons;
currentColor = landColor;
object.accept(this);
currentPolygons = null;
currentColor = null;
}
@Override
public void visit(WaterBody wb) {
super.visit(wb);
DeprecatedPropertiesOfWaterBody deprecatedProperties = wb.getDeprecatedProperties();
parseAbstractGeometry(deprecatedProperties.getLod1MultiSurface(), lod1Polygons, waterColor);
parseAbstractGeometry(wb.getLod1Solid(), lod1Polygons, waterColor);
parseAbstractGeometry(wb.getLod2Solid(), lod2Polygons, waterColor);
parseAbstractGeometry(wb.getLod3Solid(), lod3Polygons, waterColor);
parseAbstractGeometry(deprecatedProperties.getLod4Solid(), lod4Polygons, waterColor);
}
@Override
public void visit(PlantCover pc) {
super.visit(pc);
DeprecatedPropertiesOfPlantCover deprecatedProperties = pc.getDeprecatedProperties();
parseAbstractGeometry(deprecatedProperties.getLod1MultiSurface(), lod1Polygons, vegetationColor);
parseAbstractGeometry(pc.getLod2MultiSurface(), lod2Polygons, vegetationColor);
parseAbstractGeometry(pc.getLod3MultiSurface(), lod3Polygons, vegetationColor);
parseAbstractGeometry(deprecatedProperties.getLod4MultiSurface(), lod4Polygons, vegetationColor);
parseAbstractGeometry(deprecatedProperties.getLod1MultiSolid(), lod1Polygons, vegetationColor);
parseAbstractGeometry(deprecatedProperties.getLod2MultiSolid(), lod2Polygons, vegetationColor);
parseAbstractGeometry(deprecatedProperties.getLod3MultiSolid(), lod3Polygons, vegetationColor);
parseAbstractGeometry(deprecatedProperties.getLod4MultiSolid(), lod4Polygons, vegetationColor);
}
@Override
public void visit(SolitaryVegetationObject svo) {
super.visit(svo);
DeprecatedPropertiesOfSolitaryVegetationObject deprecatedProperties = svo.getDeprecatedProperties();
parseAbstractGeometry(deprecatedProperties.getLod1Geometry(), lod1Polygons, vegetationColor);
parseAbstractGeometry(deprecatedProperties.getLod2Geometry(), lod2Polygons, vegetationColor);
parseAbstractGeometry(deprecatedProperties.getLod3Geometry(), lod3Polygons, vegetationColor);
parseAbstractGeometry(deprecatedProperties.getLod4Geometry(), lod4Polygons, vegetationColor);
parseImplicitGeometry(svo.getLod1ImplicitRepresentation(), lod1Polygons, vegetationColor);
parseImplicitGeometry(svo.getLod2ImplicitRepresentation(), lod2Polygons, vegetationColor);
parseImplicitGeometry(svo.getLod3ImplicitRepresentation(), lod3Polygons, vegetationColor);
parseImplicitGeometry(deprecatedProperties.getLod4ImplicitRepresentation(), lod4Polygons, vegetationColor);
}
@Override
public void visit(AbstractThematicSurface abs) {
super.visit(abs);
Color color = getBuildingSurfaceColor(abs);
DeprecatedPropertiesOfAbstractThematicSurface deprecatedProperties = abs.getDeprecatedProperties();
parseAbstractGeometry(abs.getLod2MultiSurface(), lod2Polygons, color);
parseAbstractGeometry(abs.getLod3MultiSurface(), lod3Polygons, color);
parseAbstractGeometry(deprecatedProperties.getLod4MultiSurface(), lod4Polygons, color);
}
@Override
public void visit(LandUse landUse) {
super.visit(landUse);
DeprecatedPropertiesOfAbstractThematicSurface deprecatedProperties = landUse.getDeprecatedProperties();
parseAbstractGeometry(landUse.getLod1MultiSurface(), lod1Polygons, landColor);
parseAbstractGeometry(landUse.getLod2MultiSurface(), lod2Polygons, landColor);
parseAbstractGeometry(landUse.getLod3MultiSurface(), lod3Polygons, landColor);
parseAbstractGeometry(deprecatedProperties.getLod4MultiSurface(), lod4Polygons, landColor);
}
@Override
public void visit(AuxiliaryTrafficArea ata) {
super.visit(ata);
DeprecatedPropertiesOfAbstractThematicSurface deprecatedProperties = ata.getDeprecatedProperties();
parseAbstractGeometry(ata.getLod2MultiSurface(), lod2Polygons, transportationColor);
parseAbstractGeometry(ata.getLod3MultiSurface(), lod3Polygons, transportationColor);
parseAbstractGeometry(deprecatedProperties.getLod4MultiSurface(), lod4Polygons, transportationColor);
}
@Override
public void visit(TrafficArea ta) {
super.visit(ta);
DeprecatedPropertiesOfAbstractThematicSurface deprecatedProperties = ta.getDeprecatedProperties();
parseAbstractGeometry(ta.getLod2MultiSurface(), lod2Polygons, transportationColor);
parseAbstractGeometry(ta.getLod3MultiSurface(), lod3Polygons, transportationColor);
parseAbstractGeometry(deprecatedProperties.getLod4MultiSurface(), lod4Polygons, transportationColor);
}
@Override
public void visit(TransportationComplex tc) {
super.visit(tc);
DeprecatedPropertiesOfAbstractTransportationSpace deprecatedProperties = tc.getDeprecatedProperties();
parseAbstractGeometry(deprecatedProperties.getLod1MultiSurface(), lod1Polygons, transportationColor);
parseAbstractGeometry(tc.getLod2MultiSurface(), lod2Polygons, transportationColor);
parseAbstractGeometry(tc.getLod3MultiSurface(), lod3Polygons, transportationColor);
parseAbstractGeometry(deprecatedProperties.getLod4MultiSurface(), lod4Polygons, transportationColor);
}
private Color getBuildingSurfaceColor(AbstractThematicSurface abs) {
if (abs instanceof RoofSurface) {
return roofColor;
}
if (abs instanceof GroundSurface) {
return groundColor;
}
return wallColor;
}
@Override
public void visit(AbstractFillingElement ao) {
super.visit(ao);
Color color = getOpeningColor(ao);
parseAbstractGeometry(ao.getLod1Solid(), lod1Polygons, color);
parseAbstractGeometry(ao.getLod2Solid(), lod2Polygons, color);
parseAbstractGeometry(ao.getLod3Solid(), lod3Polygons, color);
parseAbstractGeometry(ao.getLod2MultiSurface(), lod2Polygons, color);
parseAbstractGeometry(ao.getLod3MultiSurface(), lod3Polygons, color);
parseImplicitGeometry(ao.getLod1ImplicitRepresentation(), lod1Polygons, color);
parseImplicitGeometry(ao.getLod2ImplicitRepresentation(), lod2Polygons, color);
parseImplicitGeometry(ao.getLod3ImplicitRepresentation(), lod3Polygons, color);
}
private Color getOpeningColor(AbstractFillingElement ao) {
if (ao instanceof Door) {
return doorColor;
}
if (ao instanceof Window) {
return windowColor;
}
return wallColor;
}
@Override
public void visit(AbstractBridge ab) {
super.visit(ab);
DeprecatedPropertiesOfAbstractBridge deprecatedProperties = ab.getDeprecatedProperties();
parseAbstractGeometry(deprecatedProperties.getLod1MultiSurface(), lod1Polygons, bridgeColor);
parseAbstractGeometry(ab.getLod2MultiSurface(), lod2Polygons, bridgeColor);
parseAbstractGeometry(ab.getLod3MultiSurface(), lod3Polygons, bridgeColor);
parseAbstractGeometry(deprecatedProperties.getLod4MultiSurface(), lod4Polygons, bridgeColor);
parseAbstractGeometry(ab.getLod1Solid(), lod1Polygons, bridgeColor);
parseAbstractGeometry(ab.getLod2Solid(), lod2Polygons, bridgeColor);
parseAbstractGeometry(ab.getLod3Solid(), lod3Polygons, bridgeColor);
parseAbstractGeometry(deprecatedProperties.getLod4Solid(), lod4Polygons, bridgeColor);
}
@Override
public void visit(BuildingInstallation bi) {
super.visit(bi);
DeprecatedPropertiesOfBuildingInstallation deprecatedProperties = bi.getDeprecatedProperties();
parseAbstractGeometry(deprecatedProperties.getLod2Geometry(), lod2Polygons, wallColor);
parseAbstractGeometry(deprecatedProperties.getLod3Geometry(), lod3Polygons, wallColor);
parseAbstractGeometry(deprecatedProperties.getLod4Geometry(), lod4Polygons, wallColor);
parseImplicitGeometry(bi.getLod2ImplicitRepresentation(), lod2Polygons, wallColor);
parseImplicitGeometry(bi.getLod3ImplicitRepresentation(), lod3Polygons, wallColor);
parseImplicitGeometry(deprecatedProperties.getLod4ImplicitRepresentation(), lod4Polygons, wallColor);
}
private void parseImplicitGeometry(ImplicitGeometryProperty irp, List<Polygon> polygons, Color color) {
if (irp == null || irp.getObject() == null) {
return;
}
ImplicitGeometry implicitGeometry = irp.getObject();
parseAbstractGeometry(implicitGeometry.getRelativeGeometry(), polygons, color);
}
private void parseAbstractGeometry(GeometryProperty<? extends AbstractGeometry> geom, List<Polygon> polygons,
Color color) {
if (geom == null || geom.getObject() == null) {
return;
}
currentColor = color;
currentPolygons = polygons;
geom.getObject().accept(this);
currentColor = null;
currentPolygons = null;
}
public List<Polygon> getLod1Polygons() {
return lod1Polygons;
}
public List<Polygon> getLod2Polygons() {
return lod2Polygons;
}
public List<Polygon> getLod3Polygons() {
return lod3Polygons;
}
public List<Polygon> getLod4Polygons() {
return lod4Polygons;
}
public void movePolygonsBy(Vector3d center) {
movePolygonsBy(lod1Polygons, center);
movePolygonsBy(lod2Polygons, center);
movePolygonsBy(lod3Polygons, center);
movePolygonsBy(lod4Polygons, center);
}
private void movePolygonsBy(List<Polygon> polygons, Vector3d center) {
for (Polygon p : polygons) {
for (Vector3d v : p.getExteriorRing().getVertices()) {
movePointBy(v, center);
}
for (Ring r : p.getInteriorRings()) {
for (Vector3d v : r.getVertices()) {
movePointBy(v, center);
}
}
}
}
private void movePointBy(Vector3d v, Vector3d center) {
v.setX(v.getX() - center.getX());
v.setY(v.getY() - center.getY());
v.setZ(v.getZ() - center.getZ());
}
}
/*-
* Copyright 2021 Hochschule für Technik Stuttgart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.hft.stuttgart.citygml.viewer.parser;
import java.util.EventListener;
/**
* Implementations of this listener will be informed about the (file) loading progress of ObservedInputStream
* instances. You have to register your implementation of this interface at the ObservedInputStream instance.
*
* @author Marcel Bruse
*/
public interface InputStreamListener extends EventListener {
/**
* This method will be called by the observed input stream when a new chunk of bytes has been read.
*
* @param progress Should be a value between 0 and 1. 0 means no progress and 1 means loading finished.
*/
public void updateProgress(float progress);
}
/*-
* Copyright 2021 Hochschule für Technik Stuttgart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.hft.stuttgart.citygml.viewer.parser;
public enum LOD {
LOD_1, LOD_2, LOD_3, LOD_4
}
/*-
* Copyright 2021 Hochschule für Technik Stuttgart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.hft.stuttgart.citygml.viewer.parser;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
/**
* This ObservedInputStream will update its registered listeners about the reading process of the underlying file.
* Every time when the underlying input stream is read the registered listeners will be informed and updated with the
* new progress.
*
* @author Marcel Bruse
*/
public class ObservedInputStream extends FilterInputStream {
/** List of registered progress listeners. */
private ArrayList<InputStreamListener> listeners = new ArrayList<>();
/** The number of bytes of the file to be read. */
private long fileLength;
/** The number of bytes read so far. */
private long location;
/**
* Instantiates the ObservedInputStream with a given file handle.
*
* @param file The file that should be loaded.
* @throws FileNotFoundException If there is on file, you will get some of this.
*/
public ObservedInputStream(File file) throws FileNotFoundException {
super(new BufferedInputStream(new FileInputStream(file)));
fileLength = file.length();
}
public ObservedInputStream(String fileName) throws FileNotFoundException {
this(new File(fileName));
}
public ObservedInputStream(InputStream in, long fileLength) {
super(new BufferedInputStream(in));
this.fileLength = fileLength;
}
/**
* Calls all the registered listeners if the file loading process makes any progress.
* It calculates and passes the progress in percent.
*/
private void updateProgress() {
for (InputStreamListener l : listeners) {
l.updateProgress((float) location / fileLength);
}
}
/**
* Calls super and counts the number of read bytes.
*
* @see FilterInputStream
*/
@Override
public int read() throws IOException {
final int data = super.read();
if (data != -1) {
location++;
updateProgress();
}
return data;
}
/**
* Calls super and counts the number of read bytes.
*
* @see FilterInputStream
*/
@Override
public int read(byte[] b, int off, int len) throws IOException {
final int byteCount = super.read(b, off, len);
if (byteCount != -1) {
location += byteCount;
updateProgress();
}
return byteCount;
}
/**
* Registers listeners. Every registered listener will get informed if the file loading process makes any
* progress.
*
* @param ps The progress listener to be registered.
*/
public void addListener(InputStreamListener ps) {
listeners.add(ps);
}
}
/*-
* Copyright 2021 Hochschule für Technik Stuttgart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.hft.stuttgart.citygml.viewer.parser;
import java.io.Serializable;
import org.locationtech.proj4j.BasicCoordinateTransform;
import org.locationtech.proj4j.CRSFactory;
import org.locationtech.proj4j.CoordinateReferenceSystem;
/**
* Container class to store the configuration needed to parse a file. Also
* contains information on transformation of vertices.
*
* @author Matthias Betz
*
*/
public class ParserConfiguration implements Serializable {
private static final long serialVersionUID = 6209047092991074661L;
private static final CRSFactory CRS_FACTORY = new CRSFactory();
private transient BasicCoordinateTransform targetTransform = null;
private transient BasicCoordinateTransform originalTransform = null;
private String targetTransformString;
private String originalTransformString;
private boolean hasTransformation = false;
public void setCoordinateSystem(CoordinateReferenceSystem crs, CoordinateReferenceSystem tgtCrs) {
if (crs != null && tgtCrs != null) {
hasTransformation = true;
targetTransformString = tgtCrs.getParameterString();
originalTransformString = crs.getParameterString();
targetTransform = new BasicCoordinateTransform(crs, tgtCrs);
originalTransform = new BasicCoordinateTransform(tgtCrs, crs);
}
}
public BasicCoordinateTransform getTargetTransform() {
if (hasTransformation && targetTransform == null) {
createCoordinateTransforms();
}
return targetTransform;
}
private void createCoordinateTransforms() {
CoordinateReferenceSystem tgtCrs = null;
CoordinateReferenceSystem crs = null;
synchronized (CRS_FACTORY) {
tgtCrs = CRS_FACTORY.createFromParameters("Target", targetTransformString);
crs = CRS_FACTORY.createFromParameters("Original", originalTransformString);
}
targetTransform = new BasicCoordinateTransform(crs, tgtCrs);
originalTransform = new BasicCoordinateTransform(tgtCrs, crs);
}
public BasicCoordinateTransform getOriginalTransform() {
if (hasTransformation && originalTransform == null) {
createCoordinateTransforms();
}
return originalTransform;
}
}
#version 330
// Incoming interpolated (between vertices) color.
in vec3 interpolatedColor;
// Outgoing final color.
layout (location = 0) out vec4 outputColor;
void main()
{
// We simply pad the interpolatedColor
outputColor = vec4(interpolatedColor, 1);
}
\ No newline at end of file
#version 330
// Incoming vertex position, Model Space.
layout (location = 0) in vec3 position;
// Incoming vertex color.
layout (location = 1) in vec3 color;
// Projection, view and model matrix.
uniform mat4 projViewModel;
// Outgoing color.
out vec3 interpolatedColor;
void main() {
// Normally gl_Position is in Clip Space and we calculate it by multiplying together all the matrices
gl_Position = projViewModel * vec4(position, 1);
// We assign the color to the outgoing variable.
interpolatedColor = color;
}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment