Commit 3dac795e authored by Riegel's avatar Riegel
Browse files

Merge branch 'dev' into 'master'

Version 3.17.0 Release

See merge request !28
1 merge request!28Version 3.17.0 Release
Pipeline #11012 passed with stage
in 1 minute and 10 seconds
Showing with 3023 additions and 1905 deletions
+3023 -1905
package de.hft.stuttgart.citydoctor2.datastructure;
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.CopyHandler;
import de.hft.stuttgart.citydoctor2.utils.Copyable;
import org.citygml4j.core.model.deprecated.transportation.TransportationComplex;
import org.citygml4j.core.model.transportation.Railway;
import org.citygml4j.core.model.transportation.Road;
import org.citygml4j.core.model.transportation.Square;
import org.citygml4j.core.model.transportation.Track;
import org.citygml4j.core.model.transportation.Waterway;
import org.citygml4j.core.util.geometry.GeometryFactory;
import java.util.ArrayList;
import java.util.List;
public class TopLevelTransportFeature extends TransportationSpace {
private final List<TransportSection> sections = new ArrayList<>();
private final List<TransportSection> intersections = new ArrayList<>();
public static TopLevelTransportFeature from(Track t) {
TopLevelTransportFeature top = new TopLevelTransportFeature(TransportationType.TRACK);
top.setGmlObject(t);
return top;
}
public static TopLevelTransportFeature from(Road r) {
TopLevelTransportFeature top = new TopLevelTransportFeature(TransportationType.ROAD);
top.setGmlObject(r);
return top;
}
public static TopLevelTransportFeature from(Waterway w) {
TopLevelTransportFeature top = new TopLevelTransportFeature(TransportationType.WATERWAY);
top.setGmlObject(w);
return top;
}
public static TopLevelTransportFeature from(Railway r) {
TopLevelTransportFeature top = new TopLevelTransportFeature(TransportationType.RAILWAY);
top.setGmlObject(r);
return top;
}
public static TopLevelTransportFeature from(Square s) {
TopLevelTransportFeature top = new TopLevelTransportFeature(TransportationType.SQUARE);
top.setGmlObject(s);
return top;
}
public static TopLevelTransportFeature from(TransportationComplex tc) {
TopLevelTransportFeature top = new TopLevelTransportFeature(TransportationType.TRANSPORTATION_COMPLEX);
top.setGmlObject(tc);
return top;
}
private TopLevelTransportFeature(TransportationType type) {
super(type);
}
public void addSection(TransportSection section) {
sections.add(section);
}
public void addIntersection(TransportSection section) {
intersections.add(section);
}
public List<TransportSection> getSections() {
return sections;
}
public List<TransportSection> getIntersections() {
return intersections;
}
@Override
public void reCreateGeometries(GeometryFactory factory, ParserConfiguration config) {
super.reCreateGeometries(factory, config);
for (TransportSection section : sections) {
section.reCreateGeometries(factory, config);
}
for (TransportSection section : intersections) {
section.reCreateGeometries(factory, config);
}
}
@Override
public boolean containsError(CheckId checkIdentifier) {
boolean hasError = super.containsError(checkIdentifier);
if (hasError) {
return true;
}
for (TransportSection section : sections) {
if (section.containsError(checkIdentifier)) {
return true;
}
}
for (TransportSection section : intersections) {
if (section.containsError(checkIdentifier)) {
return true;
}
}
return false;
}
@Override
public void clearAllContainedCheckResults() {
super.clearAllContainedCheckResults();
for (TransportSection section : sections) {
section.clearAllContainedCheckResults();
}
for (TransportSection section : intersections) {
section.clearAllContainedCheckResults();
}
}
@Override
public void collectContainedErrors(List<CheckError> errors) {
super.collectContainedErrors(errors);
for (TransportSection section : sections) {
section.collectContainedErrors(errors);
}
for (TransportSection section : intersections) {
section.collectContainedErrors(errors);
}
}
@Override
public boolean containsAnyError() {
boolean hasError = super.containsAnyError();
if (hasError) {
return true;
}
for (TransportSection section : sections) {
if (section.containsAnyError()) {
return true;
}
}
for (TransportSection section : intersections) {
if (section.containsAnyError()) {
return true;
}
}
return false;
}
@Override
public void accept(Check c) {
super.accept(c);
if (c.canExecute(this)) {
c.check(this);
}
for (TransportSection section : sections) {
section.accept(c);
}
for (TransportSection section : intersections) {
section.accept(c);
}
}
@Override
public void unsetGmlGeometries() {
super.unsetGmlGeometries();
for (TransportSection section : sections) {
section.unsetGmlGeometries();
}
for (TransportSection section : intersections) {
section.unsetGmlGeometries();
}
}
@Override
public CityObject getTopLevelCityObject() {
return this;
}
@Override
public String toString() {
return "TopLevelTransportFeature [id=" + getGmlId() + "]";
}
@Override
public void prepareForChecking() {
super.prepareForChecking();
for (TransportSection section : sections) {
section.prepareForChecking();
}
for (TransportSection section : intersections) {
section.prepareForChecking();
}
}
@Override
public void clearMetaInformation() {
super.clearMetaInformation();
for (TransportSection section : sections) {
section.clearMetaInformation();
}
for (TransportSection section : intersections) {
section.clearMetaInformation();
}
}
@Override
public void collectInstances(CopyHandler handler) {
super.collectInstances(handler);
for (TransportSection section : sections) {
section.collectInstances(handler);
}
for (TransportSection section : intersections) {
section.collectInstances(handler);
}
}
@Override
public void fillValues(Copyable original, CopyHandler handler) {
super.fillValues(original, handler);
TransportationObject originalTo = (TransportationObject) original;
for (TransportSection section : sections) {
section.fillValues(originalTo, handler);
}
for (TransportSection section : intersections) {
section.fillValues(originalTo, handler);
}
}
@Override
public Copyable createCopyInstance() {
return new TopLevelTransportFeature(type);
}
}
package de.hft.stuttgart.citydoctor2.datastructure;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.CityGmlUtils;
import org.citygml4j.core.model.transportation.AuxiliaryTrafficArea;
import org.citygml4j.core.model.transportation.TrafficArea;
import org.citygml4j.core.util.geometry.GeometryFactory;
import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface;
import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty;
public class TrafficAreaObject extends TransportationObject {
public enum TrafficAreaType {
TRAFFIC_AREA, AUXILIARY_TRAFFIC_AREA
}
public TrafficAreaObject(TrafficAreaType trafficAreaType) {
if (trafficAreaType == TrafficAreaType.TRAFFIC_AREA) {
setType(TransportationType.TRAFFIC_AREA);
} else {
setType(TransportationType.AUXILLIARY_TRAFFIC_AREA);
}
}
@Override
public void reCreateGeometries(GeometryFactory factory, ParserConfiguration config) {
for (Geometry geom : getGeometries()) {
if (geom instanceof ImplicitGeometryHolder) {
continue;
}
if (geom.getType() == GeometryType.MULTI_SURFACE) {
MultiSurface ms = CityGmlUtils.createMultiSurface(geom, factory, config);
switch (type) {
case TRAFFIC_AREA:
TrafficArea ta = (TrafficArea) super.getGmlObject();
setMultiSurfaceAccordingToLod(ta, ms, geom.getLod());
break;
case AUXILLIARY_TRAFFIC_AREA:
AuxiliaryTrafficArea ata = (AuxiliaryTrafficArea) super.getGmlObject();
setMultiSurfaceAccordingToLod(ata, ms, geom.getLod());
break;
}
} else {
throw new IllegalStateException("Geometry in TransportationObject cannot be of type " + geom.getType()
+ ". Only MultiSurface allowed");
}
}
}
private void setMultiSurfaceAccordingToLod(AuxiliaryTrafficArea ata, MultiSurface ms, Lod lod) {
switch (lod) {
case LOD0:
ata.setLod0MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD1:
ata.setLod1MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD2:
ata.setLod2MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD3:
ata.setLod3MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD4:
ata.getDeprecatedProperties().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 LOD0:
ta.setLod0MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD1:
ta.setLod1MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD2:
ta.setLod2MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD3:
ta.setLod3MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD4:
ta.getDeprecatedProperties().setLod4MultiSurface(new MultiSurfaceProperty(ms));
break;
default:
throw new IllegalStateException("cannot set geometry with LOD for TrafficArea: " + lod);
}
}
@Override
public void unsetGmlGeometries() {
switch (type) {
case TRAFFIC_AREA:
TrafficArea ta = (TrafficArea) super.getGmlObject();
ta.setLod2MultiSurface(null);
ta.setLod3MultiSurface(null);
ta.getDeprecatedProperties().setLod4MultiSurface(null);
break;
case AUXILLIARY_TRAFFIC_AREA:
AuxiliaryTrafficArea ata = (AuxiliaryTrafficArea) super.getGmlObject();
ata.setLod0MultiSurface(null);
ata.setLod1MultiSurface(null);
ata.setLod2MultiSurface(null);
ata.setLod3MultiSurface(null);
ata.getDeprecatedProperties().setLod4MultiSurface(null);
break;
}
}
@Override
public CityObject getTopLevelCityObject() {
return this;
}
@Override
public String toString() {
return "TrafficArea [id=" + getGmlId() + "]";
}
}
package de.hft.stuttgart.citydoctor2.datastructure;
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;
import de.hft.stuttgart.citydoctor2.utils.CopyHandler;
import de.hft.stuttgart.citydoctor2.utils.Copyable;
import org.citygml4j.core.model.core.AbstractSpace;
import org.citygml4j.core.util.geometry.GeometryFactory;
import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface;
import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty;
import java.util.ArrayList;
import java.util.List;
public class TrafficSpaceObject extends TransportationObject {
private final List<TrafficAreaObject> trafficAreas = new ArrayList<>();
public enum TrafficSpaceType {
TRAFFIC_SPACE, AUXILIARY_TRAFFIC_SPACE
}
public TrafficSpaceObject(TrafficSpaceType trafficSpaceType) {
if (trafficSpaceType == TrafficSpaceType.TRAFFIC_SPACE) {
super.setType(TransportationType.TRAFFIC_SPACE);
} else {
super.setType(TransportationType.AUXILLIARY_TRAFFIC_SPACE);
}
}
public List<TrafficAreaObject> getTrafficAreas() {
return trafficAreas;
}
public void addTrafficArea(TrafficAreaObject trafficAreaObject) {
trafficAreas.add(trafficAreaObject);
}
@Override
public void reCreateGeometries(GeometryFactory factory, ParserConfiguration config) {
for (Geometry geom : getGeometries()) {
if (geom instanceof ImplicitGeometryHolder) {
continue;
}
MultiSurface ms = CityGmlUtils.createMultiSurface(geom, factory, config);
AbstractSpace ats = (AbstractSpace) super.getGmlObject();
setMultiSurfaceAccordingToLod(ats, ms, geom.getLod());
}
for (TrafficAreaObject tao : trafficAreas) {
tao.reCreateGeometries(factory, config);
}
}
private void setMultiSurfaceAccordingToLod(AbstractSpace ats, MultiSurface ms, Lod lod) {
switch (lod) {
case LOD0:
ats.setLod0MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD2:
ats.setLod2MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD3:
ats.setLod3MultiSurface(new MultiSurfaceProperty(ms));
break;
default:
throw new IllegalStateException("cannot set geometry with LOD for AuxiliaryTrafficSpace: " + lod);
}
}
@Override
public boolean containsError(CheckId checkIdentifier) {
boolean hasError = super.containsError(checkIdentifier);
if (hasError) {
return true;
}
for (TrafficAreaObject tao : trafficAreas) {
if (tao.containsError(checkIdentifier)) {
return true;
}
}
return false;
}
@Override
public void clearAllContainedCheckResults() {
super.clearAllContainedCheckResults();
for (TrafficAreaObject tao : trafficAreas) {
tao.clearAllContainedCheckResults();
}
}
@Override
public void collectContainedErrors(List<CheckError> errors) {
super.collectContainedErrors(errors);
for (TrafficAreaObject tao : trafficAreas) {
tao.collectContainedErrors(errors);
}
}
@Override
public boolean containsAnyError() {
boolean hasError = super.containsAnyError();
if (hasError) {
return true;
}
for (TrafficAreaObject tao : trafficAreas) {
if (tao.containsAnyError()) {
return true;
}
}
return false;
}
@Override
public void accept(Check c) {
super.accept(c);
if (c.canExecute(this)) {
c.check(this);
}
for (TrafficAreaObject tao : trafficAreas) {
tao.accept(c);
}
}
@Override
public void unsetGmlGeometries() {
AbstractSpace ats = (AbstractSpace) super.getGmlObject();
ats.setLod0MultiSurface(null);
ats.setLod2MultiSurface(null);
ats.setLod3MultiSurface(null);
for (TrafficAreaObject tao : trafficAreas) {
tao.unsetGmlGeometries();
}
}
@Override
public CityObject getTopLevelCityObject() {
return this;
}
@Override
public String toString() {
return "TransportationSpaceObject [id=" + getGmlId() + "]";
}
@Override
public void prepareForChecking() {
super.prepareForChecking();
for (TrafficAreaObject tao : trafficAreas) {
tao.prepareForChecking();
}
}
@Override
public void clearMetaInformation() {
super.clearMetaInformation();
for (TrafficAreaObject tao : trafficAreas) {
tao.clearMetaInformation();
}
}
@Override
public void collectInstances(CopyHandler handler) {
super.collectInstances(handler);
for (TrafficAreaObject tao : trafficAreas) {
handler.addInstance(tao);
}
}
@Override
public void fillValues(Copyable original, CopyHandler handler) {
super.fillValues(original, handler);
TrafficSpaceObject originalTao = (TrafficSpaceObject) original;
for (TrafficAreaObject tao : originalTao.trafficAreas) {
trafficAreas.add(handler.getCopyInstance(tao));
}
super.setGmlObject(originalTao.getGmlObject());
}
@Override
public Copyable createCopyInstance() {
return new TrafficSpaceObject(null);
}
}
package de.hft.stuttgart.citydoctor2.datastructure;
public class TransportSection extends TransportationSpace {
public enum SectionType {
SECTION, INTERSECTION
}
public TransportSection(SectionType sectionType) {
super(TransportationType.SECTION);
if (sectionType == SectionType.INTERSECTION) {
super.setType(TransportationType.INTERSECTION);
}
}
}
......@@ -43,190 +43,33 @@ import java.util.List;
*
* @author Matthias Betz
*/
public class TransportationObject extends CityObject {
public abstract class TransportationObject extends CityObject {
@Serial
private static final long serialVersionUID = -2698907271726700390L;
public enum TransportationType {
ROAD, TRACK, RAILWAY, TRAFFIC_AREA, AUXILLIARY_TRAFFIC_AREA, TRANSPORTATION_COMPLEX, SQUARE, AUXILLIARY_TRAFFIC_SPACE, TRAFFIC_SPACE
ROAD, TRACK, RAILWAY, TRAFFIC_AREA, AUXILLIARY_TRAFFIC_AREA, TRANSPORTATION_COMPLEX, SQUARE, AUXILLIARY_TRAFFIC_SPACE,
TRAFFIC_SPACE, WATERWAY, SECTION, INTERSECTION
}
private AbstractCityObject ato;
private final List<TransportationObject> composesOf = new ArrayList<>(1);
private final TransportationType type;
protected TransportationType type;
public TransportationObject(TransportationType type) {
this.type = type;
}
@Override
public FeatureType getFeatureType() {
return FeatureType.TRANSPORTATION;
}
@Override
public void reCreateGeometries(GeometryFactory factory, ParserConfiguration config) {
for (Geometry geom : getGeometries()) {
if (geom instanceof ImplicitGeometryHolder) {
continue;
}
if (geom.getType() == GeometryType.MULTI_SURFACE) {
MultiSurface ms = CityGmlUtils.createMultiSurface(geom, factory, config);
switch (type) {
case ROAD, TRACK, RAILWAY, SQUARE, TRANSPORTATION_COMPLEX:
AbstractTransportationSpace tc = (AbstractTransportationSpace) 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;
case AUXILLIARY_TRAFFIC_SPACE, TRAFFIC_SPACE:
AbstractSpace ats = (AbstractSpace) ato;
setMultiSurfaceAccordingToLod(ats, ms, geom.getLod());
break;
}
} else {
throw new IllegalStateException("Geometry in TransportationObject cannot be of type " + geom.getType()
+ ". Only MultiSurface allowed");
}
}
for (TransportationObject children : composesOf) {
children.reCreateGeometries(factory, config);
}
}
private void setMultiSurfaceAccordingToLod(AbstractSpace ats, MultiSurface ms, Lod lod) {
switch (lod) {
case LOD0:
ats.setLod0MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD2:
ats.setLod2MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD3:
ats.setLod3MultiSurface(new MultiSurfaceProperty(ms));
break;
default:
throw new IllegalStateException("cannot set geometry with LOD for AuxiliaryTrafficSpace: " + lod);
}
}
private void setMultiSurfaceAccordingToLod(AbstractTransportationSpace tc, MultiSurface ms, Lod lod) {
switch (lod) {
case LOD1:
tc.getDeprecatedProperties().setLod1MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD2:
tc.setLod2MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD3:
tc.setLod3MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD4:
tc.getDeprecatedProperties().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 LOD0:
ata.setLod0MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD1:
ata.setLod1MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD2:
ata.setLod2MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD3:
ata.setLod3MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD4:
ata.getDeprecatedProperties().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.getDeprecatedProperties().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);
}
protected TransportationObject() {
}
@Override
public boolean containsAnyError() {
boolean hasError = super.containsAnyError();
if (hasError) {
return true;
}
for (TransportationObject to : composesOf) {
if (to.containsAnyError()) {
return true;
}
}
return false;
void setType(TransportationType type) {
this.type = type;
}
@Override
public void accept(Check c) {
super.accept(c);
if (c.canExecute(this)) {
c.check(this);
}
for (TransportationObject to : composesOf) {
to.accept(c);
}
public FeatureType getFeatureType() {
return FeatureType.TRANSPORTATION;
}
@Override
......@@ -234,50 +77,11 @@ public class TransportationObject extends CityObject {
return ato;
}
@Override
public void unsetGmlGeometries() {
switch (type) {
case ROAD, TRACK, RAILWAY, SQUARE, TRANSPORTATION_COMPLEX:
AbstractTransportationSpace tc = (AbstractTransportationSpace) ato;
tc.getDeprecatedProperties().setLod1MultiSurface(null);
tc.setLod2MultiSurface(null);
tc.setLod3MultiSurface(null);
tc.getDeprecatedProperties().setLod4MultiSurface(null);
break;
case TRAFFIC_AREA:
TrafficArea ta = (TrafficArea) ato;
ta.setLod2MultiSurface(null);
ta.setLod3MultiSurface(null);
ta.getDeprecatedProperties().setLod4MultiSurface(null);
break;
case AUXILLIARY_TRAFFIC_AREA:
AuxiliaryTrafficArea ata = (AuxiliaryTrafficArea) ato;
ata.setLod0MultiSurface(null);
ata.setLod1MultiSurface(null);
ata.setLod2MultiSurface(null);
ata.setLod3MultiSurface(null);
ata.getDeprecatedProperties().setLod4MultiSurface(null);
break;
case AUXILLIARY_TRAFFIC_SPACE, TRAFFIC_SPACE:
AbstractSpace ats = (AbstractSpace) ato;
ats.setLod0MultiSurface(null);
ats.setLod2MultiSurface(null);
ats.setLod3MultiSurface(null);
break;
}
}
public void setGmlObject(AbstractCityObject tc) {
ato = tc;
}
public void addChild(TransportationObject subTrans) {
composesOf.add(subTrans);
}
public List<TransportationObject> getChildren() {
return composesOf;
}
public TransportationType getTransportationType() {
return type;
......@@ -288,43 +92,17 @@ public class TransportationObject extends CityObject {
return "TransportationObject [id=" + getGmlId() + "]";
}
@Override
public void prepareForChecking() {
super.prepareForChecking();
for (TransportationObject child : composesOf) {
child.prepareForChecking();
}
}
@Override
public void clearMetaInformation() {
super.clearMetaInformation();
for (TransportationObject child : composesOf) {
child.clearMetaInformation();
}
}
@Override
public void collectInstances(CopyHandler handler) {
super.collectInstances(handler);
for (TransportationObject to : composesOf) {
handler.addInstance(to);
}
}
@Override
public void fillValues(Copyable original, CopyHandler handler) {
super.fillValues(original, handler);
TransportationObject originalTo = (TransportationObject) original;
for (TransportationObject to : originalTo.composesOf) {
composesOf.add(handler.getCopyInstance(to));
}
ato = originalTo.ato;
}
@Override
public Copyable createCopyInstance() {
return new TransportationObject(type);
return null;
}
}
package de.hft.stuttgart.citydoctor2.datastructure;
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;
import de.hft.stuttgart.citydoctor2.utils.CopyHandler;
import de.hft.stuttgart.citydoctor2.utils.Copyable;
import org.citygml4j.core.model.core.AbstractSpace;
import org.citygml4j.core.model.transportation.AbstractTransportationSpace;
import org.citygml4j.core.util.geometry.GeometryFactory;
import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface;
import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty;
import java.util.ArrayList;
import java.util.List;
public abstract class TransportationSpace extends TransportationObject {
private final List<TrafficSpaceObject> trafficSpaces = new ArrayList<>();
private final List<TrafficSpaceObject> auxTrafficSpaces = new ArrayList<>();
protected TransportationSpace(TransportationType type) {
super(type);
}
public List<TrafficSpaceObject> getTrafficSpaces() {
return trafficSpaces;
}
public List<TrafficSpaceObject> getAuxTrafficSpaces() {
return auxTrafficSpaces;
}
public void addTrafficSpace(TrafficSpaceObject t) {
trafficSpaces.add(t);
}
public void addAuxTrafficSpace(TrafficSpaceObject t) {
auxTrafficSpaces.add(t);
}
@Override
public void reCreateGeometries(GeometryFactory factory, ParserConfiguration config) {
for (Geometry geom : getGeometries()) {
if (geom instanceof ImplicitGeometryHolder) {
continue;
}
if (geom.getType() == GeometryType.MULTI_SURFACE) {
MultiSurface ms = CityGmlUtils.createMultiSurface(geom, factory, config);
if (super.getGmlObject() instanceof AbstractTransportationSpace ats) {
setMultiSurfaceAccordingToLod(ats, ms, geom.getLod());
} else {
AbstractSpace as = (AbstractSpace) super.getGmlObject();
setMultiSurfaceAccordingToLod(as, ms, geom.getLod());
}
} else {
throw new IllegalStateException("Geometry in TransportationObject cannot be of type " + geom.getType()
+ ". Only MultiSurface allowed");
}
}
for (TrafficSpaceObject spaces : trafficSpaces) {
spaces.reCreateGeometries(factory, config);
}
for (TrafficSpaceObject spaces : auxTrafficSpaces) {
spaces.reCreateGeometries(factory, config);
}
}
private void setMultiSurfaceAccordingToLod(AbstractTransportationSpace ats, MultiSurface ms, Lod lod) {
switch (lod) {
case LOD0 -> ats.setLod0MultiSurface(new MultiSurfaceProperty(ms));
case LOD1 -> ats.getDeprecatedProperties().setLod1MultiSurface(new MultiSurfaceProperty(ms));
case LOD2 -> ats.setLod2MultiSurface(new MultiSurfaceProperty(ms));
case LOD3 -> ats.setLod3MultiSurface(new MultiSurfaceProperty(ms));
case LOD4 -> ats.getDeprecatedProperties().setLod4MultiSurface(new MultiSurfaceProperty(ms));
default ->
throw new IllegalStateException("cannot set geometry with LOD for AuxiliaryTrafficSpace: " + lod);
}
}
private void setMultiSurfaceAccordingToLod(AbstractSpace as, MultiSurface ms, Lod lod) {
switch (lod) {
case LOD0:
as.setLod0MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD2:
as.setLod2MultiSurface(new MultiSurfaceProperty(ms));
break;
case LOD3:
as.setLod3MultiSurface(new MultiSurfaceProperty(ms));
break;
default:
throw new IllegalStateException("cannot set geometry with LOD for AuxiliaryTrafficSpace: " + lod);
}
}
@Override
public boolean containsError(CheckId checkIdentifier) {
boolean hasError = super.containsError(checkIdentifier);
if (hasError) {
return true;
}
for (TrafficSpaceObject to : trafficSpaces) {
if (to.containsError(checkIdentifier)) {
return true;
}
}
for (TrafficSpaceObject to : auxTrafficSpaces) {
if (to.containsError(checkIdentifier)) {
return true;
}
}
return false;
}
@Override
public void clearAllContainedCheckResults() {
super.clearAllContainedCheckResults();
for (TrafficSpaceObject to : trafficSpaces) {
to.clearAllContainedCheckResults();
}
for (TrafficSpaceObject to : auxTrafficSpaces) {
to.clearAllContainedCheckResults();
}
}
@Override
public void collectContainedErrors(List<CheckError> errors) {
super.collectContainedErrors(errors);
for (TrafficSpaceObject to : trafficSpaces) {
to.collectContainedErrors(errors);
}
for (TrafficSpaceObject to : auxTrafficSpaces) {
to.collectContainedErrors(errors);
}
}
@Override
public boolean containsAnyError() {
boolean hasError = super.containsAnyError();
if (hasError) {
return true;
}
for (TrafficSpaceObject to : trafficSpaces) {
if (to.containsAnyError()) {
return true;
}
}
for (TrafficSpaceObject to : auxTrafficSpaces) {
if (to.containsAnyError()) {
return true;
}
}
return false;
}
@Override
public void accept(Check c) {
super.accept(c);
if (c.canExecute(this)) {
c.check(this);
}
for (TrafficSpaceObject to : trafficSpaces) {
to.accept(c);
}
for (TrafficSpaceObject to : auxTrafficSpaces) {
to.accept(c);
}
}
@Override
public void unsetGmlGeometries() {
AbstractSpace ats = (AbstractSpace) super.getGmlObject();
ats.setLod0MultiSurface(null);
ats.setLod2MultiSurface(null);
ats.setLod3MultiSurface(null);
for (TrafficSpaceObject to : trafficSpaces) {
to.unsetGmlGeometries();
}
for (TrafficSpaceObject to : auxTrafficSpaces) {
to.unsetGmlGeometries();
}
}
@Override
public CityObject getTopLevelCityObject() {
return this;
}
@Override
public String toString() {
return "TransportationSpace [id=" + getGmlId() + "]";
}
@Override
public void prepareForChecking() {
super.prepareForChecking();
for (TrafficSpaceObject to : trafficSpaces) {
to.prepareForChecking();
}
for (TrafficSpaceObject to : auxTrafficSpaces) {
to.prepareForChecking();
}
}
@Override
public void clearMetaInformation() {
super.clearMetaInformation();
for (TrafficSpaceObject to : trafficSpaces) {
to.clearMetaInformation();
}
for (TrafficSpaceObject to : auxTrafficSpaces) {
to.clearMetaInformation();
}
}
@Override
public void collectInstances(CopyHandler handler) {
super.collectInstances(handler);
for (TrafficSpaceObject to : trafficSpaces) {
to.collectInstances(handler);
}
for (TrafficSpaceObject to : auxTrafficSpaces) {
to.collectInstances(handler);
}
}
@Override
public void fillValues(Copyable original, CopyHandler handler) {
super.fillValues(original, handler);
TransportationObject originalTo = (TransportationObject) original;
for (TrafficSpaceObject to : trafficSpaces) {
to.fillValues(originalTo, handler);
}
for (TrafficSpaceObject to : auxTrafficSpaces) {
to.fillValues(originalTo, handler);
}
}
}
......@@ -34,6 +34,11 @@ public class Tunnel extends AbstractTunnel {
}
}
@Override
public CityObject getTopLevelCityObject() {
return this;
}
@Override
public void accept(Check c) {
super.accept(c);
......
......@@ -31,12 +31,20 @@ public class TunnelConstructiveElement extends CityObject {
private final org.citygml4j.core.model.tunnel.TunnelConstructiveElement gmlTunnelElement;
private final List<BoundarySurface> boundarySurfaceList = new ArrayList<>();
private AbstractTunnel parent;
public TunnelConstructiveElement(org.citygml4j.core.model.tunnel.TunnelConstructiveElement gmlObject) {
this.gmlTunnelElement = gmlObject;
}
public void setParent(AbstractTunnel parent) {
this.parent = parent;
}
public AbstractTunnel getParent() {
return parent;
}
@Override
public Copyable createCopyInstance() {
return new TunnelConstructiveElement(gmlTunnelElement);
......@@ -60,6 +68,8 @@ public class TunnelConstructiveElement extends CityObject {
MultiSurface ms = CityGmlUtils.createMultiSurface(geom, factory, config);
setMultiSurfaceAccordingToLod(geom, ms);
break;
case COMPOSITE_SURFACE:
throw new IllegalStateException("Tunnel constructive element cannot have a composite surface geometry");
}
}
for (BoundarySurface bs : boundarySurfaceList) {
......@@ -186,6 +196,11 @@ public class TunnelConstructiveElement extends CityObject {
}
}
@Override
public CityObject getTopLevelCityObject() {
return getParent().getTopLevelCityObject();
}
@Override
public FeatureType getFeatureType() {
return FeatureType.TUNNEL_CONSTRUCTION_ELEMENT;
......
......@@ -43,6 +43,11 @@ public class TunnelHollow extends AbstractRoom {
parent = handler.getCopyInstance(oHollow.getParent());
}
@Override
public CityObject getTopLevelCityObject() {
return getParent().getTopLevelCityObject();
}
@Override
public void collectInstances(CopyHandler handler) {
super.collectInstances(handler);
......
......@@ -22,6 +22,11 @@ public class TunnelPart extends AbstractTunnel {
return parent;
}
@Override
public CityObject getTopLevelCityObject() {
return getParent().getTopLevelCityObject();
}
@Override
public FeatureType getFeatureType() {
return FeatureType.TUNNEL_PART;
......
......@@ -198,6 +198,11 @@ public class Vegetation extends CityObject {
}
}
@Override
public CityObject getTopLevelCityObject() {
return this;
}
public void setGmlObject(AbstractVegetationObject avo) {
citygmlVegetation = avo;
}
......
......@@ -106,6 +106,11 @@ public class WaterObject extends CityObject {
gmlWater.getDeprecatedProperties().setLod1MultiSurface(null);
}
@Override
public CityObject getTopLevelCityObject() {
return this;
}
public void setGmlObject(WaterBody waterBody) {
gmlWater = waterBody;
}
......
......@@ -27,15 +27,14 @@ import de.hft.stuttgart.citydoctor2.datastructure.BuildingPart;
import de.hft.stuttgart.citydoctor2.datastructure.BuildingRoom;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject.BridgeType;
import de.hft.stuttgart.citydoctor2.datastructure.TunnelConstructiveElement;
import de.hft.stuttgart.citydoctor2.datastructure.TunnelFurniture;
import de.hft.stuttgart.citydoctor2.datastructure.TunnelPart;
import de.hft.stuttgart.citydoctor2.datastructure.TransportationObject.TransportationType;
import de.hft.stuttgart.citydoctor2.datastructure.Vegetation.VegetationType;
import de.hft.stuttgart.citydoctor2.math.graph.KDTree;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.Localization;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.citygml4j.core.model.CityGMLVersion;
......@@ -77,6 +76,7 @@ public class Citygml3FeatureMapper extends ObjectWalker {
private final CityDoctorModel model;
private final ParserConfiguration config;
private final Path directory;
private final CityGmlZipEntry zipEntry;
private final double neighborDistance;
private Map<String, ConcretePolygon> polygonMap = new HashMap<>();
private Map<String, CompositeCollection> compositeMap = new HashMap<>();
......@@ -88,6 +88,15 @@ public class Citygml3FeatureMapper extends ObjectWalker {
this.directory = path.getParent();
model = new CityDoctorModel(config, path.toFile());
neighborDistance = 1.8d / Math.pow(10, config.getNumberOfRoundingPlaces());
zipEntry = null;
}
public Citygml3FeatureMapper(ParserConfiguration config, CityGmlZipEntry entry) {
this.config = config;
this.directory = null;
this.zipEntry = entry;
model = new CityDoctorModel(config, Path.of(entry.getEntrySubPath()).toFile());
neighborDistance = 1.8d / Math.pow(10, config.getNumberOfRoundingPlaces());
}
public static void parseId(AbstractGML gml, GmlElement gmlElement) {
......@@ -121,8 +130,7 @@ public class Citygml3FeatureMapper extends ObjectWalker {
gco.setGmlObject(gos);
GeometryProperty<?> agLod1 = gos.getDeprecatedProperties().getLod1Geometry();
GeometryProperty<?> agLod4 = gos.getDeprecatedProperties().getLod4Geometry();
if (agLod1 != null && agLod1.getObject() != null &&
agLod1.getObject() instanceof MultiSurface ms) {
if (agLod1 != null && agLod1.getObject() != null && agLod1.getObject() instanceof MultiSurface ms) {
gco.addGeometry(parseMultiSurface(ms, Lod.LOD1));
}
if (agLod4 != null && agLod4.getObject() != null) {
......@@ -211,10 +219,14 @@ public class Citygml3FeatureMapper extends ObjectWalker {
veg.setGmlObject(solitaryVegetationObject);
mapAbstractVegetationObject(solitaryVegetationObject, veg);
parseAndAddAbstractGeometry(solitaryVegetationObject.getDeprecatedProperties().getLod1Geometry(), Lod.LOD1, veg);
parseAndAddAbstractGeometry(solitaryVegetationObject.getDeprecatedProperties().getLod2Geometry(), Lod.LOD2, veg);
parseAndAddAbstractGeometry(solitaryVegetationObject.getDeprecatedProperties().getLod3Geometry(), Lod.LOD3, veg);
parseAndAddAbstractGeometry(solitaryVegetationObject.getDeprecatedProperties().getLod4Geometry(), Lod.LOD4, veg);
parseAndAddAbstractGeometry(solitaryVegetationObject.getDeprecatedProperties().getLod1Geometry(), Lod.LOD1,
veg);
parseAndAddAbstractGeometry(solitaryVegetationObject.getDeprecatedProperties().getLod2Geometry(), Lod.LOD2,
veg);
parseAndAddAbstractGeometry(solitaryVegetationObject.getDeprecatedProperties().getLod3Geometry(), Lod.LOD3,
veg);
parseAndAddAbstractGeometry(solitaryVegetationObject.getDeprecatedProperties().getLod4Geometry(), Lod.LOD4,
veg);
finishCityObjectConstruction(veg);
model.addVegetation(veg);
......@@ -252,8 +264,7 @@ public class Citygml3FeatureMapper extends ObjectWalker {
@Override
public void visit(Bridge bridge) {
BridgeObject bo = new BridgeObject(BridgeType.BRIDGE, bridge);
BridgeObject bo = new BridgeObject(bridge);
// parse deprecated geometries
parseAndAddMultiSurface(bridge.getDeprecatedProperties().getLod1MultiSurface(), Lod.LOD1, bo);
......@@ -266,7 +277,7 @@ public class Citygml3FeatureMapper extends ObjectWalker {
continue;
}
BridgePart gmlBridgePart = bPartProperty.getObject();
BridgeObject bPart = new BridgeObject(BridgeType.BRIDGE_PART, gmlBridgePart);
BridgeObject bPart = new BridgeObject(gmlBridgePart, bo);
mapAbstractBridge(gmlBridgePart, bPart);
bo.addBridgePart(bPart);
}
......@@ -399,24 +410,11 @@ public class Citygml3FeatureMapper extends ObjectWalker {
parseAndAddMultiSurface(gmlTunnel.getDeprecatedProperties().getLod4MultiSurface(), Lod.LOD4, cdTunnel);
parseAndAddSolid(gmlTunnel.getDeprecatedProperties().getLod4Solid(), Lod.LOD4, cdTunnel);
for (TunnelInstallationProperty tiProp : gmlTunnel.getTunnelInstallations()) {
var gmlTi = tiProp.getObject();
if (gmlTi == null) {
// ignore empty properties
continue;
}
Installation ti = mapTunnelInstallation(gmlTi);
cdTunnel.addTunnelInstallation(ti);
}
for (HollowSpaceProperty thProp : gmlTunnel.getHollowSpaces()) {
var gmlTh = thProp.getObject();
if (gmlTh == null) {
continue;
}
TunnelHollow br = mapTunnelHollow(gmlTh);
cdTunnel.addTunnelHollow(br);
}
parseTunnelInstallations(gmlTunnel, cdTunnel);
parseHollowSpaces(gmlTunnel, cdTunnel);
parseTunnelFurniture(gmlTunnel, cdTunnel);
parseTunnelConstructiveElements(gmlTunnel, cdTunnel);
parseTunnelBoundarySurfaces(gmlTunnel);
for (Installation ti : cdTunnel.getTunnelInstallations()) {
updateEdgesAndVertices(ti);
for (BoundarySurface bs : ti.getBoundarySurfaces()) {
......@@ -426,24 +424,14 @@ public class Citygml3FeatureMapper extends ObjectWalker {
}
}
}
for (TunnelFurnitureProperty tfProp : gmlTunnel.getTunnelFurniture()) {
var gmlTf = tfProp.getObject();
if (gmlTf == null) {
continue;
}
TunnelFurniture tf = mapTunnelFurniture(gmlTf);
cdTunnel.addTunnelFurniture(tf);
}
for (TunnelConstructiveElementProperty teProp : gmlTunnel.getTunnelConstructiveElements()) {
var gmlTc = teProp.getObject();
if (gmlTc == null) {
continue;
}
TunnelConstructiveElement tc = mapTunnelConstructiveElement(gmlTc);
cdTunnel.addTunnelConstructiveElement(tc);
cdTunnel.unsetGmlGeometries();
resolveAndClearReferences();
updateEdgesAndVertices(cdTunnel);
}
private void parseTunnelBoundarySurfaces(org.citygml4j.core.model.tunnel.AbstractTunnel gmlTunnel) {
SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config);
for (AbstractSpaceBoundaryProperty surfaceProp : gmlTunnel.getBoundaries()) {
if (!surfaceProp.isSetObject()) {
......@@ -458,11 +446,51 @@ public class Citygml3FeatureMapper extends ObjectWalker {
updateEdgesAndVertices(o);
}
}
}
private void parseTunnelConstructiveElements(org.citygml4j.core.model.tunnel.AbstractTunnel gmlTunnel, AbstractTunnel cdTunnel) {
for (TunnelConstructiveElementProperty teProp : gmlTunnel.getTunnelConstructiveElements()) {
var gmlTc = teProp.getObject();
if (gmlTc == null) {
continue;
}
TunnelConstructiveElement tc = mapTunnelConstructiveElement(gmlTc);
cdTunnel.addTunnelConstructiveElement(tc);
}
}
cdTunnel.unsetGmlGeometries();
resolveAndClearReferences();
updateEdgesAndVertices(cdTunnel);
private void parseTunnelFurniture(org.citygml4j.core.model.tunnel.AbstractTunnel gmlTunnel, AbstractTunnel cdTunnel) {
for (TunnelFurnitureProperty tfProp : gmlTunnel.getTunnelFurniture()) {
var gmlTf = tfProp.getObject();
if (gmlTf == null) {
continue;
}
TunnelFurniture tf = mapTunnelFurniture(gmlTf);
cdTunnel.addTunnelFurniture(tf);
}
}
private void parseHollowSpaces(org.citygml4j.core.model.tunnel.AbstractTunnel gmlTunnel, AbstractTunnel cdTunnel) {
for (HollowSpaceProperty thProp : gmlTunnel.getHollowSpaces()) {
var gmlTh = thProp.getObject();
if (gmlTh == null) {
continue;
}
TunnelHollow br = mapTunnelHollow(gmlTh);
cdTunnel.addTunnelHollow(br);
}
}
private void parseTunnelInstallations(org.citygml4j.core.model.tunnel.AbstractTunnel gmlTunnel, AbstractTunnel cdTunnel) {
for (TunnelInstallationProperty tiProp : gmlTunnel.getTunnelInstallations()) {
var gmlTi = tiProp.getObject();
if (gmlTi == null) {
// ignore empty properties
continue;
}
Installation ti = mapTunnelInstallation(gmlTi);
cdTunnel.addTunnelInstallation(ti);
}
}
private Installation mapTunnelInstallation(TunnelInstallation gmlTi) {
......@@ -567,7 +595,8 @@ public class Citygml3FeatureMapper extends ObjectWalker {
return tf;
}
private TunnelConstructiveElement mapTunnelConstructiveElement(org.citygml4j.core.model.tunnel.TunnelConstructiveElement gmlTe) {
private TunnelConstructiveElement mapTunnelConstructiveElement(
org.citygml4j.core.model.tunnel.TunnelConstructiveElement gmlTe) {
TunnelConstructiveElement te = new TunnelConstructiveElement(gmlTe);
mapAbstractOccupiedSpace(gmlTe, te);
te.unsetGmlGeometries();
......@@ -601,7 +630,8 @@ public class Citygml3FeatureMapper extends ObjectWalker {
}
}
private void mapConstructiveElement(org.citygml4j.core.model.bridge.BridgeConstructiveElement ele, BridgeConstructiveElement bce) {
private void mapConstructiveElement(org.citygml4j.core.model.bridge.BridgeConstructiveElement ele,
BridgeConstructiveElement bce) {
mapAbstractConstructiveElement(ele, bce);
SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config);
......@@ -617,7 +647,8 @@ public class Citygml3FeatureMapper extends ObjectWalker {
}
}
private void mapAbstractConstructiveElement(org.citygml4j.core.model.bridge.BridgeConstructiveElement ele, BridgeConstructiveElement bce) {
private void mapAbstractConstructiveElement(org.citygml4j.core.model.bridge.BridgeConstructiveElement ele,
BridgeConstructiveElement bce) {
mapAbstractOccupiedSpace(ele, bce);
}
......@@ -626,26 +657,52 @@ public class Citygml3FeatureMapper extends ObjectWalker {
}
@Override
public void visit(AuxiliaryTrafficArea ata) {
TransportationObject to = new TransportationObject(TransportationType.AUXILLIARY_TRAFFIC_AREA);
mapAbstractThematicSurface(ata, to);
finishTransportationMapping(to);
public void visit(Track track) {
TopLevelTransportFeature top = TopLevelTransportFeature.from(track);
mapSectionsAndIntersections(track.getSections(), track.getIntersections(), top);
mapAbstractTransportationSpace(track, top);
finishTransportationMapping(top);
}
@Override
public void visit(TrafficArea ta) {
TransportationObject to = new TransportationObject(TransportationType.TRAFFIC_AREA);
mapAbstractThematicSurface(ta, to);
finishTransportationMapping(to);
public void visit(Road road) {
TopLevelTransportFeature top = TopLevelTransportFeature.from(road);
mapSectionsAndIntersections(road.getSections(), road.getIntersections(), top);
mapAbstractTransportationSpace(road, top);
finishTransportationMapping(top);
}
@Override
public void visit(Road road) {
TransportationObject to = new TransportationObject(TransportationType.ROAD);
mapAbstractTransportationSpace(road, to);
finishTransportationMapping(to);
public void visit(Waterway waterway) {
TopLevelTransportFeature top = TopLevelTransportFeature.from(waterway);
mapSectionsAndIntersections(waterway.getSections(), waterway.getIntersections(), top);
mapAbstractTransportationSpace(waterway, top);
finishTransportationMapping(top);
}
@Override
public void visit(Railway railway) {
TopLevelTransportFeature top = TopLevelTransportFeature.from(railway);
mapSectionsAndIntersections(railway.getSections(), railway.getIntersections(), top);
mapAbstractTransportationSpace(railway, top);
finishTransportationMapping(top);
}
@Override
public void visit(Square square) {
TopLevelTransportFeature top = TopLevelTransportFeature.from(square);
mapAbstractTransportationSpace(square, top);
finishTransportationMapping(top);
}
@Override
public void visit(TransportationComplex tc) {
TopLevelTransportFeature top = TopLevelTransportFeature.from(tc);
mapAbstractTransportationSpace(tc, top);
finishTransportationMapping(top);
}
private void mapAbstractThematicSurface(AbstractThematicSurface ats, CityObject co) {
mapAbstractSpaceBoundary(ats, co);
parseAndAddMultiSurface(ats.getLod0MultiSurface(), Lod.LOD0, co);
......@@ -675,33 +732,6 @@ public class Citygml3FeatureMapper extends ObjectWalker {
parseId(ag, to);
}
@Override
public void visit(Railway railway) {
TransportationObject to = new TransportationObject(TransportationType.RAILWAY);
mapAbstractTransportationSpace(railway, to);
finishTransportationMapping(to);
}
@Override
public void visit(Square square) {
TransportationObject to = new TransportationObject(TransportationType.SQUARE);
mapAbstractTransportationSpace(square, to);
finishTransportationMapping(to);
}
@Override
public void visit(Track track) {
TransportationObject to = new TransportationObject(TransportationType.TRACK);
mapAbstractTransportationSpace(track, to);
finishTransportationMapping(to);
}
@Override
public void visit(TransportationComplex transportationComplex) {
TransportationObject to = new TransportationObject(TransportationType.TRANSPORTATION_COMPLEX);
mapAbstractTransportationSpace(transportationComplex, to);
finishTransportationMapping(to);
}
private void finishTransportationMapping(TransportationObject to) {
finishCityObjectConstruction(to);
......@@ -714,39 +744,97 @@ public class Citygml3FeatureMapper extends ObjectWalker {
updateEdgesAndVertices(co);
}
private void mapAbstractTransportationSpace(AbstractTransportationSpace ats, TransportationObject to) {
to.setGmlObject(ats);
parseAbstractTransportationSpaceGeometries(ats, to);
private void mapAbstractTransportationSpace(AbstractTransportationSpace ats, TransportationSpace trsp) {
parseAbstractTransportationSpaceGeometries(ats, trsp);
for (TrafficSpaceProperty tsp : ats.getTrafficSpaces()) {
if (tsp.isSetObject()) {
TransportationObject trafficSpace = new TransportationObject(TransportationType.TRAFFIC_SPACE);
mapTrafficSpace(tsp.getObject(), trafficSpace);
finishTransportationMapping(trafficSpace);
to.getChildren().add(trafficSpace);
TrafficSpaceObject tso = new TrafficSpaceObject(TrafficSpaceObject.TrafficSpaceType.TRAFFIC_SPACE);
mapTrafficSpace(tsp.getObject(), tso);
trsp.addTrafficSpace(tso);
}
}
for (AuxiliaryTrafficSpaceProperty atsp : ats.getAuxiliaryTrafficSpaces()) {
if (atsp.isSetObject()) {
TrafficSpaceObject atso = new TrafficSpaceObject(TrafficSpaceObject.TrafficSpaceType.AUXILIARY_TRAFFIC_SPACE);
mapAuxiliaryTrafficSpace(atsp.getObject(), atso);
trsp.addTrafficSpace(atso);
}
}
}
private void mapSectionsAndIntersections(List<SectionProperty> sectionProps, List<IntersectionProperty> intersectionProps,
TopLevelTransportFeature top) {
for (SectionProperty sectionProp : sectionProps) {
if (sectionProp.isSetObject()) {
TransportSection sect = new TransportSection(TransportSection.SectionType.SECTION);
sect.setGmlObject(sectionProp.getObject());
mapAbstractTransportationSpace(sectionProp.getObject(), sect);
finishCityObjectConstruction(sect);
top.addSection(sect);
}
}
for (AuxiliaryTrafficSpaceProperty auxTrafficProp : ats.getAuxiliaryTrafficSpaces()) {
if (auxTrafficProp.isSetObject()) {
TransportationObject trafficSpace = parseAuxiliaryTrafficSpace(auxTrafficProp.getObject());
finishTransportationMapping(trafficSpace);
to.addChild(trafficSpace);
for (IntersectionProperty intersectionProp : intersectionProps) {
if (intersectionProp.isSetObject()) {
TransportSection isect = new TransportSection(TransportSection.SectionType.INTERSECTION);
isect.setGmlObject(intersectionProp.getObject());
mapAbstractTransportationSpace(intersectionProp.getObject(), isect);
finishCityObjectConstruction(isect);
top.addIntersection(isect);
}
}
}
private void mapTrafficSpace(TrafficSpace ts, TransportationObject to) {
mapAbstractUnoccupiedSpace(ts, to);
private void mapTrafficSpace(TrafficSpace ts, TrafficSpaceObject tso) {
tso.setGmlObject(ts);
mapAbstractUnoccupiedSpace(ts, tso);
for (AbstractSpaceBoundaryProperty boundary : ts.getBoundaries()) {
if (boundary.isSetObject()) {
TrafficArea area = (TrafficArea) boundary.getObject();
TrafficAreaObject tao = new TrafficAreaObject(TrafficAreaObject.TrafficAreaType.TRAFFIC_AREA);
mapTrafficArea(area, tao);
tso.addTrafficArea(tao);
}
}
finishCityObjectConstruction(tso);
}
private void mapAbstractUnoccupiedSpace(AbstractUnoccupiedSpace aus, CityObject co) {
mapAbstractPhysicalSpace(aus, co);
private void mapAuxiliaryTrafficSpace(AuxiliaryTrafficSpace ats, TrafficSpaceObject tso) {
tso.setGmlObject(ats);
mapAbstractUnoccupiedSpace(ats, tso);
for (AbstractSpaceBoundaryProperty boundary : ats.getBoundaries()) {
if (boundary.isSetObject()) {
AuxiliaryTrafficArea area = (AuxiliaryTrafficArea) boundary.getObject();
TrafficAreaObject tao = new TrafficAreaObject(TrafficAreaObject.TrafficAreaType.AUXILIARY_TRAFFIC_AREA);
mapAuxiliaryTrafficArea(area, tao);
tso.addTrafficArea(tao);
}
}
finishCityObjectConstruction(tso);
}
private TransportationObject parseAuxiliaryTrafficSpace(AuxiliaryTrafficSpace ats) {
TransportationObject to = new TransportationObject(TransportationType.AUXILLIARY_TRAFFIC_SPACE);
parseAbstractSpaceGeometries(ats, to);
finishCityObjectConstruction(to);
return to;
private void mapTrafficArea(TrafficArea ta, TrafficAreaObject tao) {
tao.setGmlObject(ta);
parseAndAddMultiSurface(ta.getLod0MultiSurface(), Lod.LOD0, tao);
parseAndAddMultiSurface(ta.getLod1MultiSurface(), Lod.LOD1, tao);
parseAndAddMultiSurface(ta.getLod2MultiSurface(), Lod.LOD2, tao);
parseAndAddMultiSurface(ta.getLod3MultiSurface(), Lod.LOD3, tao);
parseAndAddMultiSurface(ta.getDeprecatedProperties().getLod4MultiSurface(), Lod.LOD4, tao);
finishCityObjectConstruction(tao);
}
private void mapAuxiliaryTrafficArea(AuxiliaryTrafficArea ats, TrafficAreaObject tao) {
tao.setGmlObject(ats);
parseAndAddMultiSurface(ats.getLod0MultiSurface(), Lod.LOD0, tao);
parseAndAddMultiSurface(ats.getLod1MultiSurface(), Lod.LOD1, tao);
parseAndAddMultiSurface(ats.getLod2MultiSurface(), Lod.LOD2, tao);
parseAndAddMultiSurface(ats.getLod3MultiSurface(), Lod.LOD3, tao);
parseAndAddMultiSurface(ats.getDeprecatedProperties().getLod4MultiSurface(), Lod.LOD4, tao);
finishCityObjectConstruction(tao);
}
private void mapAbstractUnoccupiedSpace(AbstractUnoccupiedSpace aus, CityObject co) {
mapAbstractPhysicalSpace(aus, co);
}
private void parseAbstractTransportationSpaceGeometries(AbstractTransportationSpace space, CityObject co) {
......@@ -782,22 +870,6 @@ public class Citygml3FeatureMapper extends ObjectWalker {
}
}
private void parseAndAddCompositeSurface(MultiSurfaceProperty ms, Lod lod, CityObject co) {
if (ms == null || ms.getObject() == null) {
return;
}
List<SurfaceProperty> surfaces = ms.getObject().getSurfaceMember();
for (SurfaceProperty surface : surfaces) {
if (surface.getObject() instanceof CompositeSurface cs) {
Geometry geom = parseCompositeSurface(cs, lod);
if (geom != null) {
co.addGeometry(geom);
}
}
}
}
private void parseImplicitGeometry(AbstractOccupiedSpace aos, CityObject co) {
for (int i = 1; i <= 3; i++) {
if (aos.getImplicitRepresentation(i) == null) {
......@@ -811,10 +883,10 @@ public class Citygml3FeatureMapper extends ObjectWalker {
}
} else if (aos.getImplicitRepresentation(i).getHref() != null) {
// Hrefs of ImplicitGeometries point to its RelativeGeometry.
// Can be ignored for CD since the Href is only used for repeating the RelativeGeometry at different LODs
// Can be ignored for CD since the Href is only used for repeating the
// RelativeGeometry at different LODs
}
}
}
......@@ -822,8 +894,16 @@ public class Citygml3FeatureMapper extends ObjectWalker {
private ImplicitGeometryHolder resolveImplicitGeometry(ImplicitGeometry ig, int lodInt) {
ImplicitGeometryHolder igh = null;
if (ig.getLibraryObject() != null) {
LibraryObject libObj = null;
if (directory != null){
Path libraryObjectPath = directory.resolve(ig.getLibraryObject());
LibraryObject libObj = LibraryObject.of(libraryObjectPath, config);
libObj = LibraryObject.of(libraryObjectPath, config);
} else {
String libraryObjectPath = ig.getLibraryObject();
Path loPath = zipEntry.resolveLibraryObjectPath(libraryObjectPath);
CityGmlZipEntry libEntry = zipEntry.getArchive().getEntry(loPath.toString());
libObj = LibraryObject.of(libEntry, config);
}
if (libObj != null) {
igh = new ImplicitGeometryHolder(ig, libObj);
}
......@@ -845,7 +925,7 @@ public class Citygml3FeatureMapper extends ObjectWalker {
}
} else {
logger.error(String.format("Implicit geometry of GML-ID %s has no referenced geometry.", ig.getId()));
logger.error("Implicit geometry of GML-ID {} has no referenced geometry.", ig.getId());
}
return igh;
}
......@@ -859,8 +939,20 @@ public class Citygml3FeatureMapper extends ObjectWalker {
Geometry geom = ref.geometry();
CompositeCollection comp = compositeMap.get(href);
if (comp != null) {
// composite collection, add each containing polygon to the geometry
comp.getCompositeMembers().forEach(geom::addPolygon);
} else {
handlePolygonReference(href, geom);
}
}
// clear storage for polygons and vertices
references = new ArrayList<>();
vertexMap = new HashMap<>();
polygonMap = new HashMap<>();
}
private void handlePolygonReference(String href, Geometry geom) {
ConcretePolygon concPoly = polygonMap.get(href);
if (concPoly == null) {
if (logger.isWarnEnabled()) {
......@@ -877,14 +969,6 @@ public class Citygml3FeatureMapper extends ObjectWalker {
geom.addPolygon(lPoly);
}
}
}
// clear storage for polygons and vertices
// probably faster than .clear() ?
references = new ArrayList<>();
vertexMap = new HashMap<>();
polygonMap = new HashMap<>();
}
private void readAbstractBuilding(org.citygml4j.core.model.building.AbstractBuilding gmlAb,
AbstractBuilding cdBuilding) {
......@@ -896,25 +980,47 @@ public class Citygml3FeatureMapper extends ObjectWalker {
parseAndAddMultiSurface(gmlAb.getDeprecatedProperties().getLod4MultiSurface(), Lod.LOD4, cdBuilding);
parseAndAddSolid(gmlAb.getDeprecatedProperties().getLod4Solid(), Lod.LOD4, cdBuilding);
for (BuildingInstallationProperty biProp : gmlAb.getBuildingInstallations()) {
var gmlBi = biProp.getObject();
if (gmlBi == null) {
// ignore empty properties
parseBuildingInstallations(gmlAb, cdBuilding);
parseBuildingRooms(gmlAb, cdBuilding);
parseBuildingSubdivisions(gmlAb, cdBuilding);
parseBoundarySurfaces(gmlAb, cdBuilding);
parseBuildingFurniture(gmlAb, cdBuilding);
}
private void parseBuildingFurniture(org.citygml4j.core.model.building.AbstractBuilding gmlAb, AbstractBuilding cdBuilding) {
for (BuildingFurnitureProperty bfProp : gmlAb.getBuildingFurniture()) {
var gmlBf = bfProp.getObject();
if (gmlBf == null) {
continue;
}
Installation bi = mapBuildingInstallation(gmlBi);
cdBuilding.addBuildingInstallation(bi);
BuildingRoomFurniture bf = mapBuildingFurniture(gmlBf);
cdBuilding.addBuildingRoomFurniture(bf);
}
}
for (BuildingRoomProperty brProp : gmlAb.getBuildingRooms()) {
var gmlBr = brProp.getObject();
if (gmlBr == null) {
private void parseBoundarySurfaces(org.citygml4j.core.model.building.AbstractBuilding gmlAb, AbstractBuilding cdBuilding) {
SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config);
for (AbstractSpaceBoundaryProperty surfaceProp : gmlAb.getBoundaries()) {
if (!surfaceProp.isSetObject()) {
continue;
}
BuildingRoom br = mapBuildingRoom(gmlBr);
cdBuilding.addBuildingRoom(br);
AbstractSpaceBoundary surface = surfaceProp.getObject();
surface.accept(surfaceMapper);
}
updatePartOfSurface(cdBuilding, surfaceMapper);
cdBuilding.unsetGmlGeometries();
resolveAndClearReferences();
updateEdgesAndVertices(cdBuilding);
for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
updateEdgesAndVertices(bs);
for (Opening o : bs.getOpenings()) {
updateEdgesAndVertices(o);
}
}
}
private void parseBuildingSubdivisions(org.citygml4j.core.model.building.AbstractBuilding gmlAb, AbstractBuilding cdBuilding) {
for (AbstractBuildingSubdivisionProperty abs : gmlAb.getBuildingSubdivisions()) {
var gmlABS = abs.getObject();
if (gmlABS == null) {
......@@ -928,25 +1034,28 @@ public class Citygml3FeatureMapper extends ObjectWalker {
cdBuilding.addBuildingUnit(bu);
}
}
}
SurfaceMapper surfaceMapper = new SurfaceMapper(polygonMap, references, compositeMap, vertexMap, config);
for (AbstractSpaceBoundaryProperty surfaceProp : gmlAb.getBoundaries()) {
if (!surfaceProp.isSetObject()) {
private void parseBuildingRooms(org.citygml4j.core.model.building.AbstractBuilding gmlAb, AbstractBuilding cdBuilding) {
for (BuildingRoomProperty brProp : gmlAb.getBuildingRooms()) {
var gmlBr = brProp.getObject();
if (gmlBr == null) {
continue;
}
AbstractSpaceBoundary surface = surfaceProp.getObject();
surface.accept(surfaceMapper);
BuildingRoom br = mapBuildingRoom(gmlBr);
cdBuilding.addBuildingRoom(br);
}
}
updatePartOfSurface(cdBuilding, surfaceMapper);
cdBuilding.unsetGmlGeometries();
resolveAndClearReferences();
updateEdgesAndVertices(cdBuilding);
for (BoundarySurface bs : surfaceMapper.getSurfaces()) {
updateEdgesAndVertices(bs);
for (Opening o : bs.getOpenings()) {
updateEdgesAndVertices(o);
private void parseBuildingInstallations(org.citygml4j.core.model.building.AbstractBuilding gmlAb, AbstractBuilding cdBuilding) {
for (BuildingInstallationProperty biProp : gmlAb.getBuildingInstallations()) {
var gmlBi = biProp.getObject();
if (gmlBi == null) {
// ignore empty properties
continue;
}
Installation bi = mapBuildingInstallation(gmlBi);
cdBuilding.addBuildingInstallation(bi);
}
for (Installation bi : cdBuilding.getBuildingInstallations()) {
updateEdgesAndVertices(bi);
......@@ -957,14 +1066,6 @@ public class Citygml3FeatureMapper extends ObjectWalker {
}
}
}
for (BuildingFurnitureProperty bfProp : gmlAb.getBuildingFurniture()) {
var gmlBf = bfProp.getObject();
if (gmlBf == null) {
continue;
}
BuildingRoomFurniture bf = mapBuildingFurniture(gmlBf);
cdBuilding.addBuildingRoomFurniture(bf);
}
}
private BuildingRoom mapBuildingRoom(org.citygml4j.core.model.building.BuildingRoom gmlBr) {
......@@ -1006,7 +1107,6 @@ public class Citygml3FeatureMapper extends ObjectWalker {
br.addFurnitureRef(bfProp);
}
return br;
}
......
......@@ -59,10 +59,55 @@ public class Citygml3GeometryMapper extends GeometryWalker {
public void visit(Polygon polygon) {
parsePolygon(polygon.getId(), polygon.getExterior(), polygon.getInterior());
if (polygon.getExterior() == null) {
logger.warn(String.format("No exterior: %s", polygon.getId()));
logger.warn("No exterior: {}", polygon.getId());
}
}
@Override
public void visit(Surface surface) {
if (surface.getPatches() != null && !surface.getPatches().isSetObjects()) {
logger.warn("Surface {} has no PolygonPatches.", surface.getId());
return;
}
PatchCollection patchCollection = new PatchCollection();
GeometryWalker patchCollector = new GeometryWalker() {
@Override
public void visit(PolygonPatch patch) {
parsePolygonPatch(patch, patchCollection);
}
};
surface.getPatches().getObjects().forEach(abstractSurfacePatch -> abstractSurfacePatch.accept(patchCollector));
}
private void parsePolygonPatch(PolygonPatch patch, PatchCollection collection) {
AbstractRingProperty exterior = patch.getExterior();
List<AbstractRingProperty> interior = patch.getInterior();
if (exterior == null || exterior.getObject() == null) {
if (logger.isWarnEnabled()) {
logger.warn(Localization.getText("GeometryMapper.emptyPolygon"));
}
return;
}
ConcretePolygon conc = new ConcretePolygon();
polygons.add(conc);
currentRing = new LinearRing(LinearRingType.EXTERIOR);
exterior.getObject().accept(this);
conc.setExteriorRing(currentRing);
for (AbstractRingProperty interiorGmlRing : interior) {
AbstractRing gmlRing = interiorGmlRing.getObject();
if (gmlRing == null) {
continue;
}
currentRing = new LinearRing(LinearRingType.INTERIOR);
gmlRing.accept(this);
conc.addInteriorRing(currentRing);
}
collection.addPatchMember(conc);
polygons.add(conc);
}
private void parsePolygon(String id, AbstractRingProperty exterior, List<AbstractRingProperty> interior) {
if (exterior == null || exterior.getObject() == null) {
if (logger.isWarnEnabled()) {
......
......@@ -39,6 +39,25 @@ public class MovedPolygon {
private MovedRing exteriorRing;
private List<MovedRing> innerRings;
/**
* Moves the polygon by the first exterior vertex. The center of the polygon
* will not me the origin, the polygon will just be near the origin. This can
* eliviate numeric issues in large calculations.
*
* @param p the polygon that is moved to the center.
* @return the moved polygon.
*/
public static MovedPolygon ofPolygon(Polygon p) {
Vector3d movedBy = p.getExteriorRing().getVertices().get(0);
MovedPolygon indPoly = new MovedPolygon();
indPoly.original = p;
indPoly.exteriorRing = MovedRing.ofRing(p.getExteriorRing(), movedBy);
for (LinearRing inner : p.getInnerRings()) {
indPoly.addInnerRing(MovedRing.ofRing(inner, movedBy));
}
return indPoly;
}
public static MovedPolygon ofPolygon(Polygon p, Vector3d movedBy) {
MovedPolygon indPoly = new MovedPolygon();
indPoly.original = p;
......
......@@ -41,6 +41,11 @@ public class Polygon2d {
return projectTo2D(poly, axis);
}
public static Polygon2d withProjection(MovedPolygon poly) {
ProjectionAxis axis = ProjectionAxis.of(poly.getOriginal());
return projectTo2D(poly, axis);
}
public static Polygon2d withProjection(MovedPolygon poly, ProjectionAxis projectionAxis) {
return projectTo2D(poly, projectionAxis);
}
......
......@@ -87,12 +87,27 @@ public class ProjectionAxis {
this.axis = axis;
}
/**
* This will write the two coordinates determined in this projection axis into a
* given array at the startIndex location. The array has to have at least a
* length of startIndex + 1.
*
* @param v the vector from which the coordinates are taken.
* @param array the array that is written to.
* @param startIndex the start location in the array.
*/
public void writeCoordinatesOfVectorInArray(Vector3d v, double[] array, int startIndex) {
array[startIndex] = v.getCoordinate(axis[0]);
array[startIndex + 1] = v.getCoordinate(axis[1]);
}
public Vector2d project(Vector3d v) {
return new Vector2d(v.getCoordinate(axis[0]), v.getCoordinate(axis[1]));
}
/**
* calculates the missing coordinate for 3d vector from the plane and this axis.
*
* @return the projected 3d point.
*/
public Vector3d projectToPlane(Plane plane, Vector2d v) {
......@@ -101,6 +116,7 @@ public class ProjectionAxis {
/**
* calculates the missing coordinate for 3d vector from the plane and this axis.
*
* @return the projected 3d point.
*/
public Vector3d projectToPlane(Plane plane, double vectorX, double vectorY) {
......
......@@ -22,6 +22,8 @@ import java.io.Serial;
import java.io.Serializable;
import java.util.Arrays;
import org.locationtech.jts.geom.Coordinate;
/**
* A three dimensional vector
*
......@@ -45,6 +47,17 @@ public class Vector3d implements Serializable {
this(0d, 0d, 0d);
}
/**
* Convert JTS Coordinate class to Vector3d.
* @param coord JTS Coordinate
*/
public Vector3d(Coordinate coord) {
coords = new double[3];
coords[0] = coord.getX();
coords[1] = coord.getY();
coords[2] = coord.getZ();
}
public Vector3d(double x, double y, double z) {
coords = new double[3];
coords[0] = x;
......
......@@ -18,30 +18,16 @@
*/
package de.hft.stuttgart.citydoctor2.parser;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.mapper.citygml3.Citygml3FeatureMapper;
import de.hft.stuttgart.citydoctor2.mapper.citygml3.GMLValidationHandler;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.utils.Localization;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry;
import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntryFile;
import de.hft.stuttgart.quality.QualityADEContext;
import de.hft.stuttgart.quality.QualityADEModule;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
......@@ -69,31 +55,48 @@ import org.locationtech.proj4j.BasicCoordinateTransform;
import org.locationtech.proj4j.CRSFactory;
import org.locationtech.proj4j.CoordinateReferenceSystem;
import org.locationtech.proj4j.ProjCoordinate;
import org.locationtech.proj4j.io.Proj4FileReader;
import org.locationtech.proj4j.proj.Projection;
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 org.xml.sax.SAXParseException;
import org.xmlobjects.schema.SchemaHandler;
import org.xmlobjects.schema.SchemaHandlerException;
import org.xmlobjects.stream.XMLReader;
import org.xmlobjects.stream.XMLReaderFactory;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.mapper.citygml3.Citygml3FeatureMapper;
import de.hft.stuttgart.citydoctor2.mapper.citygml3.GMLValidationHandler;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.utils.Localization;
import de.hft.stuttgart.quality.QualityADEContext;
import de.hft.stuttgart.quality.QualityADEModule;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Utility class to parse CityGML files.
*
* @author Matthias Betz
*
*/
public class CityGmlParser {
......@@ -119,11 +122,13 @@ public class CityGmlParser {
private static final SAXParserFactory FACTORY;
private static Proj4FileReader csReader = new Proj4FileReader();
private static final Pattern P_VUNITS = Pattern.compile("^\\+vunits=.+");
private static final Pattern P_GEOIDGRID = Pattern.compile("^\\+geoidgrid=.+");
private static CityGMLContext context;
private static List<QName> chunkProperties = new ArrayList<>();
// Toggle to suppress logger output for parsing of libraryObjects
private static boolean gagged = false;
static {
System.setProperty("javax.xml.transform.TransformerFactory", "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");
......@@ -160,18 +165,23 @@ public class CityGmlParser {
return context;
}
public static CityDoctorModel parseCityGmlFileSilently(String file, ParserConfiguration config)
throws CityGmlParseException, InvalidGmlFileException {
return parseCityGmlFile(file, config, null, null, false);
}
public static CityDoctorModel parseCityGmlFile(String file, ParserConfiguration config)
throws CityGmlParseException, InvalidGmlFileException {
return parseCityGmlFile(file, config, null, null);
return parseCityGmlFile(file, config, null, null, true);
}
public static CityDoctorModel parseCityGmlFile(String file, ParserConfiguration config, ProgressListener l)
throws CityGmlParseException, InvalidGmlFileException {
return parseCityGmlFile(file, config, l, null);
return parseCityGmlFile(file, config, l, null, true);
}
public static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config, ProgressListener l,
GMLValidationHandler handler) throws CityGmlParseException, InvalidGmlFileException {
GMLValidationHandler handler, boolean verbose) throws CityGmlParseException, InvalidGmlFileException {
CityGMLContext context = getContext();
Path file = Paths.get(filePath);
if (config.getValidate()) {
......@@ -189,10 +199,124 @@ public class CityGmlParser {
if (l != null) {
ois.addListener(l::updateProgress);
}
return readAndKeepFeatures(config, file, in, ois);
return readAndKeepFeatures(config, file, in, ois, verbose);
}
} catch (CityGMLReadException | IOException e) {
throw new CityGmlParseException("Failed to read CityGML file", e);
}
}
public static CityDoctorModel parseCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config)
throws CityGmlParseException, InvalidGmlFileException, IOException {
return parseCityGmlZipEntry(entry, config, null);
}
public static CityDoctorModel parseCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config, ProgressListener l)
throws CityGmlParseException, InvalidGmlFileException, IOException {
CityGMLContext context = getContext();
if (config.getValidate()) {
try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) {
List<String> messages = validateStream(entryFile.getInputStream(), context);
if (!messages.isEmpty()) {
throw new InvalidGmlFileException("Invalid GML File. First error: \n" + messages.get(0));
}
} catch (Exception e) {
throw new CityGmlParseException(e);
}
}
return decompressAndParseCityGmlEntry(entry, config, l, context);
}
public static CityDoctorModel decompressAndParseCityGmlEntry(CityGmlZipEntry entry, ParserConfiguration config, CityGMLContext context)
throws CityGmlParseException {
return decompressAndParseCityGmlEntry(entry, config, null, context);
}
public static CityDoctorModel decompressAndParseCityGmlEntry(CityGmlZipEntry entry, ParserConfiguration config, ProgressListener l, CityGMLContext context)
throws CityGmlParseException {
try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) {
BufferedInputStream bis = new BufferedInputStream(entryFile.getInputStream());
readEpsgCodeFromInputStream(bis, config);
CityGMLInputFactory in = context.createCityGMLInputFactory()
.withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false));
try (ObservedInputStream ois = new ObservedInputStream(bis, bis.available())) {
if (l != null) {
ois.addListener(l::updateProgress);
}
return readAndKeepFeatures(config, entry, in, ois, false);
}
} catch (CityGMLReadException | IOException e) {
throw new CityGmlParseException("Failed to read CityGML file", e);
} catch (Exception e) {
throw new CityGmlParseException(e);
}
}
private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, CityGmlZipEntry entry,
CityGMLInputFactory inputFactory, ObservedInputStream ois, boolean verbose) throws CityGMLReadException {
return readAndKeepModel(new Citygml3FeatureMapper(config, entry), inputFactory, ois, verbose);
}
private static List<String> validateStream(InputStream vis, CityGMLContext context) throws CityGmlParseException {
GMLValidationHandler handler = new GMLValidationHandler();
try {
BufferedInputStream bis = new BufferedInputStream(vis);
SchemaHandler schemaHandler = new ValidationSchemaHandler(context.getDefaultSchemaHandler());
readAdditionalSchemaDefinitions(context, bis, schemaHandler);
Source[] schemas = schemaHandler.getSchemas();
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
schemaFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
Schema schema = schemaFactory.newSchema(schemas);
Validator validator = schema.newValidator();
validator.setErrorHandler(handler);
validator.validate(new StreamSource(bis));
return handler.getMessages();
} catch (SchemaHandlerException | SAXException | IOException e) {
throw new CityGmlParseException("Failed to validate CityGML file", e);
}
}
private static void readAdditionalSchemaDefinitions(CityGMLContext context, BufferedInputStream bis, SchemaHandler schemaHandler)
throws CityGmlParseException {
bis.mark(Integer.MAX_VALUE);
try (XMLReader reader = XMLReaderFactory.newInstance(context.getXMLObjects())
.withSchemaHandler(schemaHandler)
.createReader(bis)) {
reader.nextTag();
bis.reset();
} catch (Exception e) {
throw new CityGmlParseException("Failed to read Schema from stream.", e);
}
}
private static void readEpsgCodeFromInputStream(BufferedInputStream bis, ParserConfiguration config) throws CityGmlParseException {
try {
// Mark start position of GML-"file"
bis.mark(Integer.MAX_VALUE);
// Buffer the first 10000 chars of the Stream, EPSG/envelope info should be found within that range
int peekingWidth = Math.min(10000, bis.available());
byte[] buf = new byte[peekingWidth];
bis.read(buf, 0, peekingWidth);
bis.reset();
parseEpsgCodeFromBuffer(buf, config);
} catch (ParserConfigurationException | SAXException | IOException e) {
throw new CityGmlParseException("Failed to read CityGML file", e);
}
}
public static void streamCityGml(CityGmlZipEntry entry, ParserConfiguration config, CityGmlConsumer cityObjectConsumer,
String outputFile) throws CityGmlParseException, IOException {
try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) {
parseEpsgCodeFromStream(entryFile.getInputStream(), config);
startReadingCityGmlZipEntry(entry, config, null, cityObjectConsumer, outputFile);
} catch (ParserConfigurationException | SAXException | IOException e) {
throw new CityGmlParseException("Failed to read CityGML stream", e);
}
}
......@@ -210,7 +334,7 @@ public class CityGmlParser {
public static void streamCityGml(Path file, ParserConfiguration config, ProgressListener l,
CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGmlParseException {
parseEpsgCodeFromFile(file, config);
startReadingCityGml(file, config, l, cityObjectConsumer, outputFile);
startReadingCityGmlFile(file, config, l, cityObjectConsumer, outputFile);
}
public static CityModel parseOnlyCityModel(File inputFile) throws CityGmlParseException {
......@@ -232,7 +356,7 @@ public class CityGmlParser {
throw new CityGmlParseException("Did not find any CityModel in CityGML file");
}
private static void startReadingCityGml(Path file, ParserConfiguration config, ProgressListener l,
private static void startReadingCityGmlFile(Path file, ParserConfiguration config, ProgressListener l,
CityGmlConsumer cityObjectConsumer, String outputFile) {
try (ObservedInputStream ois = new ObservedInputStream(file.toFile())) {
if (l != null) {
......@@ -247,12 +371,39 @@ public class CityGmlParser {
private static void readAndDiscardFeatures(Path file, ParserConfiguration config, ObservedInputStream ois,
CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException {
Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, file);
readAndDiscardModel(mapper, ois, cityObjectConsumer, outputFile);
}
private static void startReadingCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config, ProgressListener l,
CityGmlConsumer cityObjectConsumer, String outputFile) {
try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry);
ObservedInputStream ois = new ObservedInputStream(entryFile.getInputStream(), entry.getFileSize())) {
if (l != null) {
ois.addListener(l::updateProgress);
}
streamAndDiscardFeatures(entry, config, ois, cityObjectConsumer, outputFile);
} catch (IOException | CityGMLReadException e) {
logger.error(Localization.getText("CityGmlParser.errorReadingGmlFile"), e.getMessage());
logger.catching(Level.ERROR, e);
}
}
private static void streamAndDiscardFeatures(CityGmlZipEntry entry, ParserConfiguration config, ObservedInputStream ois,
CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException {
Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, entry);
readAndDiscardModel(mapper, ois, cityObjectConsumer, outputFile);
}
private static void readAndDiscardModel(Citygml3FeatureMapper mapper, ObservedInputStream ois,
CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException {
getContext();
CityGMLInputFactory inputFactory = context.createCityGMLInputFactory()
.withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false));
CityGMLChunkWriter writer = null;
try (CityGMLReader reader = inputFactory.createCityGMLReader(ois)) {
Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, file);
CityDoctorModel model = mapper.getModel();
boolean isInitialized = false;
while (reader.hasNext()) {
......@@ -329,18 +480,14 @@ public class CityGmlParser {
return writer;
}
/**
* Suppresses logger output of {@link #readAndKeepFeatures} for the next parse.
* Used to prevent logging spam while resolving implicit geometries.
*/
public static void gagLogger(boolean value){
gagged = value;
private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, Path file,
CityGMLInputFactory inputFactory, ObservedInputStream ois, boolean verbose) throws CityGMLReadException {
return readAndKeepModel(new Citygml3FeatureMapper(config, file), inputFactory, ois, verbose);
}
private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, Path file,
CityGMLInputFactory inputFactory, ObservedInputStream ois) throws CityGMLReadException {
private static CityDoctorModel readAndKeepModel(Citygml3FeatureMapper mapper, CityGMLInputFactory inputFactory,
ObservedInputStream ois, boolean verbose) throws CityGMLReadException {
try (CityGMLReader reader = inputFactory.createCityGMLReader(ois)) {
Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, file);
CityGMLVersion version = null;
// model is read in chunked mode
// object members are replaced by href in model
......@@ -374,12 +521,9 @@ public class CityGmlParser {
for (AbstractCityObject aco : acos) {
cModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco));
}
if (logger.isInfoEnabled() && !gagged) {
if (logger.isInfoEnabled() && verbose) {
logger.info(Localization.getText("CityGmlParser.parsedObjects"),
mapper.getModel().getNumberOfFeatures());
} else if (gagged){
// Remove gag
gagged = false;
}
mapper.setCityGMLVersion(version);
return mapper.getModel();
......@@ -404,16 +548,48 @@ public class CityGmlParser {
try {
parseCoordinateSystem(config, handler);
} catch (Exception e2) {
logger.debug("Exception while parsing for EPSG code", e2);
if (logger.isWarnEnabled()) {
logger.warn(Localization.getText("CityGmlParser.noEPSG"));
logEpsgParseError(e2);
}
} catch (Exception e) {
logEpsgParseError(e);
}
if (handler.getEpsg() == null && logger.isInfoEnabled()) {
logger.info(Localization.getText("CityGmlParser.missingEPSGCode"));
}
}
private static void parseEpsgCodeFromBuffer(byte[] buffer, ParserConfiguration config)
throws ParserConfigurationException, SAXException {
InputStream is = new ByteArrayInputStream(buffer);
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) {
logEpsgParseError(e2);
}
} catch (SAXParseException spe) {
// suppress XML document structure warning
if (!spe.getMessage().matches("XML document structures must start and end within the same entity.")) {
logEpsgParseError(spe);
}
} catch (Exception e) {
logEpsgParseError(e);
}
if (handler.getEpsg() == null && logger.isInfoEnabled()) {
logger.info(Localization.getText("CityGmlParser.missingEPSGCode"));
}
}
private static void logEpsgParseError(Exception e) {
logger.debug("Exception while parsing for EPSG code", e);
if (logger.isWarnEnabled()) {
logger.warn(Localization.getText("CityGmlParser.noEPSG"));
}
logger.warn(Localization.getText("CityGmlParser.failedEPSGParse"));
}
}
......@@ -425,6 +601,9 @@ public class CityGmlParser {
if (crs == null) {
// could not find a coordinate system for srsName
// assuming metric system
if (logger.isInfoEnabled()) {
logger.info(Localization.getText("CityGmlParser.missingEPSGCode"));
}
return;
}
if (crs.getProjection().getUnits() == Units.METRES) {
......@@ -481,12 +660,12 @@ public class CityGmlParser {
* string and detect the corresponding reference system. If it is found, it
* returns a proj4j.CoordinateReferenceSystem. It throws an
* IllegalArgumentException otherwise.
*
* <p>
* 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.
*
* <p>
* 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
*
......@@ -508,11 +687,12 @@ public class CityGmlParser {
Matcher mOGC = P_OGC.matcher(srsName);
if (mOGC.find()) {
return CRS_FACTORY.createFromName("EPSG:" + mOGC.group(1));
return getCrsFromOGCSrsCode(mOGC.group(1));
}
Matcher mOGC2 = P_OGC2.matcher(srsName);
if (mOGC2.find()) {
return CRS_FACTORY.createFromName("EPSG:" + mOGC2.group(1));
return getCrsFromOGCSrsCode(mOGC2.group(1));
}
Matcher mURN = P_URN.matcher(srsName);
// NOTE: Could use a HashMap if the switch/case becomes too long.
......@@ -532,6 +712,14 @@ public class CityGmlParser {
return null;
}
private static CoordinateReferenceSystem getCrsFromOGCSrsCode(String srsCode) {
String crsName = "EPSG:" + srsCode;
List<String> params = new ArrayList<>(Arrays.stream(csReader.getParameters(crsName)).toList());
params.removeIf(P_VUNITS.asPredicate());
params.removeIf(P_GEOIDGRID.asPredicate());
return CRS_FACTORY.createFromParameters(crsName, params.toArray(new String[0]));
}
private static List<String> validateFile(CityGMLContext context, GMLValidationHandler handler, Path file)
throws CityGmlParseException {
if (handler == null) {
......@@ -571,6 +759,9 @@ public class CityGmlParser {
drainCityObjectList(model.getLand(), cityObjectConsumer);
drainCityObjectList(model.getTransportation(), cityObjectConsumer);
drainCityObjectList(model.getWater(), cityObjectConsumer);
drainCityObjectList(model.getTunnels(), cityObjectConsumer);
drainCityObjectList(model.getCityFurniture(), cityObjectConsumer);
drainCityObjectList(model.getGenericCityObjects(), cityObjectConsumer);
}
private static void drainCityObjectList(List<? extends CityObject> objects, CityGmlConsumer cityObjectConsumer) {
......
package de.hft.stuttgart.citydoctor2.tesselation;
import java.util.ArrayList;
import java.util.List;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.math.ProjectionAxis;
import de.hft.stuttgart.citydoctor2.math.Triangle3d;
import earcut4j.Earcut;
/**
* Tesselator to create triangles out of polygons. Uses earcut4j implementation.
*
* @author Matthias Betz
*
*/
public class EarcutTesselator {
public static TesselatedPolygon tesselatePolygon(Polygon p) {
// sum number of vertices, don't use duplicate last point
// assume polygons are closed
int nrOfVertices = p.getExteriorRing().getVertices().size() - 1;
int[] holeStart = null;
if (!p.getInnerRings().isEmpty()) {
holeStart = new int[p.getInnerRings().size()];
List<LinearRing> innerRings = p.getInnerRings();
for (int i = 0; i < innerRings.size(); i++) {
LinearRing r = innerRings.get(i);
holeStart[i] = nrOfVertices;
nrOfVertices += r.getVertices().size() - 1;
}
}
// collect vertices
// find most dominant projection axis for 2d projection
ProjectionAxis axis = ProjectionAxis.of(p);
double[] vertices = new double[nrOfVertices * 2];
List<Vertex> vertexObjects = new ArrayList<>();
addVerticesToList(p.getExteriorRing(), vertexObjects);
for (LinearRing innerRing : p.getInnerRings()) {
addVerticesToList(innerRing, vertexObjects);
}
// write the vector data according to the projection axis into the array
int start = addRingToArray(p.getExteriorRing(), vertices, 0, axis);
for (LinearRing innerRing : p.getInnerRings()) {
start = addRingToArray(innerRing, vertices, start, axis);
}
// triangulation
List<Integer> indices = Earcut.earcut(vertices, holeStart, 2);
List<Triangle3d> triangles = new ArrayList<>();
if (indices.size() % 3 != 0) {
throw new IllegalStateException();
}
for (int i = 0; i < indices.size(); i = i + 3) {
Triangle3d t = new Triangle3d(vertexObjects.get(indices.get(i + 0)),
vertexObjects.get(indices.get(i + 1)),
vertexObjects.get(indices.get(i + 2)));
triangles.add(t);
}
return new TesselatedPolygon(triangles, p);
}
private static void addVerticesToList(LinearRing ring, List<Vertex> vertexObjects) {
List<Vertex> vertices = ring.getVertices();
for (int i = 0; i < vertices.size() - 1; i++) {
Vertex v = vertices.get(i);
vertexObjects.add(v);
}
}
private static int addRingToArray(LinearRing ring, double[] vertices, int start, ProjectionAxis axis) {
List<Vertex> ringVertices = ring.getVertices();
for (int i = 0; i < ringVertices.size() - 1; i++) {
Vertex v = ringVertices.get(i);
axis.writeCoordinatesOfVectorInArray(v, vertices, start);
start = start + 2;
}
return start;
}
private EarcutTesselator() {
}
}
Supports Markdown
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