Commit 5d40c7b6 authored by Matthias Betz's avatar Matthias Betz
Browse files

CityDoctor2 validation open source release

parents
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.datastructure;
import java.util.ArrayList;
import java.util.List;
import org.citygml4j.factory.GMLGeometryFactory;
import org.citygml4j.model.citygml.building.AbstractBoundarySurface;
import org.citygml4j.model.citygml.building.BoundarySurfaceProperty;
import org.citygml4j.model.gml.geometry.aggregates.MultiSurface;
import org.citygml4j.model.gml.geometry.aggregates.MultiSurfaceProperty;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils;
public class BuildingInstallation extends CityObject {
private static final long serialVersionUID = 1576237433322680191L;
private List<BoundarySurface> boundarySurfaces = new ArrayList<>(4);
private org.citygml4j.model.citygml.building.BuildingInstallation gmlBi;
private AbstractBuilding parent;
public void reCreateGeometries(GMLGeometryFactory factory, ParserConfiguration config) {
for (Geometry geom : getGeometries()) {
if (geom.getType() == GeometryType.MULTI_SURFACE) {
MultiSurface ms = CityGmlUtils.createMultiSurface(geom, factory, config);
setGeometryAccordingToLod(geom.getLod(), new MultiSurfaceProperty(ms));
} else {
throw new IllegalStateException("BuildingInstallation can only have MultiSurface geometries");
}
}
for (BoundarySurface bs : boundarySurfaces) {
bs.reCreateGeometries(factory, config);
}
}
@Override
public CityObject copy() {
throw new UnsupportedOperationException("Cannot copy BuildingInstallation");
}
private void setGeometryAccordingToLod(Lod lod, MultiSurfaceProperty ms) {
switch (lod) {
case LOD2:
gmlBi.setLod2Geometry(ms);
break;
case LOD3:
gmlBi.setLod3Geometry(ms);
break;
case LOD4:
gmlBi.setLod4Geometry(ms);
break;
default:
throw new IllegalStateException("Found geometry with LOD other than LOD2,"
+ " LOD3, LOD4, which is illegal for BuildingInstallations: " + lod);
}
}
@Override
public void accept(Check c) {
super.accept(c);
if (c.canExecute(this)) {
c.check(this);
}
for (BoundarySurface bs : boundarySurfaces) {
bs.accept(c);
}
}
@Override
public void clearAllContainedCheckResults() {
super.clearCheckResults();
for (BoundarySurface bs : boundarySurfaces) {
bs.clearAllContainedCheckResults();
}
}
@Override
public void collectContainedErrors(List<CheckError> errors) {
super.collectContainedErrors(errors);
for (BoundarySurface bs : boundarySurfaces) {
bs.collectContainedErrors(errors);
}
}
@Override
public boolean containsAnyError() {
boolean hasError = super.containsAnyError();
if (hasError) {
return true;
}
for (BoundarySurface bs : boundarySurfaces) {
if (bs.containsAnyError()) {
return true;
}
}
return false;
}
@Override
public boolean containsError(CheckId checkIdentifier) {
boolean hasError = super.containsError(checkIdentifier);
if (hasError) {
return true;
}
for (BoundarySurface bs : boundarySurfaces) {
if (bs.containsError(checkIdentifier)) {
return true;
}
}
return false;
}
public void setParent(AbstractBuilding parent) {
this.parent = parent;
}
public AbstractBuilding getParent() {
return parent;
}
public void addBoundarySurface(BoundarySurface bs) {
boundarySurfaces.add(bs);
bs.setParent(this);
}
public void unsetGmlGeometries() {
gmlBi.unsetLod2Geometry();
gmlBi.unsetLod3Geometry();
gmlBi.unsetLod4Geometry();
for (BoundarySurface bs : boundarySurfaces) {
bs.unsetGmlGeometries();
}
}
public void setGmlObject(org.citygml4j.model.citygml.building.BuildingInstallation gmlBi) {
this.gmlBi = gmlBi;
}
@Override
public String toString() {
return "BuildingInstallation [id=" + getGmlId() + "]";
}
void anonymize() {
setGmlId(GmlId.generateId());
gmlBi = new org.citygml4j.model.citygml.building.BuildingInstallation();
gmlBi.setId(getGmlId().getGmlString());
for (BoundarySurface bs : boundarySurfaces) {
bs.anonymize();
gmlBi.addBoundedBySurface(new BoundarySurfaceProperty((AbstractBoundarySurface) bs.getGmlObject()));
}
}
@Override
public org.citygml4j.model.citygml.building.BuildingInstallation getGmlObject() {
return gmlBi;
}
@Override
public FeatureType getFeatureType() {
return FeatureType.BUILDING_INSTALLATION;
}
public List<BoundarySurface> getBoundarySurfaces() {
if (boundarySurfaces == null) {
boundarySurfaces = new ArrayList<>(4);
}
return boundarySurfaces;
}
}
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.datastructure;
public class BuildingPart extends AbstractBuilding {
private static final long serialVersionUID = 8200322261958679163L;
private Building parent;
public BuildingPart(Building parent) {
this.parent = parent;
parent.addBuildingPart(this);
}
@Override
public CityObject copy() {
throw new UnsupportedOperationException("Cannot copy BuildingPart");
}
public Building getParent() {
return parent;
}
@Override
public FeatureType getFeatureType() {
return FeatureType.BUILDING_PART;
}
@Override
public String toString() {
return "BuildingPart [id=" + getGmlId() + "]";
}
}
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.datastructure;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import org.citygml4j.CityGMLContext;
import org.citygml4j.builder.jaxb.CityGMLBuilder;
import org.citygml4j.builder.jaxb.CityGMLBuilderException;
import org.citygml4j.factory.GMLGeometryFactory;
import org.citygml4j.model.citygml.core.CityModel;
import org.citygml4j.model.module.citygml.CityGMLVersion;
import org.citygml4j.xml.io.CityGMLOutputFactory;
import org.citygml4j.xml.io.writer.CityGMLWriteException;
import org.citygml4j.xml.io.writer.CityModelInfo;
import org.citygml4j.xml.io.writer.CityModelWriter;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
/**
* The complete CityGML model containing all features that are used in
* CityDoctor
*
* @author Matthias Betz
*
*/
public class CityDoctorModel {
private static final String COULD_NOT_FIND_FEATURE = "Could not find feature: ";
private List<Building> buildings;
private List<Vegetation> vegetation;
private List<BridgeObject> bridges;
private List<LandObject> land;
private List<TransportationObject> roads;
private List<WaterObject> water;
private CityModel cModel;
private ParserConfiguration config;
private String fileName;
private File file;
private List<CheckError> globalErrors;
public CityDoctorModel(ParserConfiguration config, File file) {
if (config == null) {
throw new IllegalArgumentException("Parser configuration may not be null");
}
this.fileName = file.getName();
this.config = config;
this.file = file;
buildings = new ArrayList<>();
vegetation = new ArrayList<>();
bridges = new ArrayList<>();
land = new ArrayList<>();
roads = new ArrayList<>();
water = new ArrayList<>();
globalErrors = new ArrayList<>();
}
public void addGlobalError(CheckError err) {
globalErrors.add(err);
}
public void addGlobalErrors(List<? extends CheckError> generalErrors) {
globalErrors.addAll(generalErrors);
}
public List<CheckError> getGlobalErrors() {
return globalErrors;
}
public File getFile() {
return file;
}
public Stream<CityObject> createFeatureStream() {
return Stream.of(buildings.stream(), vegetation.stream(), bridges.stream(), land.stream(), roads.stream(),
water.stream()).flatMap(co -> co);
}
public void saveAs(String file) throws CityGMLBuilderException, CityGMLWriteException {
CityGMLContext gmlContext = CityGMLContext.getInstance();
CityGMLBuilder builder = gmlContext.createCityGMLBuilder();
CityGMLOutputFactory factory = builder.createCityGMLOutputFactory();
try (CityModelWriter writer = factory.createCityModelWriter(new File(file))) {
writer.setCityModelInfo(new CityModelInfo(cModel));
writer.setIndentString(" ");
writer.setPrefixes(CityGMLVersion.DEFAULT);
writer.setSchemaLocations(CityGMLVersion.DEFAULT);
writer.writeStartDocument();
GMLGeometryFactory gmlFactory = new GMLGeometryFactory();
storeCityObjects(writer, buildings, gmlFactory);
storeCityObjects(writer, vegetation, gmlFactory);
storeCityObjects(writer, bridges, gmlFactory);
storeCityObjects(writer, land, gmlFactory);
storeCityObjects(writer, roads, gmlFactory);
storeCityObjects(writer, water, gmlFactory);
writer.writeEndDocument();
}
}
private void storeCityObjects(CityModelWriter writer, List<? extends CityObject> cos, GMLGeometryFactory gmlFactory)
throws CityGMLWriteException {
for (CityObject co : cos) {
co.reCreateGeometries(gmlFactory, config);
writer.writeFeatureMember(co.getGmlObject());
co.unsetGmlGeometries();
}
}
public String getFileName() {
return fileName;
}
public void addBridge(BridgeObject coBridge) {
bridges.add(coBridge);
}
public void addBuilding(Building coBuilding) {
buildings.add(coBuilding);
}
public List<Building> getBuildings() {
return buildings;
}
public List<BridgeObject> getBridges() {
return bridges;
}
public void setCityModel(CityModel cModel) {
this.cModel = cModel;
}
public CityModel getCityModel() {
return cModel;
}
public List<TransportationObject> getTransportation() {
return roads;
}
public List<WaterObject> getWater() {
return water;
}
public List<LandObject> getLand() {
return land;
}
public List<Vegetation> getVegetation() {
return vegetation;
}
public void addWater(WaterObject wo) {
water.add(wo);
}
public void addLand(LandObject lo) {
land.add(lo);
}
public void addVegetation(Vegetation veg) {
vegetation.add(veg);
}
public void addTransportation(TransportationObject to) {
roads.add(to);
}
public int getNumberOfFeatures() {
return buildings.size() + vegetation.size() + bridges.size() + land.size() + roads.size() + water.size();
}
public ParserConfiguration getParserConfig() {
return config;
}
public void replaceFeature(CityObject currentFeature, CityObject nextFeature) {
if (nextFeature instanceof Building) {
replaceBuilding(currentFeature, nextFeature);
} else if (nextFeature instanceof BridgeObject) {
replaceBridge(currentFeature, nextFeature);
} else if (nextFeature instanceof TransportationObject) {
replaceTransportationObject(currentFeature, nextFeature);
} else if (nextFeature instanceof Vegetation) {
replaceVegetation(currentFeature, nextFeature);
} else if (nextFeature instanceof LandObject) {
replaceLandObject(currentFeature, nextFeature);
} else if (nextFeature instanceof WaterObject) {
replaceWaterObject(currentFeature, nextFeature);
}
}
private void replaceWaterObject(CityObject currentFeature, CityObject nextFeature) {
int index = water.indexOf(currentFeature);
if (index == -1) {
throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in water objects");
}
water.set(index, (WaterObject) nextFeature);
}
private void replaceLandObject(CityObject currentFeature, CityObject nextFeature) {
int index = land.indexOf(currentFeature);
if (index == -1) {
throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in land objects");
}
land.set(index, (LandObject) nextFeature);
}
private void replaceVegetation(CityObject currentFeature, CityObject nextFeature) {
int index = vegetation.indexOf(currentFeature);
if (index == -1) {
throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in vegetation");
}
vegetation.set(index, (Vegetation) nextFeature);
}
private void replaceTransportationObject(CityObject currentFeature, CityObject nextFeature) {
int index = roads.indexOf(currentFeature);
if (index == -1) {
throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in transportation objects");
}
roads.set(index, (TransportationObject) nextFeature);
}
private void replaceBridge(CityObject currentFeature, CityObject nextFeature) {
int index = bridges.indexOf(currentFeature);
if (index == -1) {
throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in bridges");
}
bridges.set(index, (BridgeObject) nextFeature);
}
private void replaceBuilding(CityObject currentFeature, CityObject nextFeature) {
int index = buildings.indexOf(currentFeature);
if (index == -1) {
throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in buildings");
}
buildings.set(index, (Building) nextFeature);
}
}
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.datastructure;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import org.citygml4j.factory.GMLGeometryFactory;
import org.citygml4j.model.citygml.core.AbstractCityObject;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
/**
* Abstract class for general features such as Buildings, Water objects, ...
* <br>
* Contains the geometries
*
* @author Matthias Betz
*
*/
public abstract class CityObject extends GmlElement {
private static final long serialVersionUID = 651712070755024188L;
private List<Geometry> geometryList = new ArrayList<>();
/**
* Recreates the CityGML4j geometry in this object. The CityGML4j geometry is
* deleted from the data structure as was mapped to the city doctor data
* structure. For writing CityGML files the geometry has to be restored. Also
* maps changes to geometry back to CityGML4j
*
* @param factory needed to create CityGML4j data structures
* @param config contains information whether a city doctor point was
* transformed to a different coordinate system. It would need to
* be transformed to the original coordinate system
*/
public abstract void reCreateGeometries(GMLGeometryFactory factory, ParserConfiguration config);
/**
* Getter for the CityGML4j data object
*
* @return the CityGML4j data object
*/
public abstract AbstractCityObject getGmlObject();
/**
* Remove the CityGML4j geometries from the data structure to save memory
*/
public abstract void unsetGmlGeometries();
@Override
public CityObject copy() {
return (CityObject) super.copy();
}
public void addGeometry(Geometry geom) {
Objects.requireNonNull(geom);
geometryList.add(geom);
geom.setParent(this);
}
public List<Geometry> getGeometries() {
return geometryList;
}
public void removeGeometry(Lod lod) {
Iterator<Geometry> it = geometryList.iterator();
while (it.hasNext()) {
Geometry next = it.next();
if (next.getLod() == lod) {
it.remove();
}
}
}
public Geometry getHighestLodGeometry() {
Geometry highestLodGeometry = null;
Lod highestLod = null;
for (Geometry geom : geometryList) {
if (highestLod == null || geom.getLod().isHigher(highestLod)) {
highestLodGeometry = geom;
highestLod = geom.getLod();
}
}
return highestLodGeometry;
}
public abstract FeatureType getFeatureType();
@Override
public void accept(Check c) {
super.accept(c);
if (c.canExecute(this)) {
c.check(this);
}
for (Geometry geom : geometryList) {
geom.accept(c);
}
}
@Override
public boolean containsError(CheckId checkIdentifier) {
boolean hasError = super.containsError(checkIdentifier);
if (hasError) {
return true;
}
for (Geometry geom : geometryList) {
if (geom.containsError(checkIdentifier)) {
return true;
}
}
return false;
}
@Override
public void clearAllContainedCheckResults() {
super.clearCheckResults();
for (Geometry geom : geometryList) {
geom.clearAllContainedCheckResults();
}
}
@Override
public void collectContainedErrors(List<CheckError> errors) {
super.collectContainedErrors(errors);
for (Geometry geom : geometryList) {
geom.collectContainedErrors(errors);
}
}
@Override
public boolean containsAnyError() {
boolean hasError = super.containsAnyError();
if (hasError) {
return true;
}
for (Geometry geom : geometryList) {
if (geom.containsAnyError()) {
return true;
}
}
return false;
}
public Geometry getGeometry(GeometryType type, Lod lod) {
for (Geometry geom : geometryList) {
if (geom.getType() == type && geom.getLod() == lod) {
return geom;
}
}
return null;
}
}
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.datastructure;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.Checkable;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.tesselation.TesselatedPolygon;
import de.hft.stuttgart.citydoctor2.tesselation.JoglTesselator;
/**
* A polygon that actually contains the rings and points representing the
* geometry
*
* @author Matthias Betz
*
*/
public class ConcretePolygon extends Polygon {
private static final long serialVersionUID = -2208347892270418372L;
private LinearRing exterior;
private List<LinearRing> innerRings;
private BoundarySurface partOfSurface;
private BuildingInstallation partfOfInstallation;
private Geometry parent;
private LinkedPolygon linkedFromPolygon;
/*
* (non-Javadoc)
*
* @see de.hft.stuttgart.citydoctor2.datastructure.Polygon#calculateNormal()
*/
@Override
public Vector3d calculateNormal() {
return exterior.calculateNormal();
}
/*
* (non-Javadoc)
*
* @see de.hft.stuttgart.citydoctor2.datastructure.Polygon#tesselate()
*/
@Override
public TesselatedPolygon tesselate() {
return JoglTesselator.tesselatePolygon(this);
}
/*
* (non-Javadoc)
*
* @see de.hft.stuttgart.citydoctor2.datastructure.Polygon#getExteriorRing()
*/
@Override
public LinearRing getExteriorRing() {
return exterior;
}
/*
* (non-Javadoc)
*
* @see de.hft.stuttgart.citydoctor2.datastructure.Polygon#getInnerRings()
*/
@Override
public List<LinearRing> getInnerRings() {
if (innerRings == null) {
return Collections.emptyList();
}
return innerRings;
}
/*
* (non-Javadoc)
*
* @see
* de.hft.stuttgart.citydoctor2.datastructure.Polygon#isPointInsideExteriorRing(
* de.hft.stuttgart.citydoctor2.math.Vector3d)
*/
@Override
public boolean isPointInsideExteriorRing(Vector3d v) {
return exterior.isPointInside(v);
}
/*
* (non-Javadoc)
*
* @see de.hft.stuttgart.citydoctor2.datastructure.Polygon#getParent()
*/
@Override
public Geometry getParent() {
return parent;
}
/*
* (non-Javadoc)
*
* @see
* de.hft.stuttgart.citydoctor2.datastructure.Polygon#setParent(de.hft.stuttgart
* .citydoctor2.datastructure.Geometry)
*/
@Override
public void setParent(Geometry geometry) {
parent = geometry;
}
/*
* (non-Javadoc)
*
* @see
* de.hft.stuttgart.citydoctor2.datastructure.Polygon#setExteriorRing(de.hft.
* stuttgart.citydoctor2.datastructure.LinearRing)
*/
@Override
public void setExteriorRing(LinearRing extRing) {
exterior = extRing;
extRing.setParent(this);
}
/*
* (non-Javadoc)
*
* @see
* de.hft.stuttgart.citydoctor2.datastructure.Polygon#accept(de.hft.stuttgart.
* citydoctor2.check.Check)
*/
@Override
public void accept(Check c) {
if (c.canExecute(this)) {
c.check(this);
}
if (exterior != null) {
exterior.accept(c);
}
if (innerRings != null) {
for (LinearRing lr : innerRings) {
lr.accept(c);
}
}
setValidated(true);
}
@Override
public Class<? extends Checkable> getCheckClass() {
return Polygon.class;
}
/*
* (non-Javadoc)
*
* @see
* de.hft.stuttgart.citydoctor2.datastructure.Polygon#isPolygonConnectedViaPoint
* (de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon)
*/
@Override
public boolean isPolygonConnectedViaPoint(Polygon other) {
return exterior.isRingConnectedViaPoint(other.getExteriorRing());
}
/*
* (non-Javadoc)
*
* @see de.hft.stuttgart.citydoctor2.datastructure.Polygon#containsAnyError()
*/
@Override
public boolean containsAnyError() {
if (super.containsAnyError()) {
return true;
}
if (exterior.containsAnyError()) {
return true;
}
if (innerRings != null) {
for (LinearRing lr : innerRings) {
if (lr.containsAnyError()) {
return true;
}
}
}
return false;
}
/*
* (non-Javadoc)
*
* @see
* de.hft.stuttgart.citydoctor2.datastructure.Polygon#collectContainedErrors(
* java.util.List)
*/
@Override
public void collectContainedErrors(List<CheckError> errors) {
super.collectContainedErrors(errors);
if (exterior != null) {
exterior.collectContainedErrors(errors);
}
if (innerRings != null) {
for (LinearRing lr : innerRings) {
lr.collectContainedErrors(errors);
}
}
}
/*
* (non-Javadoc)
*
* @see de.hft.stuttgart.citydoctor2.datastructure.Polygon#
* clearAllContainedCheckResults()
*/
@Override
public void clearAllContainedCheckResults() {
super.clearCheckResults();
if (exterior != null) {
exterior.clearAllContainedCheckResults();
}
if (innerRings != null) {
for (LinearRing lr : innerRings) {
lr.clearAllContainedCheckResults();
}
}
}
/*
* (non-Javadoc)
*
* @see de.hft.stuttgart.citydoctor2.datastructure.Polygon#containsError(de.hft.
* stuttgart.citydoctor2.check.CheckId)
*/
@Override
public boolean containsError(CheckId checkIdentifier) {
boolean hasError = super.containsError(checkIdentifier);
if (hasError) {
return true;
}
if (exterior.containsError(checkIdentifier)) {
return true;
}
if (innerRings != null) {
for (LinearRing lr : innerRings) {
if (lr.containsError(checkIdentifier)) {
return true;
}
}
}
return false;
}
/*
* (non-Javadoc)
*
* @see
* de.hft.stuttgart.citydoctor2.datastructure.Polygon#addInteriorRing(de.hft.
* stuttgart.citydoctor2.datastructure.LinearRing)
*/
@Override
public void addInteriorRing(LinearRing inter) {
if (inter == null) {
return;
}
if (innerRings == null) {
innerRings = new ArrayList<>(2);
}
innerRings.add(inter);
inter.setParent(this);
}
/*
* (non-Javadoc)
*
* @see de.hft.stuttgart.citydoctor2.datastructure.Polygon#getPartOfSurface()
*/
@Override
public BoundarySurface getPartOfSurface() {
return partOfSurface;
}
/*
* (non-Javadoc)
*
* @see
* de.hft.stuttgart.citydoctor2.datastructure.Polygon#setPartOfSurface(de.hft.
* stuttgart.citydoctor2.datastructure.BoundarySurface)
*/
@Override
public void setPartOfSurface(BoundarySurface bs) {
partOfSurface = bs;
}
@Override
public void removeInnerRing(LinearRing ring) {
if (innerRings == null) {
return;
}
innerRings.remove(ring);
// remove vertex association
emptyRing(ring);
}
/*
* (non-Javadoc)
*
* @see
* de.hft.stuttgart.citydoctor2.datastructure.Polygon#setPartOfInstallation(de.
* hft.stuttgart.citydoctor2.datastructure.BuildingInstallation)
*/
@Override
public void setPartOfInstallation(BuildingInstallation bi) {
partfOfInstallation = bi;
}
/*
* (non-Javadoc)
*
* @see
* de.hft.stuttgart.citydoctor2.datastructure.Polygon#getPartOfInstallation()
*/
@Override
public BuildingInstallation getPartOfInstallation() {
return partfOfInstallation;
}
/*
* (non-Javadoc)
*
* @see de.hft.stuttgart.citydoctor2.datastructure.Polygon#toString()
*/
@Override
public String toString() {
return "Polygon [id=" + getGmlId() + "]";
}
@Override
void anonymize() {
setGmlId(GmlId.generateId());
exterior.anonymize();
if (innerRings != null) {
for (LinearRing lr : innerRings) {
lr.anonymize();
}
}
}
/*
* (non-Javadoc)
*
* @see
* de.hft.stuttgart.citydoctor2.datastructure.Polygon#hasPointAsCorner(de.hft.
* stuttgart.citydoctor2.datastructure.Vertex)
*/
@Override
public boolean hasPointAsCorner(Vertex v) {
boolean hasPoint = exterior.hasPointAsCorner(v);
if (hasPoint) {
return true;
}
if (innerRings != null) {
for (LinearRing lr : innerRings) {
if (lr.hasPointAsCorner(v)) {
return true;
}
}
}
return false;
}
/*
* (non-Javadoc)
*
* @see de.hft.stuttgart.citydoctor2.datastructure.Polygon#removeRings()
*/
@Override
public void removeRings() {
emptyRing(exterior);
exterior = null;
clearInteriorRings();
}
private void clearInteriorRings() {
if (innerRings == null) {
return;
}
for (LinearRing lr : innerRings) {
emptyRing(lr);
}
innerRings.clear();
}
private void emptyRing(LinearRing lr) {
if (isLinkedTo()) {
Geometry geom2 = linkedFromPolygon.getParent();
for (Vertex v : lr.getVertices()) {
v.removeAdjacency(lr, this, geom2);
}
}
for (Vertex v : lr.getVertices()) {
v.removeAdjacency(lr, this, parent);
}
}
@Override
public boolean isLinkedTo() {
return linkedFromPolygon != null;
}
@Override
public LinkedPolygon getLinkedFromPolygon() {
return linkedFromPolygon;
}
@Override
public boolean isLink() {
return false;
}
@Override
public ConcretePolygon getOriginal() {
return this;
}
void setLinkedTo(LinkedPolygon linkedPolygon) {
linkedFromPolygon = linkedPolygon;
}
}
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.datastructure;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* Representing an edge between two consecutive points of a linear ring
*
* @author Matthias Betz
*
*/
public class Edge implements Serializable {
private static final long serialVersionUID = -504694863498128296L;
private List<Polygon> adjacentPolygons = new ArrayList<>(2);
private List<LinearRing> adjacentRings = new ArrayList<>(2);
private Vertex from;
private Vertex to;
// start with 1 for a new edge
private int numberOfHalfEdges = 1;
private int numberOfOppositeHalfEdges;
public Edge(Vertex from, Vertex to) {
this.from = from;
this.to = to;
}
public Vertex getFrom() {
return from;
}
public Vertex getTo() {
return to;
}
public void addHalfEdge(Vertex v0, Vertex v1) {
if (v0 == null || v1 == null) {
return;
}
if (isPointOfEdge(v0, from) && isPointOfEdge(v1, to)) {
numberOfHalfEdges++;
return;
}
if (isPointOfEdge(v1, from) && isPointOfEdge(v0, to)) {
numberOfOppositeHalfEdges++;
return;
}
throw new IllegalStateException("The given vertices do not from this edge");
}
private boolean isPointOfEdge(Vertex v0, Vertex edgePoint) {
if (v0.equals(edgePoint)) {
return true;
}
for (Vertex v : v0.getNeighbors()) {
if (v.equals(edgePoint)) {
return true;
}
}
return false;
}
public boolean formedByIgnoreDirection(Vertex v1, Vertex v2) {
return (v1 == from && v2 == to) || (v1 == to && v2 == from);
}
public boolean equalsIgnoreDirection(Edge other) {
return (other.from.equals(from) && other.to.equals(to)) || (other.from.equals(to) && other.to.equals(from));
}
public int getNumberOfHalfEdges() {
return numberOfHalfEdges;
}
public int getNumberOppositeHalfEdges() {
return numberOfOppositeHalfEdges;
}
public int getNumberOfAllHalfEdges() {
return numberOfHalfEdges + numberOfOppositeHalfEdges;
}
public List<Polygon> getAdjacentPolygons() {
return adjacentPolygons;
}
public List<LinearRing> getAdjacentRings() {
return adjacentRings;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((from == null) ? 0 : from.hashCode());
result = prime * result + ((to == null) ? 0 : to.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;
}
Edge other = (Edge) obj;
if (from == null) {
if (other.from != null) {
return false;
}
} else if (!from.equals(other.from)) {
return false;
}
if (to == null) {
if (other.to != null) {
return false;
}
} else if (!to.equals(other.to)) {
return false;
}
return true;
}
public void addAdjacentPolygon(Polygon p) {
adjacentPolygons.add(p);
}
public void addAdjacentRing(LinearRing lr) {
adjacentRings.add(lr);
}
@Override
public String toString() {
return "Edge [from=" + from + ", to=" + to + "]";
}
public boolean contains(Vertex v) {
return v == from || v == to;
}
public Vertex getConnectionPoint(Edge e) {
if (from == e.getTo() || from == e.getFrom()) {
return from;
}
if (to == e.getTo() || to == e.getFrom()) {
return to;
}
return null;
}
/**
* finds the point on the other end of the edge if the given vertex is one of
* the end points.
*
* @param c the given vertex
* @return the opposite vertex or null if the given vertex is not an end point.
*/
public Vertex getOppositeVertex(Vertex c) {
if (c == from) {
return to;
}
if (c == to) {
return from;
}
return null;
}
}
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.datastructure;
/**
* Possible feature types in CityGML
*
* @author Matthias Betz
*
*/
public enum FeatureType {
BUILDING, TRANSPORTATION, VEGETATION, BRIDGE, LAND, WATER, BOUNDARY_SURFACE, BUILDING_INSTALLATION, OPENING,
BUILDING_PART;
}
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.datastructure;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.math.Triangle3d;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.tesselation.TesselatedPolygon;
import de.hft.stuttgart.citydoctor2.utils.BoundingBoxCalculator;
import de.hft.stuttgart.citydoctor2.utils.SerializablePair;
/**
* Representation of a geometry containing the polygons and edges
*
* @author Matthias Betz
*
*/
public class Geometry extends GmlElement {
private static final long serialVersionUID = 2539031030917731575L;
private static Random r = new Random();
private GeometryType type;
private Lod lod;
private CityObject parent;
private List<Polygon> polygons = new ArrayList<>();
private List<Edge> edges;
private Map<SerializablePair<Vertex, Vertex>, Edge> edgeMap;
private List<Vertex> vertices;
public Geometry(GeometryType type, Lod lod) {
this.lod = lod;
this.type = type;
}
void setParent(CityObject parent) {
this.parent = parent;
}
public CityObject getParent() {
return parent;
}
public void setType(GeometryType type) {
this.type = type;
}
public GeometryType getType() {
return type;
}
public Lod getLod() {
return lod;
}
public List<Polygon> getPolygons() {
return polygons;
}
public void addPolygon(Polygon cdPoly) {
polygons.add(cdPoly);
cdPoly.setParent(this);
}
public void updateEdges() {
edges = new ArrayList<>();
edgeMap = new HashMap<>();
Map<Edge, Edge> duplicacyMap = new HashMap<>();
for (Polygon p : polygons) {
LinearRing extRing = p.getExteriorRing();
createEdgesFromRing(duplicacyMap, p, extRing);
for (LinearRing inner : p.getInnerRings()) {
createEdgesFromRing(duplicacyMap, p, inner);
}
}
}
void anonymize() {
setGmlId(GmlId.generateId());
for (Polygon p : polygons) {
p.anonymize();
}
double minX = Double.MAX_VALUE;
double minY = Double.MAX_VALUE;
double minZ = Double.MAX_VALUE;
for (Vertex v : vertices) {
if (v.getX() < minX) {
minX = v.getX();
}
if (v.getY() < minY) {
minY = v.getY();
}
if (v.getZ() < minZ) {
minZ = v.getZ();
}
}
double varX = r.nextDouble() * 10;
double varY = r.nextDouble() * 10;
double varZ = r.nextDouble() * 10;
for (Vertex v : vertices) {
v.setX(v.getX() - minX + varX);
v.setY(v.getY() - minY + varY);
v.setZ(v.getZ() - minZ + varZ);
}
}
private void createEdgesFromRing(Map<Edge, Edge> duplicacyMap, Polygon p, LinearRing ring) {
// only go to size -1 as ring should be closed
for (int i = 0; i < ring.getVertices().size() - 1; i++) {
Vertex v0 = ring.getVertices().get(i + 0);
Vertex v1 = ring.getVertices().get(i + 1);
Edge tempEdge = new Edge(v0, v1);
Edge e = duplicacyMap.get(tempEdge);
if (e == null) {
// no duplicate found, enter edges
Edge oppositeEdge = new Edge(v1, v0);
duplicacyMap.put(tempEdge, tempEdge);
duplicacyMap.put(oppositeEdge, tempEdge);
for (Vertex v0Neighbor : v0.getNeighbors()) {
Edge alternativeEdge = new Edge(v0Neighbor, v1);
Edge oppositeAlternativeEdge = new Edge(v1, v0Neighbor);
duplicacyMap.put(alternativeEdge, tempEdge);
duplicacyMap.put(oppositeAlternativeEdge, tempEdge);
for (Vertex v1Neighbor : v1.getNeighbors()) {
alternativeEdge = new Edge(v0Neighbor, v1Neighbor);
oppositeAlternativeEdge = new Edge(v1Neighbor, v0Neighbor);
duplicacyMap.put(alternativeEdge, tempEdge);
duplicacyMap.put(oppositeAlternativeEdge, tempEdge);
}
}
for (Vertex v : v1.getNeighbors()) {
Edge alternativeEdge = new Edge(v0, v);
Edge oppositeAlternativeEdge = new Edge(v, v0);
duplicacyMap.put(alternativeEdge, tempEdge);
duplicacyMap.put(oppositeAlternativeEdge, tempEdge);
}
e = tempEdge;
edges.add(e);
edgeMap.put(new SerializablePair<>(v0, v1), e);
edgeMap.put(new SerializablePair<>(v1, v0), e);
} else {
e.addHalfEdge(v0, v1);
}
e.addAdjacentPolygon(p);
e.addAdjacentRing(ring);
}
}
public Vector3d getCenter() {
return calculateBoundingBox().getCenter();
}
/**
* 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.
*
* @return an array of two points. The first contains the lowest coordinates,
* the second contains the highest coordinates
*/
public BoundingBox calculateBoundingBox() {
return BoundingBoxCalculator.calculateBoundingBox(polygons);
}
public double calculateVolume() {
double sum = 0;
Vector3d center = calculateBoundingBox().getCenter();
for (Polygon p : polygons) {
TesselatedPolygon tesselatedPolygon = p.tesselate();
for (Triangle3d t : tesselatedPolygon.getTriangles()) {
Vector3d p1 = t.getP1().minus(center);
Vector3d p2 = t.getP2().minus(center);
Vector3d p3 = t.getP3().minus(center);
sum += det(p1.getCoordinates(), p2.getCoordinates(), p3.getCoordinates());
}
}
return sum / 6d;
}
private double det(double[] a, double[] b, double[] c) {
return -c[0] * b[1] * a[2] + b[0] * c[1] * a[2] + c[0] * a[1] * b[2] - a[0] * c[1] * b[2] - b[0] * a[1] * c[2]
+ a[0] * b[1] * c[2];
}
@Override
public boolean containsError(CheckId checkIdentifier) {
boolean hasError = super.containsError(checkIdentifier);
if (hasError) {
return true;
}
for (Polygon p : polygons) {
if (p.containsError(checkIdentifier)) {
return true;
}
}
return false;
}
@Override
public void clearAllContainedCheckResults() {
super.clearCheckResults();
for (Polygon p : polygons) {
p.clearAllContainedCheckResults();
}
}
@Override
public void collectContainedErrors(List<CheckError> errors) {
super.collectContainedErrors(errors);
for (Polygon p : polygons) {
p.collectContainedErrors(errors);
}
}
@Override
public boolean containsAnyError() {
boolean hasError = super.containsAnyError();
if (hasError) {
return true;
}
for (Polygon p : polygons) {
if (p.containsAnyError()) {
return true;
}
}
return false;
}
@Override
public void accept(Check c) {
super.accept(c);
if (c.canExecute(this)) {
c.check(this);
}
for (Polygon p : polygons) {
p.accept(c);
}
}
@Override
public String toString() {
return "Geometry [type=" + type + ", lod=" + lod + ", id=" + getGmlId() + "]";
}
public List<Vertex> getVertices() {
return vertices;
}
public void setVertices(List<Vertex> vertices) {
this.vertices = vertices;
}
public List<Edge> getEdges() {
return edges;
}
public List<Edge> getEdgesAdjacentTo(Vertex v) {
List<Edge> adjacentEdges = new ArrayList<>();
for (Edge e : edges) {
if (e.getFrom() == v || e.getTo() == v) {
adjacentEdges.add(e);
}
}
return adjacentEdges;
}
public Edge getEdge(Vertex v1, Vertex v2) {
Edge edge = edgeMap.get(new SerializablePair<>(v1, v2));
if (edge != null) {
return edge;
}
for (Vertex neighborV1 : v1.getNeighbors()) {
edge = edgeMap.get(new SerializablePair<>(neighborV1, v2));
if (edge != null) {
return edge;
}
for (Vertex neighborV2 : v2.getNeighbors()) {
edge = edgeMap.get(new SerializablePair<>(neighborV1, neighborV2));
if (edge != null) {
return edge;
}
}
}
for (Vertex neighborV2 : v2.getNeighbors()) {
edge = edgeMap.get(new SerializablePair<>(neighborV2, v1));
if (edge != null) {
return edge;
}
}
return null;
}
public void updateEdgesAndVertices() {
updateVertices();
updateEdges();
}
public void updateVertices() {
Set<Vertex> vertexSet = new HashSet<>();
for (Polygon p : getPolygons()) {
updateRing(p, vertexSet, p.getExteriorRing());
for (LinearRing inner : p.getInnerRings()) {
updateRing(p, vertexSet, inner);
}
}
vertices = new ArrayList<>(vertexSet);
}
private void updateRing(Polygon p, Set<Vertex> vertexSet, LinearRing ring) {
for (Vertex v : ring.getVertices()) {
if (vertexSet.add(v)) {
// new vertex, clear adjacent rings in case new rings have been added
v.clearAdjacentRings(this);
}
// add ring to adjacent rings of vertex
v.addAdjacentRing(p, ring, this);
}
}
public void removePolygon(Polygon p) {
p.removeRings();
polygons.remove(p);
if (p.isLink()) {
Polygon original = p.getOriginal();
original.getParent().polygons.remove(original);
} else if (p.isLinkedTo()) {
Polygon link = p.getLinkedFromPolygon();
link.getParent().polygons.remove(link);
}
}
public void replacePolygon(Polygon p, ConcretePolygon... polygons) {
removePolygon(p);
if (p.isLink()) {
Geometry geom2 = p.getOriginal().getParent();
for (ConcretePolygon newPoly : polygons) {
geom2.addPolygon(newPoly);
addPolygon(new LinkedPolygon(newPoly, this));
}
} else if (p.isLinkedTo()) {
Geometry geom2 = p.getLinkedFromPolygon().getParent();
for (ConcretePolygon newPoly : polygons) {
addPolygon(newPoly);
geom2.addPolygon(new LinkedPolygon(newPoly, this));
}
} else {
for (Polygon newPoly : polygons) {
addPolygon(newPoly);
}
}
}
public List<Edge> getEdgesAdjacentTo(Polygon p) {
List<Edge> list = new ArrayList<>();
for (Edge e : edges) {
if (e.getAdjacentPolygons().contains(p)) {
list.add(e);
}
}
return list;
}
public boolean containsPolygon(Polygon other) {
for (Polygon p : polygons) {
if (p.getGmlId().equals(other.getGmlId())) {
return true;
}
}
return false;
}
@Override
public Geometry copy() {
return (Geometry) super.copy();
}
}
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.datastructure;
/**
* Available geometry types
*
* @author Matthias Betz
*
*/
public enum GeometryType {
MULTI_SURFACE, SOLID, COMPOSITE_SURFACE
}
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.datastructure;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.UncheckedIOException;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.Checkable;
/**
* A general GML element that has a gml id
*
* @author Matthias Betz
*
*/
public abstract class GmlElement extends Checkable {
private static final long serialVersionUID = -3242505393303017359L;
private GmlId gmlId;
public GmlElement copy() {
ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(arrayOutputStream);
oos.writeObject(this);
ObjectInputStream ooi = new ObjectInputStream(new ByteArrayInputStream(arrayOutputStream.toByteArray()));
return (GmlElement) ooi.readObject();
} catch (IOException e) {
throw new UncheckedIOException(e);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e);
}
}
@Override
public void accept(Check c) {
if (c.canExecute(this)) {
c.check(this);
}
setValidated(true);
}
public void setGmlId(GmlId id) {
gmlId = id;
}
@Override
public GmlId getGmlId() {
if (gmlId == null) {
gmlId = GmlId.generateId();
}
return gmlId;
}
}
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.datastructure;
import java.io.Serializable;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Represents a GML id. Additionally contains information whether this id was
* generated by city doctor
*
* @author Matthias Betz
*
*/
public class GmlId implements Serializable {
private static final long serialVersionUID = 4273817255150972966L;
/* prefixes for generation */
private static final String GENERAL_GEN = "CityDoctor_%d_%d";
private static final AtomicInteger ID_COUNTER = new AtomicInteger();
private String gmlString;
private boolean generated;
public GmlId(String gmlId) {
this(gmlId, false);
}
private GmlId(String gmlId, boolean generated) {
if (gmlId == null || gmlId.isEmpty()) {
throw new IllegalArgumentException("GmlId may not be null or empty. If no GmlId is given generate one");
}
this.gmlString = gmlId;
this.generated = generated;
}
@Override
public String toString() {
return gmlString;
}
public String getGmlString() {
return gmlString;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (generated ? 1231 : 1237);
result = prime * result + ((gmlString == null) ? 0 : gmlString.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;
}
GmlId other = (GmlId) obj;
if (generated != other.generated) {
return false;
}
if (gmlString == null) {
if (other.gmlString != null) {
return false;
}
} else if (!gmlString.equals(other.gmlString)) {
return false;
}
return true;
}
/**
* Determines if the GML-ID was generated by CityDoctor or not.
*
* @return True if it was generated, otherwise false.
*/
public boolean isGenerated() {
return generated;
}
public static GmlId generateId() {
return new GmlId(String.format(GENERAL_GEN, System.currentTimeMillis(), ID_COUNTER.getAndIncrement()), true);
}
}
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.datastructure;
import org.citygml4j.factory.GMLGeometryFactory;
import org.citygml4j.model.citygml.core.AbstractCityObject;
import org.citygml4j.model.citygml.landuse.LandUse;
import org.citygml4j.model.gml.geometry.aggregates.MultiSurface;
import org.citygml4j.model.gml.geometry.aggregates.MultiSurfaceProperty;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils;
/**
* Represents a land use CityGML object.
*
* @author Matthias Betz
*
*/
public class LandObject extends CityObject {
private static final long serialVersionUID = 1887455662411087326L;
private LandUse lu;
public LandObject(LandUse lu) {
this.lu = lu;
}
@Override
public void reCreateGeometries(GMLGeometryFactory factory, ParserConfiguration config) {
for (Geometry geom : getGeometries()) {
if (geom.getType() == GeometryType.MULTI_SURFACE) {
MultiSurface ms = CityGmlUtils.createMultiSurface(geom, factory, config);
setMultiSurfaceAccordingToLod(geom, ms);
} else {
throw new IllegalStateException("Landuse cannot have solids: " + geom);
}
}
}
@Override
public FeatureType getFeatureType() {
return FeatureType.LAND;
}
private void setMultiSurfaceAccordingToLod(Geometry geom, MultiSurface ms) {
switch (geom.getLod()) {
case LOD0:
lu.setLod0MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD1:
lu.setLod1MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD2:
lu.setLod2MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD3:
lu.setLod3MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD4:
lu.setLod4MultiSurface(new MultiSurfaceProperty(ms));
break;
}
}
@Override
public void accept(Check c) {
super.accept(c);
if (c.canExecute(this)) {
c.check(this);
}
}
@Override
public AbstractCityObject getGmlObject() {
return lu;
}
@Override
public void unsetGmlGeometries() {
lu.unsetLod0MultiSurface();
lu.unsetLod1MultiSurface();
lu.unsetLod2MultiSurface();
lu.unsetLod3MultiSurface();
lu.unsetLod4MultiSurface();
}
public void setGmlObject(LandUse landUse) {
lu = landUse;
}
@Override
public String toString() {
return "LandObject [id=" + getGmlId() + "]";
}
}
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.datastructure;
import java.util.ArrayList;
import java.util.List;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.math.Vector2d;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
/**
* Represents a linear ring used in polygons. The ring contains the vertices.
*
* @author Matthias Betz
*
*/
public class LinearRing extends GmlElement {
private static final long serialVersionUID = -2488180830514940722L;
private LinearRingType type;
private Polygon parent;
private List<Vertex> vertices = new ArrayList<>();
public enum LinearRingType {
EXTERIOR, INTERIOR
}
public LinearRing(LinearRingType type) {
this.type = type;
}
/**
* Checks whether a point is inside this ring. A point on the edge does count as
* inside.
*
* @param v the point.
* @return true if the point is inside or on an edge, false if it is outside.
*/
public boolean isPointInside(Vector3d v) {
// project to 2d ring
List<Vector2d> projectedRing = new ArrayList<>();
Vector2d point;
Vector3d normal = calculateNormal();
double x = Math.abs(normal.getX());
double y = Math.abs(normal.getY());
double z = Math.abs(normal.getZ());
if (x > y && x > z) {
for (Vertex vert : vertices) {
Vector2d projCoords = new Vector2d(vert.getY(), vert.getZ());
projectedRing.add(projCoords);
}
point = new Vector2d(v.getY(), v.getZ());
} else if (y > x && y > z) {
for (Vertex vert : vertices) {
Vector2d projCoords = new Vector2d(vert.getX(), vert.getZ());
projectedRing.add(projCoords);
}
point = new Vector2d(v.getX(), v.getZ());
} else {
for (Vertex vert : vertices) {
Vector2d projCoords = new Vector2d(vert.getX(), vert.getY());
projectedRing.add(projCoords);
}
point = new Vector2d(v.getX(), v.getY());
}
int t = -1;
for (int i = 0; i < projectedRing.size() - 1; i++) {
t = t * crossProdTest(point, projectedRing.get(i), projectedRing.get(i + 1));
if (t == 0) {
return true;
}
}
return t >= 0;
}
private int crossProdTest(Vector2d a, Vector2d b, Vector2d c) {
if (a.getY() == b.getY() && a.getY() == c.getY()) {
if ((b.getX() <= a.getX() && a.getX() <= c.getX()) || (c.getX() <= a.getX() && a.getX() <= b.getX())) {
return 0;
} else {
return 1;
}
}
if (a.getY() == b.getY() && a.getX() == b.getX()) {
return 0;
}
if (b.getY() > c.getY()) {
Vector2d temp = b;
b = c;
c = temp;
}
if (a.getY() <= b.getY() || a.getY() > c.getY()) {
return 1;
}
return calculateDelta(a, b, c);
}
private int calculateDelta(Vector2d a, Vector2d b, Vector2d c) {
double delta = (b.getX() - a.getX()) * (c.getY() - a.getY()) - (b.getY() - a.getY()) * (c.getX() - a.getX());
if (delta > 0) {
return -1;
} else if (delta < 0) {
return 1;
} else {
return 0;
}
}
/**
* Calculates the normal vector of the ring. Method by Newell. If the Newell
* method would return a (0, 0, 0) vector a cross product is formed from the
* first 3 vertices. If there are no 3 vertices available (1, 0, 0) is returned.
*
* @return the normal as a normalized vector
*/
public Vector3d calculateNormal() {
double[] coords = new double[3];
for (int i = 0; i < vertices.size() - 1; i++) {
Vertex current = vertices.get(i + 0);
Vertex 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);
}
Vertex v1 = vertices.get(0);
Vertex v2 = vertices.get(1);
Vertex v3 = vertices.get(2);
return calculateNormalWithCross(v1, v2, v3);
}
Vector3d v = new Vector3d(coords);
v.normalize();
return v;
}
private Vector3d calculateNormalWithCross(Vertex v1, Vertex v2, Vertex v3) {
Vector3d dir1 = v2.minus(v1);
Vector3d dir2 = v3.minus(v1);
Vector3d cross = dir1.cross(dir2);
return cross.normalize();
}
@Override
public void accept(Check c) {
super.accept(c);
if (c.canExecute(this)) {
c.check(this);
}
}
@Override
public void clearAllContainedCheckResults() {
super.clearCheckResults();
}
public void setParent(Polygon polygon) {
parent = polygon;
}
public Polygon getParent() {
return parent;
}
public LinearRingType getType() {
return type;
}
public List<Vertex> getVertices() {
return vertices;
}
public void addVertex(Vertex v) {
vertices.add(v);
if (parent == null) {
return;
}
if (parent.isLinkedTo()) {
v.addAdjacentRing(parent.getLinkedFromPolygon(), this, parent.getLinkedFromPolygon().getParent());
}
v.addAdjacentRing(parent, this, parent.getParent());
}
public void addVertex(int i, Vertex v) {
vertices.add(i, v);
if (parent.isLinkedTo()) {
v.addAdjacentRing(parent.getLinkedFromPolygon(), this, parent.getLinkedFromPolygon().getParent());
}
v.addAdjacentRing(parent, this, parent.getParent());
}
@Override
public String toString() {
return "LinearRing [type=" + type + ", gmlId=" + getGmlId() + "]";
}
void anonymize() {
setGmlId(GmlId.generateId());
}
public boolean isRingConnectedViaPoint(LinearRing other) {
for (Vertex v : vertices) {
if (other.getVertices().contains(v)) {
return true;
}
}
return false;
}
public boolean hasPointAsCorner(Vertex v) {
return vertices.contains(v);
}
public void setType(LinearRingType type) {
this.type = type;
}
public void addAllVertices(List<Vertex> extRing) {
vertices.addAll(extRing);
}
}
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.datastructure;
import java.util.List;
import java.util.Map;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.tesselation.TesselatedPolygon;
/**
* A polygon that does not contain the geometry but is only a link to an actual
* polygon. Created when href in CityGML are used.
*
* @author Matthias Betz
*
*/
public class LinkedPolygon extends Polygon {
private static final long serialVersionUID = -4897578390280277931L;
private Geometry parent;
private ConcretePolygon poly;
public LinkedPolygon(ConcretePolygon poly, Geometry parent) {
this.parent = parent;
this.poly = poly;
poly.setLinkedTo(this);
for (Vertex v : poly.getExteriorRing().getVertices()) {
v.addAdjacentRing(this, poly.getExteriorRing(), parent);
}
for (LinearRing lr : poly.getInnerRings()) {
for (Vertex v : lr.getVertices()) {
v.addAdjacentRing(this, lr, parent);
}
}
}
@Override
public CheckResult getCheckResult(Check c) {
return poly.getCheckResult(c);
}
@Override
public Map<CheckId, CheckResult> getAllCheckResults() {
return poly.getAllCheckResults();
}
@Override
public boolean hasAnyError() {
return poly.hasAnyError();
}
@Override
public boolean hasDependencyNotMetError(CheckId id) {
return poly.hasDependencyNotMetError(id);
}
@Override
public CheckResult getCheckResult(CheckId id) {
return poly.getCheckResult(id);
}
@Override
public boolean hasCheckResults() {
return poly.hasCheckResults();
}
@Override
public void setGmlId(GmlId id) {
poly.setGmlId(id);
}
@Override
public GmlId getGmlId() {
return poly.getGmlId();
}
@Override
public void accept(Check c) {
// linked polygons are not checked
setValidated(true);
}
@Override
public void addCheckResult(CheckResult cr) {
poly.addCheckResult(cr);
}
@Override
public void collectContainedErrors(List<CheckError> errors) {
poly.collectContainedErrors(errors);
}
@Override
public boolean containsAnyError() {
return poly.containsAnyError();
}
@Override
public boolean containsError(CheckId checkIdentifier) {
return poly.containsError(checkIdentifier);
}
@Override
public void clearCheckResults() {
// linked polygons don't have check results
}
@Override
public Vector3d calculateNormal() {
return poly.calculateNormal();
}
@Override
public TesselatedPolygon tesselate() {
return poly.tesselate();
}
@Override
public LinearRing getExteriorRing() {
return poly.getExteriorRing();
}
@Override
public List<LinearRing> getInnerRings() {
return poly.getInnerRings();
}
@Override
public boolean isPointInsideExteriorRing(Vector3d v) {
return poly.isPointInsideExteriorRing(v);
}
@Override
public Geometry getParent() {
return parent;
}
@Override
public void setParent(Geometry geometry) {
this.parent = geometry;
}
@Override
public void setExteriorRing(LinearRing extRing) {
poly.setExteriorRing(extRing);
}
@Override
public boolean isPolygonConnectedViaPoint(Polygon other) {
return poly.isPolygonConnectedViaPoint(other);
}
@Override
public String toString() {
return "LinkedPolygon [" + poly.getGmlId() + "]";
}
@Override
public void addInteriorRing(LinearRing inter) {
poly.addInteriorRing(inter);
}
@Override
public BoundarySurface getPartOfSurface() {
return poly.getPartOfSurface();
}
@Override
public void setPartOfSurface(BoundarySurface bs) {
poly.setPartOfSurface(bs);
}
@Override
public void removeInnerRing(LinearRing ring) {
poly.removeInnerRing(ring);
}
@Override
public void setPartOfInstallation(BuildingInstallation bi) {
poly.setPartOfInstallation(bi);
}
@Override
public BuildingInstallation getPartOfInstallation() {
return poly.getPartOfInstallation();
}
@Override
public boolean hasPointAsCorner(Vertex v) {
return poly.hasPointAsCorner(v);
}
@Override
public void removeRings() {
poly.removeRings();
}
@Override
public boolean isLinkedTo() {
return false;
}
@Override
public boolean isLink() {
return true;
}
@Override
public LinkedPolygon getLinkedFromPolygon() {
return this;
}
@Override
void anonymize() {
poly.anonymize();
}
@Override
public void clearAllContainedCheckResults() {
poly.clearAllContainedCheckResults();
}
@Override
public ConcretePolygon getOriginal() {
return poly;
}
}
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.datastructure;
/**
* The different LODs available in CityGML
*
* @author Matthias Betz
*
*/
public enum Lod {
LOD0(0), LOD1(1), LOD2(2), LOD3(3), LOD4(4);
private int rank;
private Lod(int rank) {
this.rank = rank;
}
public boolean isHigher(Lod other) {
if (other == null) {
return true;
}
return rank > other.rank;
}
}
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.datastructure;
import org.citygml4j.factory.GMLGeometryFactory;
import org.citygml4j.model.citygml.core.AbstractCityObject;
import org.citygml4j.model.gml.geometry.aggregates.MultiSurface;
import org.citygml4j.model.gml.geometry.aggregates.MultiSurfaceProperty;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils;
/**
* Represents an Opening suchs a window or Door in a surface of a feature.
* Contains a reference to the boundary surface where this opening is embedded
* in.
*
* @author Matthias Betz
*
*/
public class Opening extends CityObject {
private static final long serialVersionUID = 6409303152284607944L;
private BoundarySurface partOf;
private OpeningType type;
private SurfaceFeatureType featureType;
private AbstractCityObject ao;
public Opening(OpeningType type, SurfaceFeatureType featureType, BoundarySurface partOf, AbstractCityObject ao) {
this.featureType = featureType;
this.partOf = partOf;
this.type = type;
this.ao = ao;
}
@Override
public CityObject copy() {
throw new UnsupportedOperationException("Cannot copy Opening");
}
public void reCreateGeometries(GMLGeometryFactory factory, ParserConfiguration config) {
if (!ao.isSetId()) {
ao.setId(getGmlId().getGmlString());
}
for (Geometry geom : getGeometries()) {
if (geom.getType() == GeometryType.MULTI_SURFACE) {
MultiSurface ms = CityGmlUtils.createMultiSurface(geom, factory, config);
setGeometryAccordingToLod(geom.getLod(), new MultiSurfaceProperty(ms));
} else {
throw new IllegalStateException("BoundarySurfaces can only have MultiSurface geometries");
}
}
}
private void setGeometryAccordingToLod(Lod lod, MultiSurfaceProperty ms) {
switch (lod) {
case LOD3:
setLod3Ms(ms);
break;
case LOD4:
setLod4Ms(ms);
break;
default:
throw new IllegalStateException("Cannot add geometry to opening because lod is not allowed: " + lod);
}
}
private void setLod3Ms(MultiSurfaceProperty ms) {
switch (featureType) {
case BRIDGE:
org.citygml4j.model.citygml.bridge.AbstractOpening bridgeAo = (org.citygml4j.model.citygml.bridge.AbstractOpening) ao;
bridgeAo.setLod3MultiSurface(ms);
break;
case BUILDING:
org.citygml4j.model.citygml.building.AbstractOpening buildAo = (org.citygml4j.model.citygml.building.AbstractOpening) ao;
buildAo.setLod3MultiSurface(ms);
break;
case TUNNEL:
org.citygml4j.model.citygml.tunnel.AbstractOpening tunAo = (org.citygml4j.model.citygml.tunnel.AbstractOpening) ao;
tunAo.setLod3MultiSurface(ms);
}
}
private void setLod4Ms(MultiSurfaceProperty ms) {
switch (featureType) {
case BRIDGE:
org.citygml4j.model.citygml.bridge.AbstractOpening bridgeAo = (org.citygml4j.model.citygml.bridge.AbstractOpening) ao;
bridgeAo.setLod4MultiSurface(ms);
break;
case BUILDING:
org.citygml4j.model.citygml.building.AbstractOpening buildAo = (org.citygml4j.model.citygml.building.AbstractOpening) ao;
buildAo.setLod4MultiSurface(ms);
break;
case TUNNEL:
org.citygml4j.model.citygml.tunnel.AbstractOpening tunAo = (org.citygml4j.model.citygml.tunnel.AbstractOpening) ao;
tunAo.setLod4MultiSurface(ms);
}
}
public SurfaceFeatureType getSurfaceFeatureType() {
return featureType;
}
public BoundarySurface getPartOf() {
return partOf;
}
public void setType(OpeningType type) {
this.type = type;
}
public OpeningType getType() {
return type;
}
@Override
public void accept(Check c) {
super.accept(c);
if (c.canExecute(this)) {
c.check(this);
}
}
@Override
public void clearAllContainedCheckResults() {
super.clearCheckResults();
}
public void unsetGmlGeometries() {
switch (featureType) {
case BRIDGE:
org.citygml4j.model.citygml.bridge.AbstractOpening bridgeAo = (org.citygml4j.model.citygml.bridge.AbstractOpening) ao;
bridgeAo.unsetLod3MultiSurface();
bridgeAo.unsetLod4MultiSurface();
break;
case BUILDING:
org.citygml4j.model.citygml.building.AbstractOpening buildAo = (org.citygml4j.model.citygml.building.AbstractOpening) ao;
buildAo.unsetLod3MultiSurface();
buildAo.unsetLod4MultiSurface();
break;
case TUNNEL:
org.citygml4j.model.citygml.tunnel.AbstractOpening tunAo = (org.citygml4j.model.citygml.tunnel.AbstractOpening) ao;
tunAo.unsetLod3MultiSurface();
tunAo.unsetLod4MultiSurface();
}
}
@Override
public String toString() {
return "Opening [type=" + type + ", id=" + getGmlId() + "]";
}
public void setPartOfSurface(BoundarySurface boundarySurface) {
partOf = boundarySurface;
}
@Override
public AbstractCityObject getGmlObject() {
return ao;
}
@Override
public FeatureType getFeatureType() {
return FeatureType.OPENING;
}
}
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.datastructure;
/**
* The available opening types in CityGML
*
* @author Matthias Betz
*
*/
public enum OpeningType {
DOOR, WINDOW, UNKNOWN
}
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.datastructure;
import java.util.List;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.tesselation.TesselatedPolygon;
/**
* Interface for concrete and linked polygons.
*
* @author Matthias Betz
*
*/
public abstract class Polygon extends GmlElement {
private static final long serialVersionUID = -613942946364706513L;
public abstract Vector3d calculateNormal();
public abstract TesselatedPolygon tesselate();
public abstract LinearRing getExteriorRing();
public abstract List<LinearRing> getInnerRings();
public abstract boolean isPointInsideExteriorRing(Vector3d v);
public abstract Geometry getParent();
public abstract void setParent(Geometry geometry);
public abstract void setExteriorRing(LinearRing extRing);
public abstract boolean isPolygonConnectedViaPoint(Polygon other);
public abstract void addInteriorRing(LinearRing inter);
public abstract BoundarySurface getPartOfSurface();
public abstract void setPartOfSurface(BoundarySurface bs);
public abstract void removeInnerRing(LinearRing ring);
public abstract void setPartOfInstallation(BuildingInstallation bi);
public abstract BuildingInstallation getPartOfInstallation();
public abstract boolean hasPointAsCorner(Vertex v);
public abstract void removeRings();
public abstract boolean isLinkedTo();
public abstract boolean isLink();
public abstract LinkedPolygon getLinkedFromPolygon();
abstract void anonymize();
public abstract ConcretePolygon getOriginal();
}
\ No newline at end of file
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.datastructure;
/**
* Availabel bridge types in CityGML
*
* @author Matthias Betz
*
*/
public enum SurfaceFeatureType {
BUILDING, BRIDGE, TUNNEL;
}
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.datastructure;
import java.util.ArrayList;
import java.util.List;
import org.citygml4j.factory.GMLGeometryFactory;
import org.citygml4j.model.citygml.core.AbstractCityObject;
import org.citygml4j.model.citygml.transportation.AbstractTransportationObject;
import org.citygml4j.model.citygml.transportation.AuxiliaryTrafficArea;
import org.citygml4j.model.citygml.transportation.TrafficArea;
import org.citygml4j.model.citygml.transportation.TransportationComplex;
import org.citygml4j.model.gml.geometry.aggregates.MultiSurface;
import org.citygml4j.model.gml.geometry.aggregates.MultiSurfaceProperty;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils;
/**
* Representation of a transportation object.
*
* @author Matthias Betz
*
*/
public class TransportationObject extends CityObject {
private static final long serialVersionUID = -2698907271726700390L;
public enum TransportationType {
ROAD, TRACK, RAILWAY, TRAFFIC_AREA, AUXILLIARY_TRAFFIC_AREA, TRANSPORTATION_COMPLEX, SQUARE
}
private AbstractTransportationObject ato;
private List<TransportationObject> composesOf = new ArrayList<>();
private TransportationType type;
public TransportationObject(TransportationType type) {
this.type = type;
}
@Override
public FeatureType getFeatureType() {
return FeatureType.TRANSPORTATION;
}
@Override
public void reCreateGeometries(GMLGeometryFactory factory, ParserConfiguration config) {
for (Geometry geom : getGeometries()) {
if (geom.getType() == GeometryType.MULTI_SURFACE) {
MultiSurface ms = CityGmlUtils.createMultiSurface(geom, factory, config);
switch (type) {
case ROAD:
case TRACK:
case RAILWAY:
case SQUARE:
case TRANSPORTATION_COMPLEX:
TransportationComplex tc = (TransportationComplex) ato;
setMultiSurfaceAccordingToLod(tc, ms, geom.getLod());
break;
case TRAFFIC_AREA:
TrafficArea ta = (TrafficArea) ato;
setMultiSurfaceAccordingToLod(ta, ms, geom.getLod());
break;
case AUXILLIARY_TRAFFIC_AREA:
AuxiliaryTrafficArea ata = (AuxiliaryTrafficArea) ato;
setMultiSurfaceAccordingToLod(ata, ms, geom.getLod());
break;
}
}
}
for (TransportationObject children : composesOf) {
children.reCreateGeometries(factory, config);
}
}
private void setMultiSurfaceAccordingToLod(TransportationComplex tc, MultiSurface ms, Lod lod) {
switch (lod) {
case LOD1:
tc.setLod1MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD2:
tc.setLod2MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD3:
tc.setLod3MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD4:
tc.setLod4MultiSurface(new MultiSurfaceProperty(ms));
break;
default:
throw new IllegalStateException("cannot set geometry with LOD for TransportationComplex: " + lod);
}
}
private void setMultiSurfaceAccordingToLod(AuxiliaryTrafficArea ata, MultiSurface ms, Lod lod) {
switch (lod) {
case LOD2:
ata.setLod2MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD3:
ata.setLod3MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD4:
ata.setLod4MultiSurface(new MultiSurfaceProperty(ms));
break;
default:
throw new IllegalStateException("cannot set geometry with LOD for AuxiliaryTrafficArea: " + lod);
}
}
private void setMultiSurfaceAccordingToLod(TrafficArea ta, MultiSurface ms, Lod lod) {
switch (lod) {
case LOD2:
ta.setLod2MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD3:
ta.setLod3MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD4:
ta.setLod4MultiSurface(new MultiSurfaceProperty(ms));
break;
default:
throw new IllegalStateException("cannot set geometry with LOD for TrafficArea: " + lod);
}
}
@Override
public boolean containsError(CheckId checkIdentifier) {
boolean hasError = super.containsError(checkIdentifier);
if (hasError) {
return true;
}
for (TransportationObject to : composesOf) {
if (to.containsError(checkIdentifier)) {
return true;
}
}
return false;
}
@Override
public void clearAllContainedCheckResults() {
super.clearAllContainedCheckResults();
for (TransportationObject to : composesOf) {
to.clearAllContainedCheckResults();
}
}
@Override
public void collectContainedErrors(List<CheckError> errors) {
super.collectContainedErrors(errors);
for (TransportationObject to : composesOf) {
to.collectContainedErrors(errors);
}
}
@Override
public boolean containsAnyError() {
boolean hasError = super.containsAnyError();
if (hasError) {
return true;
}
for (TransportationObject to : composesOf) {
if (to.containsAnyError()) {
return true;
}
}
return false;
}
@Override
public void accept(Check c) {
super.accept(c);
if (c.canExecute(this)) {
c.check(this);
}
for (TransportationObject to : composesOf) {
to.accept(c);
}
}
@Override
public AbstractCityObject getGmlObject() {
return ato;
}
@Override
public void unsetGmlGeometries() {
switch (type) {
case ROAD:
case TRACK:
case RAILWAY:
case SQUARE:
case TRANSPORTATION_COMPLEX:
TransportationComplex tc = (TransportationComplex) ato;
tc.unsetLod1MultiSurface();
tc.unsetLod2MultiSurface();
tc.unsetLod3MultiSurface();
tc.unsetLod4MultiSurface();
break;
case TRAFFIC_AREA:
TrafficArea ta = (TrafficArea) ato;
ta.unsetLod2MultiSurface();
ta.unsetLod3MultiSurface();
ta.unsetLod4MultiSurface();
break;
case AUXILLIARY_TRAFFIC_AREA:
AuxiliaryTrafficArea ata = (AuxiliaryTrafficArea) ato;
ata.unsetLod2MultiSurface();
ata.unsetLod3MultiSurface();
ata.unsetLod4MultiSurface();
break;
}
}
public void setGmlObject(AbstractTransportationObject tc) {
ato = tc;
}
public void addChild(TransportationObject subTrans) {
composesOf.add(subTrans);
}
public List<TransportationObject> getChildren() {
return composesOf;
}
public TransportationType getTransportationType() {
return type;
}
@Override
public String toString() {
return "TransportationObject [id=" + getGmlId() + "]";
}
}
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