/*- * 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 . */ package de.hft.stuttgart.citydoctor2.datastructure; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; import java.util.ArrayList; import java.util.EnumMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Stream; import org.citygml4j.core.model.CityGMLVersion; import org.citygml4j.core.model.core.CityModel; import de.hft.stuttgart.citydoctor2.check.AbstractCheck; import de.hft.stuttgart.citydoctor2.check.Check; import de.hft.stuttgart.citydoctor2.check.CheckError; import de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.writer.CityGMLWriterUtils; import de.hft.stuttgart.quality.model.types.ValidationPlan; /** * The complete CityGML model containing all features that are used in * CityDoctor * * @author Matthias Betz * */ public class CityDoctorModel { private static final String COULD_NOT_FIND_FEATURE = "Could not find feature: "; private List buildings; private List vegetation; private List bridges; private List land; private List roads; private List water; private CityModel cModel; private ParserConfiguration config; private String fileName; private File file; private List globalErrors; private boolean isValidated = false; private ValidationPlan plan; private CityGMLVersion cityGMLVersion; public CityDoctorModel(ParserConfiguration config, File file) { if (config == null) { throw new IllegalArgumentException("Parser configuration may not be null"); } this.fileName = file.getName(); this.config = config; this.file = file; buildings = new ArrayList<>(); vegetation = new ArrayList<>(); bridges = new ArrayList<>(); land = new ArrayList<>(); roads = new ArrayList<>(); water = new ArrayList<>(); globalErrors = new ArrayList<>(); } public boolean isValidated() { return isValidated; } public void setValidated(ValidationPlan plan) { this.plan = plan; this.isValidated = true; } public ValidationPlan getValidationPlan() { return plan; } public void addGlobalError(CheckError err) { globalErrors.add(err); } public void addGlobalErrors(List generalErrors) { globalErrors.addAll(generalErrors); } public List getGlobalErrors() { return globalErrors; } public File getFile() { return file; } public Stream createFeatureStream() { return Stream.of(buildings.stream(), vegetation.stream(), bridges.stream(), land.stream(), roads.stream(), water.stream()).flatMap(co -> co); } public void saveAs(String file) throws CityDoctorWriteException { if (file.endsWith(".off")) { exportAsOff(file); } else { exportAsGML(file); } } private void exportAsGML(String file) throws CityDoctorWriteException { CityGMLWriterUtils.writeCityModel(file, this); } private void exportAsOff(String file) { file = file.substring(0, file.lastIndexOf('.')); File folder = new File(file); folder.mkdirs(); createFeatureStream().forEach(co -> { Map> polygonMap = new EnumMap<>(Lod.class); Check c = new AbstractCheck() { @Override public void check(Geometry geom) { polygonMap.compute(geom.getLod(), (lod, polys) -> { if (polys == null) { polys = new HashSet<>(); } for (Polygon poly : geom.getPolygons()) { polys.add(poly.getOriginal()); } return polys; }); } }; co.accept(c); Set highestLod = polygonMap.get(Lod.LOD2); if (highestLod == null) { highestLod = polygonMap.get(Lod.LOD1); } if (highestLod == null) { return; } Map idMap = new LinkedHashMap<>(); int counter = 0; for (Polygon poly : highestLod) { counter = addRing(poly.getExteriorRing(), idMap, counter); } try (BufferedWriter writer = Files.newBufferedWriter(folder.toPath().resolve(co.getGmlId() + ".off"))) { writer.write("OFF\n"); writer.write(Integer.toString(idMap.size())); writer.write(' '); writer.write(Integer.toString(highestLod.size())); writer.write(" 0\n"); for (Vertex v : idMap.keySet()) { writer.write(Double.toString(v.getX())); writer.write(' '); writer.write(Double.toString(v.getY())); writer.write(' '); writer.write(Double.toString(v.getZ())); writer.write('\n'); } for (Polygon poly : highestLod) { writer.write(Integer.toString(poly.getExteriorRing().getVertices().size() - 1)); writer.write(' '); for (int i = 0; i < poly.getExteriorRing().getVertices().size() - 1; i++) { Vertex v = poly.getExteriorRing().getVertices().get(i); writer.write(Integer.toString(idMap.get(v))); writer.write(' '); } writer.write('\n'); } } catch (IOException e) { throw new UncheckedIOException(e); } }); } private int addRing(LinearRing ring, Map idMap, int counter) { for (Vertex v : ring.getVertices()) { if (!idMap.containsKey(v)) { idMap.put(v, counter); counter++; } } return counter; } public Set collectErrors() { List errors = new ArrayList<>(); collectErrorsFromList(errors, buildings); collectErrorsFromList(errors, vegetation); collectErrorsFromList(errors, bridges); collectErrorsFromList(errors, land); collectErrorsFromList(errors, roads); collectErrorsFromList(errors, water); return new HashSet<>(errors); } private void collectErrorsFromList(List errors, List cos) { for (CityObject co : cos) { co.collectContainedErrors(errors); } } public String getFileName() { return fileName; } public void addBridge(BridgeObject coBridge) { bridges.add(coBridge); } public void addBuilding(Building coBuilding) { buildings.add(coBuilding); } public List getBuildings() { return buildings; } public List getBridges() { return bridges; } public void setCityModel(CityModel cModel) { this.cModel = cModel; } public CityModel getCityModel() { return cModel; } public List getTransportation() { return roads; } public List getWater() { return water; } public List getLand() { return land; } public List getVegetation() { return vegetation; } public void addWater(WaterObject wo) { water.add(wo); } public void addLand(LandObject lo) { land.add(lo); } public void addVegetation(Vegetation veg) { vegetation.add(veg); } public void addTransportation(TransportationObject to) { roads.add(to); } public int getNumberOfFeatures() { return buildings.size() + vegetation.size() + bridges.size() + land.size() + roads.size() + water.size(); } public ParserConfiguration getParserConfig() { return config; } public void replaceFeature(CityObject currentFeature, CityObject nextFeature) { if (nextFeature instanceof Building) { replaceBuilding(currentFeature, nextFeature); } else if (nextFeature instanceof BridgeObject) { replaceBridge(currentFeature, nextFeature); } else if (nextFeature instanceof TransportationObject) { replaceTransportationObject(currentFeature, nextFeature); } else if (nextFeature instanceof Vegetation) { replaceVegetation(currentFeature, nextFeature); } else if (nextFeature instanceof LandObject) { replaceLandObject(currentFeature, nextFeature); } else if (nextFeature instanceof WaterObject) { replaceWaterObject(currentFeature, nextFeature); } } private void replaceWaterObject(CityObject currentFeature, CityObject nextFeature) { int index = water.indexOf(currentFeature); if (index == -1) { throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in water objects"); } water.set(index, (WaterObject) nextFeature); } private void replaceLandObject(CityObject currentFeature, CityObject nextFeature) { int index = land.indexOf(currentFeature); if (index == -1) { throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in land objects"); } land.set(index, (LandObject) nextFeature); } private void replaceVegetation(CityObject currentFeature, CityObject nextFeature) { int index = vegetation.indexOf(currentFeature); if (index == -1) { throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in vegetation"); } vegetation.set(index, (Vegetation) nextFeature); } private void replaceTransportationObject(CityObject currentFeature, CityObject nextFeature) { int index = roads.indexOf(currentFeature); if (index == -1) { throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in transportation objects"); } roads.set(index, (TransportationObject) nextFeature); } private void replaceBridge(CityObject currentFeature, CityObject nextFeature) { int index = bridges.indexOf(currentFeature); if (index == -1) { throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in bridges"); } bridges.set(index, (BridgeObject) nextFeature); } private void replaceBuilding(CityObject currentFeature, CityObject nextFeature) { int index = buildings.indexOf(currentFeature); if (index == -1) { throw new IllegalStateException(COULD_NOT_FIND_FEATURE + currentFeature + " in buildings"); } buildings.set(index, (Building) nextFeature); } public void addCityObject(CityObject co) { if (co instanceof Building b) { buildings.add(b); } else if (co instanceof BridgeObject bo) { bridges.add(bo); } else if (co instanceof TransportationObject to) { roads.add(to); } else if (co instanceof Vegetation veg) { vegetation.add(veg); } else if (co instanceof LandObject lo) { land.add(lo); } else if (co instanceof WaterObject wo) { water.add(wo); } } public void setParsedCityGMLVersion(CityGMLVersion cityGMLVersion) { this.cityGMLVersion = cityGMLVersion; } public CityGMLVersion getCityGMLVersion() { return cityGMLVersion; } }