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.checks.geometry;
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.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.Checkable;
import de.hft.stuttgart.citydoctor2.check.GeometrySelfIntersection;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError;
import de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.GeometryType;
import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection;
/**
* Check for self intersecting solids
*
* @author Matthias Betz
*
*/
public class SolidSelfIntCheck extends Check {
private static final List<CheckId> dependencies;
private static final List<Class<? extends Checkable>> applicableToClasses;
static {
ArrayList<CheckId> deps = new ArrayList<>();
dependencies = Collections.unmodifiableList(deps);
deps.add(CheckId.C_GE_R_TOO_FEW_POINTS);
deps.add(CheckId.C_GE_R_NOT_CLOSED);
deps.add(CheckId.C_GE_R_DUPLICATE_POINT);
deps.add(CheckId.C_GE_R_SELF_INTERSECTION);
deps.add(CheckId.C_GE_P_HOLE_OUTSIDE);
deps.add(CheckId.C_GE_P_INNER_RINGS_NESTED);
deps.add(CheckId.C_GE_P_INTERIOR_DISCONNECTED);
deps.add(CheckId.C_GE_P_INTERSECTING_RINGS);
deps.add(CheckId.C_GE_P_ORIENTATION_RINGS_SAME);
deps.add(CheckId.C_GE_P_NON_PLANAR);
deps.add(CheckId.C_GE_S_TOO_FEW_POLYGONS);
deps.add(CheckId.C_GE_S_MULTIPLE_CONNECTED_COMPONENTS);
deps.add(CheckId.C_GE_S_NON_MANIFOLD_EDGE);
deps.add(CheckId.C_GE_S_NON_MANIFOLD_VERTEX);
deps.add(CheckId.C_GE_S_POLYGON_WRONG_ORIENTATION);
ArrayList<Class<? extends Checkable>> classes = new ArrayList<>();
classes.add(Geometry.class);
applicableToClasses = Collections.unmodifiableList(classes);
}
public SolidSelfIntCheck() {
super(CheckId.C_GE_S_SELF_INTERSECTION);
}
@Override
public void check(Geometry g) {
if (g.getType() != GeometryType.SOLID && g.getType() != GeometryType.COMPOSITE_SURFACE) {
return;
}
CheckResult cr;
List<PolygonIntersection> intersections = SelfIntersectionUtil.doesSolidSelfIntersect2(g);
if (intersections.isEmpty()) {
cr = new CheckResult(this, ResultStatus.OK, null);
} else {
CheckError e = new SolidSelfIntError(g, intersections);
cr = new CheckResult(this, ResultStatus.ERROR, e);
}
g.addCheckResult(cr);
}
@SuppressWarnings("unused")
private CheckResult oldIntersectionAlgorithm(Geometry g) {
CheckResult cr;
GeometrySelfIntersection intersect = SelfIntersectionUtil.doesSolidSelfIntersect(g);
if (intersect != null) {
cr = new CheckResult(this, ResultStatus.ERROR, null);
} else {
cr = new CheckResult(this, ResultStatus.OK, null);
}
return cr;
}
@Override
public List<Class<? extends Checkable>> getApplicableToClasses() {
return applicableToClasses;
}
@Override
public List<CheckId> getDependencies() {
return dependencies;
}
@Override
public CheckType getType() {
return CheckType.GEOMETRY;
}
@Override
public Check createNewInstance() {
return new SolidSelfIntCheck();
}
}
/*-
* 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.checks.geometry;
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.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.Checkable;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.GeometryType;
/**
* This class checks minimum number of planes for a solid
*
* @author Matthias Betz 12bema1bif@hft-stuttgart.de
*/
public class TooFewPolygonsCheck extends Check {
private static final List<CheckId> dependencies;
private static final List<Class<? extends Checkable>> applicableToClasses;
static {
ArrayList<CheckId> deps = new ArrayList<>();
deps.add(CheckId.C_GE_R_TOO_FEW_POINTS);
deps.add(CheckId.C_GE_R_NOT_CLOSED);
deps.add(CheckId.C_GE_R_DUPLICATE_POINT);
deps.add(CheckId.C_GE_R_SELF_INTERSECTION);
deps.add(CheckId.C_GE_P_INTERIOR_DISCONNECTED);
deps.add(CheckId.C_GE_P_INTERSECTING_RINGS);
deps.add(CheckId.C_GE_P_NON_PLANAR);
dependencies = Collections.unmodifiableList(deps);
ArrayList<Class<? extends Checkable>> classes = new ArrayList<>();
classes.add(Geometry.class);
applicableToClasses = Collections.unmodifiableList(classes);
}
public TooFewPolygonsCheck() {
super(CheckId.C_GE_S_TOO_FEW_POLYGONS);
}
/**
* This method does the main check whether the solid have at least 4 faces or
* not.
*
* @param toBeCheckedGeometry
* The ViewableGeometry that has to be checked.
* @return Vector of CDErrors
*/
@Override
public void check(Geometry g) {
// only for solids
if (g.getType() != GeometryType.SOLID) {
return;
}
CheckResult cr;
if (g.getPolygons().size() < 4) {
CheckError err = new TooFewPolygonsError(g);
cr = new CheckResult(this, ResultStatus.ERROR, err);
} else {
cr = new CheckResult(this, ResultStatus.OK, null);
}
g.addCheckResult(cr);
}
@Override
public List<CheckId> getDependencies() {
return dependencies;
}
@Override
public List<Class<? extends Checkable>> getApplicableToClasses() {
return applicableToClasses;
}
@Override
public CheckType getType() {
return CheckType.GEOMETRY;
}
@Override
public Check createNewInstance() {
return new TooFewPolygonsCheck();
}
}
/*-
* 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.checks.semantics;
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.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.Checkable;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.checks.util.UnfragmentedCheck;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurfaceType;
/**
* Checks whether all polygons are in the same plane of a ground surface
*
* @author Matthias Betz
*
*/
public class GroundSurfaceUnfragmented extends Check {
private static final List<CheckId> dependencies;
private static final List<Class<? extends Checkable>> applicableToClasses;
static {
ArrayList<CheckId> deps = new ArrayList<>();
deps.add(CheckId.C_GE_R_TOO_FEW_POINTS);
dependencies = Collections.unmodifiableList(deps);
ArrayList<Class<? extends Checkable>> classes = new ArrayList<>();
classes.add(BoundarySurface.class);
applicableToClasses = Collections.unmodifiableList(classes);
}
public GroundSurfaceUnfragmented() {
super(CheckId.C_SEM_BS_GROUND_NOT_FRAGMENTED);
}
@Override
public void check(BoundarySurface bs) {
// only use ground surfaces
if (bs.getType() != BoundarySurfaceType.GROUND) {
return;
}
// only use lod1 and lod2 polygons
CheckResult cr = UnfragmentedCheck.checkForFragmentedBoundarySurfaces(this, bs, 0.1);
if (cr == null) {
// no error found
cr = new CheckResult(this, ResultStatus.OK, null);
}
bs.addCheckResult(cr);
}
@Override
public List<CheckId> getDependencies() {
return dependencies;
}
@Override
public List<Class<? extends Checkable>> getApplicableToClasses() {
return applicableToClasses;
}
@Override
public CheckType getType() {
return CheckType.SEMANTIC;
}
@Override
public Check createNewInstance() {
return new GroundSurfaceUnfragmented();
}
}
/*-
* 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.checks.semantics;
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.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.Checkable;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.NotCeilingError;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurfaceType;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
/**
* SEM_ISCEILING checks if the normal direction of a polygon labeled as
* OuterCeilingSurface points downwards.
*
* @author Matthias Betz - 12bema1bif@hft-stuttgart.de
*
*/
public class IsCeilingCheck extends Check {
private static final List<Class<? extends Checkable>> applicableToClasses;
private static final List<CheckId> dependencies;
static {
ArrayList<CheckId> deps = new ArrayList<>();
deps.add(CheckId.C_GE_R_TOO_FEW_POINTS);
deps.add(CheckId.C_GE_R_NOT_CLOSED);
deps.add(CheckId.C_GE_R_DUPLICATE_POINT);
deps.add(CheckId.C_GE_R_SELF_INTERSECTION);
deps.add(CheckId.C_GE_P_NON_PLANAR);
dependencies = Collections.unmodifiableList(deps);
ArrayList<Class<? extends Checkable>> classes = new ArrayList<>();
classes.add(BoundarySurface.class);
applicableToClasses = Collections.unmodifiableList(classes);
}
public IsCeilingCheck() {
super(CheckId.C_SEM_BS_NOT_CEILING);
}
@Override
public void check(BoundarySurface bs) {
// works only on outer ceiling and ceiling polygons
if (bs.getType() != BoundarySurfaceType.OUTER_CEILING && bs.getType() != BoundarySurfaceType.CEILING) {
return;
}
for (Geometry geom : bs.getGeometries()) {
for (Polygon p : geom.getPolygons()) {
Vector3d normal = p.calculateNormal();
if (normal.getZ() >= 0) {
NotCeilingError err = new NotCeilingError(bs, p);
CheckResult cr = new CheckResult(this, ResultStatus.ERROR, err);
bs.addCheckResult(cr);
return;
}
}
}
CheckResult cr = new CheckResult(this, ResultStatus.OK, null);
bs.addCheckResult(cr);
}
@Override
public List<Class<? extends Checkable>> getApplicableToClasses() {
return applicableToClasses;
}
@Override
public List<CheckId> getDependencies() {
return dependencies;
}
@Override
public CheckType getType() {
return CheckType.SEMANTIC;
}
@Override
public Check createNewInstance() {
return new IsCeilingCheck();
}
}
/*-
* 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.checks.semantics;
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.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.Checkable;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.NotFloorError;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurfaceType;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
/**
* SEM_ISCEILING checks if the normal direction of a polygon labeled as
* OuterCeilingSurface points downwards.
*
* @author Matthias Betz - 12bema1bif@hft-stuttgart.de
*
*/
public class IsFloorCheck extends Check {
private static final List<Class<? extends Checkable>> applicableToClasses;
private static final List<CheckId> dependencies;
static {
ArrayList<CheckId> deps = new ArrayList<>();
deps.add(CheckId.C_GE_R_TOO_FEW_POINTS);
deps.add(CheckId.C_GE_R_NOT_CLOSED);
deps.add(CheckId.C_GE_R_DUPLICATE_POINT);
deps.add(CheckId.C_GE_R_SELF_INTERSECTION);
deps.add(CheckId.C_GE_P_NON_PLANAR);
dependencies = Collections.unmodifiableList(deps);
ArrayList<Class<? extends Checkable>> classes = new ArrayList<>();
classes.add(BoundarySurface.class);
applicableToClasses = Collections.unmodifiableList(classes);
}
public IsFloorCheck() {
super(CheckId.C_SEM_BS_NOT_FLOOR);
}
@Override
public void check(BoundarySurface bs) {
// works only on outer floor polygons
if (bs.getType() != BoundarySurfaceType.OUTER_FLOOR && bs.getType() != BoundarySurfaceType.FLOOR) {
return;
}
for (Geometry geom : bs.getGeometries()) {
for (Polygon p : geom.getPolygons()) {
Vector3d normal = p.calculateNormal();
if (normal.getZ() <= 0) {
NotFloorError err = new NotFloorError(bs, p);
CheckResult cr = new CheckResult(this, ResultStatus.ERROR, err);
bs.addCheckResult(cr);
return;
}
}
}
CheckResult cr = new CheckResult(this, ResultStatus.OK, null);
bs.addCheckResult(cr);
}
@Override
public List<CheckId> getDependencies() {
return dependencies;
}
@Override
public List<Class<? extends Checkable>> getApplicableToClasses() {
return applicableToClasses;
}
@Override
public CheckType getType() {
return CheckType.SEMANTIC;
}
@Override
public Check createNewInstance() {
return new IsFloorCheck();
}
}
/*-
* 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.checks.semantics;
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.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.Checkable;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.NotGroundError;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurfaceType;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
/**
* checks if the normal direction of a polygon labeled as
* GroundSurface points downwards.
* @author dwagner
* @author Matthias Betz - 12bema1bif@hft-stuttgart.de
*
*/
public class IsGroundCheck extends Check {
private static final List<Class<? extends Checkable>> applicableToClasses;
private static final List<CheckId> dependencies;
static {
ArrayList<CheckId> deps = new ArrayList<>();
deps.add(CheckId.C_GE_R_TOO_FEW_POINTS);
deps.add(CheckId.C_GE_R_NOT_CLOSED);
deps.add(CheckId.C_GE_R_DUPLICATE_POINT);
deps.add(CheckId.C_GE_R_SELF_INTERSECTION);
deps.add(CheckId.C_GE_P_NON_PLANAR);
dependencies = Collections.unmodifiableList(deps);
ArrayList<Class<? extends Checkable>> classes = new ArrayList<>();
classes.add(BoundarySurface.class);
applicableToClasses = Collections.unmodifiableList(classes);
}
public IsGroundCheck() {
super(CheckId.C_SEM_BS_NOT_GROUND);
}
@Override
public void check(BoundarySurface bs) {
// works only on ground surfaces
if (bs.getType() != BoundarySurfaceType.GROUND) {
return;
}
for (Geometry geom : bs.getGeometries()) {
for (Polygon p : geom.getPolygons()) {
Vector3d normal = p.calculateNormal();
if (normal.getZ() >= 0) {
NotGroundError err = new NotGroundError(bs, p);
CheckResult cr = new CheckResult(this, ResultStatus.ERROR, err);
bs.addCheckResult(cr);
return;
}
}
}
CheckResult cr = new CheckResult(this, ResultStatus.OK, null);
bs.addCheckResult(cr);
}
@Override
public List<Class<? extends Checkable>> getApplicableToClasses() {
return applicableToClasses;
}
@Override
public List<CheckId> getDependencies() {
return dependencies;
}
@Override
public CheckType getType() {
return CheckType.SEMANTIC;
}
@Override
public Check createNewInstance() {
return new IsGroundCheck();
}
}
/*-
* 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.checks.semantics;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.Checkable;
import de.hft.stuttgart.citydoctor2.check.DefaultParameter;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.Unit;
import de.hft.stuttgart.citydoctor2.check.error.NotWallError;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurfaceType;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
/**
* checks if the normal direction of a polygon labeled as WallSurface are
* horizontal or not.
*
* @author dwagner
* @author Matthias Betz - 12bema1bif@hft-stuttgart.de
*
*/
public class IsWallCheck extends Check {
private static final String LOWER_ANGLE_NAME = "lowerAngle";
private static final String UPPER_ANGLE_NAME = "upperAngle";
private static final List<Class<? extends Checkable>> applicableToClasses;
private static final List<CheckId> dependencies;
private static final List<DefaultParameter> defaultParameters;
static {
ArrayList<CheckId> deps = new ArrayList<>();
deps.add(CheckId.C_GE_R_TOO_FEW_POINTS);
deps.add(CheckId.C_GE_R_NOT_CLOSED);
deps.add(CheckId.C_GE_R_DUPLICATE_POINT);
deps.add(CheckId.C_GE_R_SELF_INTERSECTION);
deps.add(CheckId.C_GE_P_NON_PLANAR);
dependencies = Collections.unmodifiableList(deps);
ArrayList<Class<? extends Checkable>> classes = new ArrayList<>();
classes.add(BoundarySurface.class);
applicableToClasses = Collections.unmodifiableList(classes);
ArrayList<DefaultParameter> defParameters = new ArrayList<>(3);
defParameters.add(new DefaultParameter(LOWER_ANGLE_NAME, "45", Unit.DEGREE));
defParameters.add(new DefaultParameter(UPPER_ANGLE_NAME, "135", Unit.DEGREE));
defaultParameters = Collections.unmodifiableList(defParameters);
}
private static final Vector3d Z_AXIS = new Vector3d(0, 0, 1);
// The SIG3d handbook proposes a +-45 degree to the horizontal plane. To
// minimize computation the upper and lower values are calculated only once
// and in advance. The algorithm is based on the angle calculation between
// the normal of the polygon and the positive z direction vector.
private double lowerAngleCos = Math.cos(45 * Math.PI / 180);
private double upperAngleCos = Math.cos(135 * Math.PI / 180);
public IsWallCheck() {
super(CheckId.C_SEM_BS_IS_WALL);
}
@Override
public void init(Map<String, String> params, ParserConfiguration config) {
String lowerAngleString = params.get(LOWER_ANGLE_NAME);
if (lowerAngleString != null) {
lowerAngleCos = Double.parseDouble(lowerAngleString);
lowerAngleCos = Math.cos(lowerAngleCos * Math.PI / 180);
}
String upperAngleString = params.get(UPPER_ANGLE_NAME);
if (upperAngleString != null) {
upperAngleCos = Double.parseDouble(upperAngleString);
upperAngleCos = Math.cos(upperAngleCos * Math.PI / 180);
}
}
@Override
public void check(BoundarySurface bs) {
// works only on wall surfaces
if (bs.getType() != BoundarySurfaceType.WALL) {
return;
}
for (Geometry geom : bs.getGeometries()) {
for (Polygon p : geom.getPolygons()) {
Vector3d normal = p.calculateNormal();
double dot = normal.dot(Z_AXIS);
// The angle should be between 45 and 135 degree, measured
// against the z-direction, hence the optimal angle would be 90 degree
// CAUTION: This might seems odd, but keep in mind, that the acos won't
// be calculated and we won't receive measures in radian or degree.
// The values for cosine are:
// cos [0°, 90°) > 0
// cos ( 90° ) = 0
// cos ( 90°, 180°] < 0
// Hence we need to check if the calculated angle is above the lower
// angle cosine or below the upper angle cosine
if (dot > lowerAngleCos || dot < upperAngleCos) {
NotWallError err = new NotWallError(bs, p, normal);
CheckResult cr = new CheckResult(this, ResultStatus.ERROR, err);
bs.addCheckResult(cr);
return;
}
}
}
CheckResult cr = new CheckResult(this, ResultStatus.OK, null);
bs.addCheckResult(cr);
}
@Override
public List<Class<? extends Checkable>> getApplicableToClasses() {
return applicableToClasses;
}
@Override
public List<CheckId> getDependencies() {
return dependencies;
}
@Override
public List<DefaultParameter> getDefaultParameter() {
return defaultParameters;
}
@Override
public CheckType getType() {
return CheckType.SEMANTIC;
}
@Override
public Check createNewInstance() {
return new IsWallCheck();
}
}
/*-
* 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.checks.semantics;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.Checkable;
import de.hft.stuttgart.citydoctor2.check.DefaultParameter;
import de.hft.stuttgart.citydoctor2.check.Unit;
import de.hft.stuttgart.citydoctor2.checks.util.UnfragmentedCheck;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurfaceType;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
/**
* Checks whether all polygons in the same plane of a roof surface
*
* @author Matthias Betz
*
*/
public class RoofSurfaceUnfragmentedCheck extends Check {
private static final List<CheckId> dependencies;
private static final List<Class<? extends Checkable>> applicableToClasses;
private static final List<DefaultParameter> defaultParameters;
private static final String MAX_ANGLE_DEVIATION = "maxAngleDeviation";
private double maxAngleDeviation = 0.1;
static {
ArrayList<DefaultParameter> defParameters = new ArrayList<>(3);
defParameters.add(new DefaultParameter(MAX_ANGLE_DEVIATION, "0.1", Unit.RADIAN));
defaultParameters = Collections.unmodifiableList(defParameters);
ArrayList<CheckId> deps = new ArrayList<>();
deps.add(CheckId.C_GE_R_TOO_FEW_POINTS);
deps.add(CheckId.C_GE_R_NOT_CLOSED);
deps.add(CheckId.C_GE_R_DUPLICATE_POINT);
deps.add(CheckId.C_GE_R_SELF_INTERSECTION);
deps.add(CheckId.C_GE_P_NON_PLANAR);
dependencies = Collections.unmodifiableList(deps);
ArrayList<Class<? extends Checkable>> classes = new ArrayList<>();
classes.add(BoundarySurface.class);
applicableToClasses = Collections.unmodifiableList(classes);
}
public RoofSurfaceUnfragmentedCheck() {
super(CheckId.C_SEM_BS_ROOF_NOT_FRAGMENTED);
}
@Override
public void init(Map<String, String> params, ParserConfiguration config) {
String maxAngleString = params.get(MAX_ANGLE_DEVIATION);
if (maxAngleString != null) {
maxAngleDeviation = Double.parseDouble(maxAngleString);
}
}
@Override
public void check(BoundarySurface bs) {
// only use roof surfaces
if (bs.getType() != BoundarySurfaceType.ROOF) {
return;
}
// only use lod1 and lod2 polygons
CheckResult cr = UnfragmentedCheck.checkForFragmentedBoundarySurfaces(this, bs, maxAngleDeviation);
bs.addCheckResult(cr);
}
@Override
public List<CheckId> getDependencies() {
return dependencies;
}
@Override
public List<Class<? extends Checkable>> getApplicableToClasses() {
return applicableToClasses;
}
@Override
public List<DefaultParameter> getDefaultParameter() {
return defaultParameters;
}
@Override
public CheckType getType() {
return CheckType.SEMANTIC;
}
@Override
public Check createNewInstance() {
return new RoofSurfaceUnfragmentedCheck();
}
}
/*-
* 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.checks.semantics;
import java.util.Collections;
import java.util.List;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckType;
import de.hft.stuttgart.citydoctor2.check.Checkable;
/**
* This is only a pseudo check, Schematron is handled differently, but to have a
* check responsible for errors this class is created.
*
* @author Matthias Betz
*
*/
public class SchematronCheck extends Check {
public SchematronCheck() {
super(CheckId.C_SEM_SCHEMATRON);
}
@Override
public List<Class<? extends Checkable>> getApplicableToClasses() {
return Collections.emptyList();
}
@Override
public CheckType getType() {
return CheckType.SEMANTIC;
}
@Override
public Check createNewInstance() {
return new SchematronCheck();
}
}
/*-
* 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.checks.util;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
/**
* Listener for when a feature has been checked
*
* @author Matthias Betz
*
*/
public interface FeatureCheckedListener {
public void featureChecked(CityObject co);
}
/*-
* 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.checks.util;
/**
*
* @author Matthias Betz
*
*/
public enum Orientation {
CLOCKWISE, COUNTER_CLOCKWISE
}
/*-
* 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.checks.util;
import de.hft.stuttgart.citydoctor2.math.Ring2d;
import de.hft.stuttgart.citydoctor2.math.Vector2d;
/**
* Utility class to calculate the orientation
*
* @author Matthias Betz
*
*/
public final class RingOrientationUtil {
private RingOrientationUtil() {
}
/**
* Calculates whether a point is left, right or on the line
*
* @param v1 first point of the line
* @param v2 second point of the line
* @param v3 checking point
* @return > 0: left of the line<br>
* = 0: on the line<br>
* < 0: right of the line
*/
@SuppressWarnings("unused")
private static double calcPointLineOrientation(Vector2d v1, Vector2d v2, Vector2d v3) {
return ((v2.getX() - v1.getX()) * (v3.getY() - v1.getY()) - (v3.getX() - v1.getX()) * (v2.getY() - v1.getY()));
}
public static Orientation calculateOrientation(Ring2d ring) {
double sum = 0;
for (int i = 0; i < ring.getVertices().size() - 1; i++) {
Vector2d v1 = ring.getVertices().get(i);
Vector2d v2 = ring.getVertices().get(i + 1);
sum = sum + (v2.getX() - v1.getX()) * (v2.getY() + v1.getY());
}
if (sum < 0) {
return Orientation.COUNTER_CLOCKWISE;
}
return Orientation.CLOCKWISE;
}
}
/*-
* 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.checks.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.CoordinateSequence;
import org.locationtech.jts.geom.GeometryCollection;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.MultiLineString;
import org.locationtech.jts.geom.MultiPoint;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.PrecisionModel;
import org.locationtech.jts.geom.impl.CoordinateArraySequence;
import org.locationtech.jts.operation.overlay.OverlayOp;
import org.locationtech.jts.operation.overlay.snap.SnapIfNeededOverlayOp;
import de.hft.stuttgart.citydoctor2.check.GeometrySelfIntersection;
import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing.LinearRingType;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.math.MovedPolygon;
import de.hft.stuttgart.citydoctor2.math.MovedRing;
import de.hft.stuttgart.citydoctor2.math.Plane;
import de.hft.stuttgart.citydoctor2.math.PlaneSegmentIntersection;
import de.hft.stuttgart.citydoctor2.math.PlaneSegmentIntersection.Type;
import de.hft.stuttgart.citydoctor2.math.Polygon2d;
import de.hft.stuttgart.citydoctor2.math.Ring2d;
import de.hft.stuttgart.citydoctor2.math.Segment3d;
import de.hft.stuttgart.citydoctor2.math.Triangle3d;
import de.hft.stuttgart.citydoctor2.math.Vector2d;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.tesselation.JoglTesselator;
import de.hft.stuttgart.citydoctor2.tesselation.TesselatedPolygon;
import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection;
import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection.IntersectionType;
/**
* Utility class for checking for intersections
*
* @author Matthias Betz
*
*/
public class SelfIntersectionUtil {
private static final String DIVISOR_IS_0 = "Divisor is 0";
private static final Logger logger = LogManager.getLogger(SelfIntersectionUtil.class);
private static GeometryFactory factory = new GeometryFactory(new PrecisionModel(PrecisionModel.FLOATING));
private SelfIntersectionUtil() {
}
public static GeometrySelfIntersection doesSolidSelfIntersect(Geometry g) {
return selfIntersectionJava(g);
}
public static List<PolygonIntersection> doesSolidSelfIntersect2(Geometry g) {
List<Polygon> polygons = g.getPolygons();
List<PolygonIntersection> intersections = new ArrayList<>();
for (int i = 0; i < polygons.size() - 1; i++) {
Polygon p1 = polygons.get(i);
for (int j = i + 1; j < polygons.size(); j++) {
Polygon p2 = polygons.get(j);
PolygonIntersection intersect = polygonIntersection(p1, p2, 0.1, 0.01);
if (intersect.getType() != IntersectionType.NONE) {
intersections.add(intersect);
}
}
}
return intersections;
}
/**
* Checks if two polygons are intersecting. Result may be nothing, a line or a
* polygon if they are planar and intersecting.
*
* @param p1 the first polygon
* @param p2 the second polygon
* @param planarEpsilon the deviation of the normal before they are
* considered planar. In degrees.
* @param intersectionEpsilon epsilon to have some lee-way before polygons are
* intersecting.
* @return the intersection result.
*/
public static PolygonIntersection polygonIntersection(Polygon p1, Polygon p2, double planarEpsilon,
double intersectionEpsilon) {
Vector3d movedBy = p1.getExteriorRing().getVertices().get(0);
MovedPolygon indPoly1 = MovedPolygon.ofPolygon(p1, movedBy);
MovedPolygon indPoly2 = MovedPolygon.ofPolygon(p2, movedBy);
Plane plane1 = Plane.of(indPoly1);
// check p2 points all above or under plane1
if (isPolygonOnOneSide(intersectionEpsilon, plane1, indPoly2.getExteriorRing())) {
return PolygonIntersection.none();
}
Plane plane2 = Plane.of(indPoly2);
// check if p1 points all above or under plane2
if (isPolygonOnOneSide(intersectionEpsilon, plane2, indPoly1.getExteriorRing())) {
return PolygonIntersection.none();
}
// are polygons planar
double dot = Math.abs(plane1.getNormal().dot(plane2.getNormal()));
double radians = Math.toRadians(planarEpsilon);
if (dot > 1 - radians && dot < 1 + radians) {
// polygons are planar and on the same plane more or less
return polygon2DIntersection(indPoly1, indPoly2, plane1);
}
// possible intersection, get all intersection points
List<Vector3d> intersectionPoints = new ArrayList<>();
getIntersectionPoints(indPoly1, indPoly2, intersectionEpsilon, plane2, intersectionPoints);
getIntersectionPoints(indPoly2, indPoly1, intersectionEpsilon, plane1, intersectionPoints);
if (intersectionPoints.isEmpty()) {
// no points found: no intersection
return PolygonIntersection.none();
}
int size = intersectionPoints.size();
if (size == 1) {
List<Segment3d> lines = new ArrayList<>();
Vector3d v = intersectionPoints.get(0);
Segment3d seg = new Segment3d(v, v);
lines.add(seg);
return PolygonIntersection.lines(lines, p1, p2);
}
if (size % 2 != 0) {
throw new IllegalStateException(
"Polygon intersection has uneven number of intersection points: " + intersectionPoints);
}
List<Segment3d> lines = new ArrayList<>();
for (int i = 0; i < size / 2; i++) {
createLine(intersectionPoints, lines);
}
return PolygonIntersection.lines(lines, p1, p2);
}
private static PolygonIntersection polygon2DIntersection(MovedPolygon p1, MovedPolygon p2, Plane plane1) {
int[] projectionAxis = Polygon2d.getProjectionAxis(plane1.getNormal());
Polygon2d projectedP1 = Polygon2d.withProjection(p1, projectionAxis);
Polygon2d projectedP2 = Polygon2d.withProjection(p2, projectionAxis);
org.locationtech.jts.geom.Polygon jtsP1 = createJtsPolygon(projectedP1);
org.locationtech.jts.geom.Polygon jtsP2 = createJtsPolygon(projectedP2);
// use JTS to get intersection
// NOTE: OverlayOp.overlayOp sometimes throws "side location conflict"
// TopologyException.
// SnapIfNeededOverlayOp should be more robust.
org.locationtech.jts.geom.Geometry intersection = SnapIfNeededOverlayOp.overlayOp(jtsP1, jtsP2,
OverlayOp.INTERSECTION);
if (intersection instanceof LineString || intersection instanceof MultiPoint
|| intersection instanceof MultiLineString || intersection instanceof Point || intersection.isEmpty()) {
// intersection is only an edge, not a polygon
// edge intersections are allowed
return PolygonIntersection.none();
} else if (intersection instanceof GeometryCollection) {
GeometryCollection col = (GeometryCollection) intersection;
for (int i = 0; i < col.getNumGeometries(); i++) {
org.locationtech.jts.geom.Geometry interGeom = col.getGeometryN(i);
if (interGeom instanceof org.locationtech.jts.geom.Polygon) {
org.locationtech.jts.geom.Polygon intPoly = (org.locationtech.jts.geom.Polygon) interGeom;
ConcretePolygon poly = convertToPolygon(plane1, projectionAxis, intPoly);
return PolygonIntersection.polygon(poly, p1.getOriginal(), p2.getOriginal());
}
}
// no polygon in collection, so no intersection
return PolygonIntersection.none();
}
if (intersection instanceof org.locationtech.jts.geom.Polygon) {
org.locationtech.jts.geom.Polygon intPoly = (org.locationtech.jts.geom.Polygon) intersection;
ConcretePolygon poly = convertToPolygon(plane1, projectionAxis, intPoly);
return PolygonIntersection.polygon(poly, p1.getOriginal(), p2.getOriginal());
} else {
throw new IllegalStateException("Unknown intersection class: " + intersection);
}
}
private static ConcretePolygon convertToPolygon(Plane plane1, int[] projectionAxis,
org.locationtech.jts.geom.Polygon intPoly) {
List<Vertex> extRing = convertTo3dRing(plane1, intPoly.getExteriorRing(), projectionAxis);
ConcretePolygon poly = new ConcretePolygon();
LinearRing ext = new LinearRing(LinearRingType.EXTERIOR);
poly.setExteriorRing(ext);
ext.addAllVertices(extRing);
for (int i = 0; i < intPoly.getNumInteriorRing(); i++) {
List<Vertex> intRing = convertTo3dRing(plane1, intPoly.getInteriorRingN(i), projectionAxis);
LinearRing inner = new LinearRing(LinearRingType.INTERIOR);
poly.addInteriorRing(inner);
inner.addAllVertices(intRing);
}
return poly;
}
private static List<Vertex> convertTo3dRing(Plane plane, LineString lineString, int[] axis) {
List<Vertex> ring = new ArrayList<>();
for (Coordinate coord : lineString.getCoordinates()) {
Vertex v = projectPointToPlane(plane, axis, coord.getX(), coord.getY());
ring.add(v);
}
return ring;
}
private static Vertex projectPointToPlane(Plane plane, int[] axis, double oldX, double oldY) {
Vector3d normal = plane.getNormal();
double a = normal.getX();
double b = normal.getY();
double c = normal.getZ();
double d = plane.getD();
if (axis[0] == 0 && axis[1] == 1) {
double x = oldX;
double y = oldY;
if (c == 0) {
throw new IllegalStateException(DIVISOR_IS_0);
}
double z = (d - a * x - b * y) / c;
return new Vertex(x, y, z);
} else if (axis[0] == 0 && axis[1] == 2) {
double x = oldX;
double z = oldY;
if (b == 0) {
throw new IllegalStateException(DIVISOR_IS_0);
}
double y = (d - a * x - c * z) / b;
return new Vertex(x, y, z);
} else if (axis[0] == 1 && axis[1] == 2) {
double y = oldX;
double z = oldY;
if (a == 0) {
throw new IllegalStateException(DIVISOR_IS_0);
}
double x = (d - b * y - c * z) / a;
return new Vertex(x, y, z);
} else {
throw new IllegalStateException("Unkown axis: " + Arrays.toString(axis));
}
}
private static org.locationtech.jts.geom.Polygon createJtsPolygon(Polygon2d poly) {
org.locationtech.jts.geom.LinearRing shell = createJtsRing(poly.getExterior());
org.locationtech.jts.geom.LinearRing[] holes = null;
List<Ring2d> interiorRings = poly.getInteriorRings();
if (!interiorRings.isEmpty()) {
holes = new org.locationtech.jts.geom.LinearRing[interiorRings.size()];
for (int i = 0; i < interiorRings.size(); i++) {
holes[i] = createJtsRing(interiorRings.get(i));
}
}
return new org.locationtech.jts.geom.Polygon(shell, holes, factory);
}
private static org.locationtech.jts.geom.LinearRing createJtsRing(Ring2d ring) {
Coordinate[] coords = new Coordinate[ring.getVertices().size()];
for (int i = 0; i < ring.getVertices().size(); i++) {
Vector2d v = ring.getVertices().get(i);
coords[i] = new Coordinate(v.getX(), v.getY());
}
CoordinateSequence points = new CoordinateArraySequence(coords);
return new org.locationtech.jts.geom.LinearRing(points, factory);
}
private static void createLine(List<Vector3d> intersectionPoints, List<Segment3d> lines) {
Vector3d startPoint = getStartPoint(intersectionPoints);
intersectionPoints.remove(startPoint);
Vector3d endPoint = getNearestPoint(intersectionPoints, startPoint);
intersectionPoints.remove(endPoint);
Segment3d line = new Segment3d(startPoint, endPoint);
lines.add(line);
}
private static Vector3d getNearestPoint(List<Vector3d> intersectionPoints, Vector3d startPoint) {
if (intersectionPoints.size() == 1) {
return intersectionPoints.get(0);
}
Vector3d nearestPoint = intersectionPoints.get(0);
double distance = startPoint.getDistanceSquare(nearestPoint);
for (int i = 1; i < intersectionPoints.size(); i++) {
Vector3d checkPoint = intersectionPoints.get(i);
double checkDistance = checkPoint.getDistanceSquare(startPoint);
if (checkDistance < distance) {
nearestPoint = checkPoint;
distance = checkDistance;
}
}
return nearestPoint;
}
private static Vector3d getStartPoint(List<Vector3d> intersectionPoints) {
Vector3d point = intersectionPoints.get(0);
for (int i = 1; i < intersectionPoints.size(); i++) {
Vector3d checkPoint = intersectionPoints.get(i);
if (checkPoint.getX() < point.getX()
|| (checkPoint.getX() == point.getX() && (checkPoint.getY() < point.getY()
|| (checkPoint.getY() == point.getY() && checkPoint.getZ() < point.getZ())))) {
point = checkPoint;
}
}
return point;
}
private static void getIntersectionPoints(MovedPolygon p1, MovedPolygon p2, double intersectionEpsilon, Plane plane,
List<Vector3d> intersectionPoints) {
List<Segment3d> segments = getSegments(p1);
for (Segment3d segment : segments) {
PlaneSegmentIntersection intersection = plane.intersects(segment, intersectionEpsilon);
if (intersection.getType() == Type.INTERSECTION_POINT
&& isPointInsidePolygon(p2, intersection.getPoint())) {
intersectionPoints.add(intersection.getPoint());
}
}
}
private static List<Segment3d> getSegments(MovedPolygon p1) {
List<Segment3d> segments = new ArrayList<>();
getSegments(p1.getExteriorRing(), segments);
for (MovedRing inner : p1.getInnerRings()) {
getSegments(inner, segments);
}
return segments;
}
private static void getSegments(MovedRing movedRing, List<Segment3d> segments) {
List<Vector3d> vertices = movedRing.getVertices();
for (int i = 0; i < vertices.size() - 1; i++) {
Segment3d seg = new Segment3d(vertices.get(i), vertices.get(i + 1));
segments.add(seg);
}
}
private static boolean isPointInsidePolygon(MovedPolygon p, Vector3d v) {
if (p.getExteriorRing().isPointInside(v)) {
for (MovedRing r : p.getInnerRings()) {
if (r.isPointInside(v)) {
return false;
}
}
return true;
}
return false;
}
private static boolean isPolygonOnOneSide(double intersectionEpsilon, Plane plane, MovedRing ring) {
double sign = 0;
for (Vector3d v : ring.getVertices()) {
double signedDistance = plane.getSignedDistance(v);
if (signedDistance > -intersectionEpsilon && signedDistance < intersectionEpsilon) {
// point is slightly off the plane, does not count as possible intersection yet
signedDistance = 0;
}
if ((signedDistance < 0 && sign > 0) || (signedDistance > 0 && sign < 0)) {
// possible intersection detected
return false;
}
if (signedDistance != 0) {
sign = Math.signum(signedDistance);
}
}
// all points are on the same plane, therefore possible intersection
return sign != 0;
}
private static GeometrySelfIntersection selfIntersectionJava(Geometry g) {
List<TesselatedPolygon> tessPolys = new ArrayList<>();
for (Polygon p : g.getPolygons()) {
tessPolys.add(JoglTesselator.tesselatePolygon(p));
}
for (int i = 0; i < tessPolys.size() - 1; i++) {
TesselatedPolygon p1 = tessPolys.get(i);
for (int j = i + 1; j < tessPolys.size(); j++) {
TesselatedPolygon p2 = tessPolys.get(j);
GeometrySelfIntersection intersection = doPolygonsIntersect(p1, p2);
if (intersection != null) {
return intersection;
}
}
}
return null;
}
private static GeometrySelfIntersection doPolygonsIntersect(TesselatedPolygon p1, TesselatedPolygon p2) {
for (int p1Index = 0; p1Index < p1.getTriangles().size(); p1Index++) {
for (int p2Index = 0; p2Index < p2.getTriangles().size(); p2Index++) {
Triangle3d t1 = p1.getTriangles().get(p1Index);
Triangle3d t2 = p2.getTriangles().get(p2Index);
if (t1.doesIntersect(t2)) {
logger.trace("{} intersects {}", t1, t2);
logger.trace("{} intersects {}", t1.getPartOf().getOriginal().getGmlId(),
t2.getPartOf().getOriginal().getGmlId());
return new GeometrySelfIntersection(t1.getPartOf().getOriginal(), t2.getPartOf().getOriginal(), t1,
t2);
}
}
}
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.checks.util;
import java.util.ArrayList;
import java.util.List;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.SurfaceUnfragmentedError;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
/**
* Utility class for checking whether a boundary surface has different normals
* in their polygons.
*
* @author Matthias Betz
*
*/
public class UnfragmentedCheck {
private UnfragmentedCheck() {
}
/**
* This checks each geometry in a boundary surface whether the normals deviate
* too much from the average of all normals in the same geometry.
*
* @param c the check from which this method is called, needed to construct
* the check result
* @param bs the boundary surface containing the geometries.
* @param delta the maximum angle which the normals may deviate. In radians.
* @return the check result with either the result ok, or error
*/
public static CheckResult checkForFragmentedBoundarySurfaces(Check c, BoundarySurface bs, double delta) {
for (Geometry geom : bs.getGeometries()) {
List<Vector3d> normals = new ArrayList<>();
for (Polygon p : geom.getPolygons()) {
normals.add(p.calculateNormal());
}
double x = 0;
double y = 0;
double z = 0;
for (Vector3d n : normals) {
x += n.getX();
y += n.getY();
z += n.getZ();
}
x = x / normals.size();
y = y / normals.size();
z = z / normals.size();
Vector3d averageNormal = new Vector3d(x, y, z);
for (Vector3d n : normals) {
double dot = averageNormal.dot(n);
double acos = Math.acos(dot);
if (acos > delta) {
SurfaceUnfragmentedError err = new SurfaceUnfragmentedError(bs, acos);
return new CheckResult(c, ResultStatus.ERROR, err);
}
}
}
return new CheckResult(c, ResultStatus.OK, 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.error;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* CLI utility class for handling termination reasons
*
* @author Matthias Betz
*
*/
public class ErrorHandler {
private static final Logger LOGGER = LogManager.getLogger(ErrorHandler.class);
private ErrorHandler() {
// only static usage
}
private static final String TERMINATION_STRING = "The application has"
+ " encountered a fatal error and is now terminating. Reason:\n";
private static final String ENCOUNTERED_ERROR = "Encountered error:\n";
/**
* Terminates the application with the given message. The message is printed to
* the error output stream. A stack trace is printed from the given error.
*
* @param msg the termination message
* @param error an error object, can be null.
*/
public static void terminateOnError(String msg, Throwable error) {
System.err.println(TERMINATION_STRING);
System.err.println(msg);
if (error != null) {
System.err.println(ENCOUNTERED_ERROR);
LOGGER.error(msg, error);
}
System.exit(1);
}
/**
* Terminates the application with the given message. The message is printed to
* the error output stream.
*
* @param msg the termination message
*/
public static void terminateOnError(String msg) {
terminateOnError(msg, null);
}
/**
* Called when an illegal parameter has been used or wrong usage of a parameter.
* Prints the message to the error stream, prints the help menu to the output
* stream and terminates.
*
* @param msg the message to be printed to the error stream
*/
public static void printHelpAndTerminate(String msg) {
System.err.println(msg);
printHelp();
System.exit(1);
}
/**
* Prints the help to the output stream.
*/
public static void printHelp() {
System.out.println("\nUsage: ");
System.out.println("Mandatory parameters:");
System.out.println("-config <filename>");
System.out.println("\t Contains all configuration needed for checking");
}
}
/*-
* 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.parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import de.hft.stuttgart.citydoctor2.error.ErrorHandler;
/**
* Class for parsing input arguments
*
* @author Matthias Betz
*
*/
public class ArgumentParser {
public static final String CONFIG = "config";
private final HashMap<String, List<String>> arguments;
public ArgumentParser(String[] args) {
arguments = parseArguments(args);
}
public List<String> getValues(String option) {
return arguments.get(option.toLowerCase());
}
public boolean containsOption(String option) {
return arguments.containsKey(option);
}
private HashMap<String, List<String>> parseArguments(String[] args) {
HashMap<String, List<String>> params = new HashMap<>();
List<String> options = null;
for (int i = 0; i < args.length; i++) {
final String a = args[i];
if (a.charAt(0) == '-') {
if (a.length() < 2) {
ErrorHandler.printHelpAndTerminate("Error while parsing argument " + a);
}
String flag = a.substring(1).toLowerCase();
options = new ArrayList<>();
params.put(flag, options);
} else if (options != null) {
options.add(a);
} else {
ErrorHandler.printHelpAndTerminate("Illegal parameter usage at " + a);
}
}
return params;
}
}
/*-
* 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.reporting;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Class for storing the amount of errors per error type
*
* @author Matthias Betz
*
*/
public class ErrorStatisticsCollector {
private Map<String, AtomicInteger> errorCounts = new HashMap<>();
public void addError(String errorName) {
AtomicInteger numErrors = errorCounts.get(errorName);
if (numErrors == null) {
errorCounts.put(errorName, new AtomicInteger(1));
} else {
numErrors.incrementAndGet();
}
}
public Map<String, AtomicInteger> getErrorCounts() {
return errorCounts;
}
public void add(ErrorStatisticsCollector otherStats) {
for (Entry<String, AtomicInteger> errorCount : otherStats.getErrorCounts().entrySet()) {
AtomicInteger oldValue = errorCounts.get(errorCount.getKey());
if (oldValue == null) {
errorCounts.put(errorCount.getKey(), new AtomicInteger(errorCount.getValue().get()));
} else {
oldValue.addAndGet(errorCount.getValue().get());
}
}
}
}
/*-
* 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.reporting;
import java.io.OutputStream;
import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration;
import de.hft.stuttgart.citydoctor2.checkresult.utility.CheckReportWriteException;
import de.hft.stuttgart.citydoctor2.checks.Checks;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
/**
* Interface to write reports
*
* @author Matthias Betz
*
*/
public interface Reporter {
public void writeReport(Checks checks, OutputStream outFile, CityDoctorModel model, ValidationConfiguration config)
throws CheckReportWriteException;
}
/*-
* 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.reporting;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.checkresult.utility.CheckReportWriteException;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
/**
* Interface to write reports from streams of features
*
* @author Matthias Betz
*
*/
public interface StreamReporter {
public void report(CityObject co);
public void finishReport() throws CheckReportWriteException;
public void reportGlobalError(CheckError err);
/**
* Add an error to a feature. The GML ID can be one of Building, BuildingPart,
* Vegetation, Transportation, Land, Water or Bridge. No Polygon or Ring IDs are
* valid. This is used for Schematron errors, as the Schematron validation
* happens after all internal checks are done.
*
* @param gmlId the GML ID where the error belongs to
* @param err the error object.
*/
public void addError(String gmlId, CheckError err);
}
/*-
* 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.reporting;
import java.util.Collection;
import org.osgeo.proj4j.BasicCoordinateTransform;
import org.osgeo.proj4j.ProjCoordinate;
import de.hft.stuttgart.citydoctor2.check.ErrorReport;
import de.hft.stuttgart.citydoctor2.checkresult.EdgeReport;
import de.hft.stuttgart.citydoctor2.checkresult.EdgesReport;
import de.hft.stuttgart.citydoctor2.checkresult.ErrorDetails;
import de.hft.stuttgart.citydoctor2.checkresult.GeometryReport;
import de.hft.stuttgart.citydoctor2.checkresult.GeometryReportLod;
import de.hft.stuttgart.citydoctor2.checkresult.GeometryReportType;
import de.hft.stuttgart.citydoctor2.checkresult.ParameterReport;
import de.hft.stuttgart.citydoctor2.checkresult.ParentReport;
import de.hft.stuttgart.citydoctor2.checkresult.PolygonReport;
import de.hft.stuttgart.citydoctor2.checkresult.PolygonsReport;
import de.hft.stuttgart.citydoctor2.checkresult.RingReport;
import de.hft.stuttgart.citydoctor2.checkresult.RingType;
import de.hft.stuttgart.citydoctor2.checkresult.SurfaceReport;
import de.hft.stuttgart.citydoctor2.checkresult.VertexReport;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.datastructure.Edge;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing.LinearRingType;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.math.Vector3d;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
/**
* Error handler to create detailed report on errors
*
* @author Matthias Betz
*
*/
public class XmlStreamErrorHandler implements ErrorReport {
private ErrorDetails details;
private BasicCoordinateTransform originalTransform;
public XmlStreamErrorHandler(ParserConfiguration config) {
originalTransform = config.getOriginalTransform();
details = new ErrorDetails();
}
@Override
public void add(Polygon p) {
PolygonReport report = createPolygonReport(p);
details.getPolygon().add(report);
}
private PolygonReport createPolygonReport(Polygon p) {
PolygonReport report = new PolygonReport();
if (!p.getGmlId().isGenerated()) {
report.setGmlId(p.getGmlId().getGmlString());
}
LinearRing exteriorRing = p.getExteriorRing();
RingReport extReport = createRingReport(exteriorRing);
report.setExteriorRing(extReport);
if (!p.getInnerRings().isEmpty()) {
for (LinearRing lr : p.getInnerRings()) {
RingReport rep = createRingReport(lr);
report.getInteriorRing().add(rep);
}
}
return report;
}
private RingReport createRingReport(LinearRing lr) {
RingReport ringReport = new RingReport();
if (!lr.getGmlId().isGenerated()) {
ringReport.setGmlId(lr.getGmlId().getGmlString());
}
if (lr.getType() == LinearRingType.EXTERIOR) {
ringReport.setType(RingType.EXTERIOR);
} else if (lr.getType() == LinearRingType.INTERIOR) {
ringReport.setType(RingType.INTERIOR);
} else {
throw new IllegalStateException("Unknown ring type found: " + lr.getType());
}
for (Vertex v : lr.getVertices()) {
VertexReport vReport = createVertexReport(v);
ringReport.getVertices().add(vReport);
}
return ringReport;
}
private VertexReport createVertexReport(Vector3d v) {
VertexReport vReport = new VertexReport();
double x = v.getX();
double y = v.getY();
double z = v.getZ();
if (originalTransform != null) {
ProjCoordinate p1 = new ProjCoordinate();
ProjCoordinate p2 = new ProjCoordinate();
p1.x = v.getX();
p1.y = v.getY();
originalTransform.transform(p1, p2);
x = p2.x;
y = p2.y;
}
vReport.setX(x);
vReport.setY(y);
vReport.setZ(z);
return vReport;
}
private void addRing(LinearRing lr) {
RingReport report = createRingReport(lr);
details.getRing().add(report);
}
private void addVertex(Vector3d v) {
addVertex(v, null);
}
private void addVertex(Vector3d v, String name) {
VertexReport rep = createVertexReport(v);
if (name != null) {
rep.setName(name);
}
details.getVertex().add(rep);
}
@Override
public void add(LinearRing lr) {
addRing(lr);
}
@Override
public void add(Geometry geom) {
addGeometry(geom);
}
private void addGeometry(Geometry geom) {
GeometryReport rep = new GeometryReport();
if (!geom.getGmlId().isGenerated()) {
rep.setGmlId(geom.getGmlId().getGmlString());
}
rep.setType(GeometryReportType.valueOf(geom.getType().toString()));
rep.setLod(GeometryReportLod.valueOf(geom.getLod().toString()));
ParentReport parent = new ParentReport();
parent.setGmlId(geom.getParent().getGmlId().getGmlString());
parent.setType(geom.getParent().getFeatureType().toString());
rep.setParent(parent);
details.getGeometry().add(rep);
}
@Override
public void add(Vertex v) {
addVertex(v);
}
@Override
public void add(String name, Vertex v) {
addVertex(v, name);
}
@Override
public void add(Edge e) {
EdgeReport rep = createEdgeReport(e);
details.getEdge().add(rep);
}
private EdgeReport createEdgeReport(Edge e) {
return createEdgeReport(e, null);
}
private EdgeReport createEdgeReport(Edge e, String name) {
EdgeReport rep = new EdgeReport();
if (name != null) {
rep.setName(name);
}
VertexReport from = createVertexReport(e.getFrom());
VertexReport to = createVertexReport(e.getTo());
rep.setFrom(from);
rep.setTo(to);
return rep;
}
@Override
public void add(String name, Edge e) {
EdgeReport rep = createEdgeReport(e, name);
details.getEdge().add(rep);
}
@Override
public void add(String name, int value) {
addParameter(name, "" + value);
}
private void addParameter(String name, String value) {
ParameterReport rep = new ParameterReport();
rep.setName(name);
rep.setValue(value);
details.getParameter().add(rep);
}
@Override
public void add(String name, double value) {
addParameter(name, "" + value);
}
@Override
public void add(String name, String value) {
addParameter(name, value);
}
@Override
public void add(String name, Vector3d point) {
addVertex(point, name);
}
@Override
public void addPolygons(String name, Collection<Polygon> polygons) {
PolygonsReport rep = new PolygonsReport();
if (name != null) {
rep.setName(name);
}
for (Polygon p : polygons) {
PolygonReport polyRep;
if (p.getGmlId().isGenerated()) {
polyRep = createPolygonReport(p);
} else {
polyRep = new PolygonReport();
polyRep.setGmlId(p.getGmlId().getGmlString());
}
rep.getPolygons().add(polyRep);
}
details.getPolygons().add(rep);
}
@Override
public void addEdges(String name, Collection<Edge> edges) {
EdgesReport rep = new EdgesReport();
if (name != null) {
rep.setName(name);
}
for (Edge e : edges) {
EdgeReport eRep = createEdgeReport(e);
rep.getEdges().add(eRep);
}
details.getEdges().add(rep);
}
@Override
public void add(BoundarySurface bs) {
SurfaceReport rep = new SurfaceReport();
rep.setGmlId(bs.getGmlId().getGmlString());
details.getSurface().add(rep);
}
public ErrorDetails getDetails() {
return details;
}
}
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