Commit 9e161477 authored by Riegel's avatar Riegel
Browse files

Format code

parent 6b536067
Pipeline #10248 passed with stage
in 1 minute and 13 seconds
......@@ -45,1133 +45,1133 @@ import javafx.stage.FileChooser.ExtensionFilter;
public class CityDoctorController {
private static final int MAX_FEATURES_PER_CHUNK = 200;
private static final Logger logger = LogManager.getLogger(CityDoctorController.class);
private MainWindow mainWindow;
private CityDoctorModel model;
private ParserConfiguration currentConfig;
private String sourceFile;
private Checker currentChecker;
private HighlightController highlightController;
private Renderer renderer;
private AtomicInteger buildingChunkNr = new AtomicInteger(0);
private AtomicInteger vegetationChunkNr = new AtomicInteger(0);
private AtomicInteger transportationChunkNr = new AtomicInteger(0);
private AtomicInteger bridgeChunkNr = new AtomicInteger(0);
private AtomicInteger waterChunkNr = new AtomicInteger(0);
private AtomicInteger landChunkNr = new AtomicInteger(0);
private AtomicInteger cityFurnitureChunkNr = new AtomicInteger(0);
private AtomicInteger otherObjectsChunkNr = new AtomicInteger(0);
public CityDoctorController(MainWindow mainWindow, HighlightController highlightController, Renderer renderer) {
this.mainWindow = mainWindow;
this.highlightController = highlightController;
this.renderer = renderer;
}
public void loadCityGml(String path, int numberOfRoundingPlaces, ProgressListener l, boolean useValidation)
throws CityGmlParseException, InvalidGmlFileException {
loadCityGml(path, numberOfRoundingPlaces, l, useValidation, false);
}
public void loadCityGml(String path, int numberOfRoundingPlaces, ProgressListener l, boolean useValidation,
boolean lowMemory) throws CityGmlParseException, InvalidGmlFileException {
try {
Platform.runLater(() -> {
mainWindow.getOpenBtn().setDisable(true);
mainWindow.getWriteReportButton().setDisable(true);
mainWindow.getMeshGroup().getChildren().clear();
clearTrees();
mainWindow.resetSearchBar();
mainWindow.resetFilterComboBox();
});
currentChecker = null;
currentConfig = new ParserConfiguration(numberOfRoundingPlaces, useValidation, lowMemory);
GMLValidationHandler handler = null;
List<String> validationIssues = new ArrayList<>();
if (useValidation) {
handler = new GMLValidationHandler() {
@Override
public void error(SAXParseException exception) {
if (exception.getLineNumber() >= 0) {
String s = "In line " + exception.getLineNumber() + ":";
validationIssues.add(s);
}
validationIssues.add(exception.getMessage());
}
@Override
public void warning(SAXParseException exception) {
error(exception);
}
@Override
public void fatalError(SAXParseException exception) {
error(exception);
}
};
}
model = CityGmlParser.parseCityGmlFile(path, currentConfig, l, handler);
if (!validationIssues.isEmpty()) {
StringJoiner sj = new StringJoiner("\n");
validationIssues.stream().forEach(sj::add);
throw new InvalidGmlFileException(sj.toString());
}
mainWindow.getClickHandler().setConfig(currentConfig);
sourceFile = path;
renderer.reset();
Platform.runLater(() -> {
mainWindow.addFileNameToTitle(path);
mainWindow.getCheckButton().setDisable(false);
mainWindow.getLod1Btn().setDisable(false);
mainWindow.getLod2Btn().setDisable(false);
mainWindow.getLod3Btn().setDisable(false);
mainWindow.getLod4Btn().setDisable(false);
mainWindow.getWorldBtn().setDisable(false);
mainWindow.getSaveBtn().setDisable(false);
buildTrees();
});
} finally {
Platform.runLater(() -> mainWindow.getOpenBtn().setDisable(false));
}
}
public void buildTrees() {
resetFeatureChunks();
buildBuildings(model);
buildVegetation(model.getVegetation());
buildTransportation(model.getTransportation());
buildBridges(model);
buildWater(model);
buildLand(model);
buildCityFurniture(model);
private static final int MAX_FEATURES_PER_CHUNK = 200;
private static final Logger logger = LogManager.getLogger(CityDoctorController.class);
private MainWindow mainWindow;
private CityDoctorModel model;
private ParserConfiguration currentConfig;
private String sourceFile;
private Checker currentChecker;
private HighlightController highlightController;
private Renderer renderer;
private AtomicInteger buildingChunkNr = new AtomicInteger(0);
private AtomicInteger vegetationChunkNr = new AtomicInteger(0);
private AtomicInteger transportationChunkNr = new AtomicInteger(0);
private AtomicInteger bridgeChunkNr = new AtomicInteger(0);
private AtomicInteger waterChunkNr = new AtomicInteger(0);
private AtomicInteger landChunkNr = new AtomicInteger(0);
private AtomicInteger cityFurnitureChunkNr = new AtomicInteger(0);
private AtomicInteger otherObjectsChunkNr = new AtomicInteger(0);
public CityDoctorController(MainWindow mainWindow, HighlightController highlightController, Renderer renderer) {
this.mainWindow = mainWindow;
this.highlightController = highlightController;
this.renderer = renderer;
}
public void loadCityGml(String path, int numberOfRoundingPlaces, ProgressListener l, boolean useValidation)
throws CityGmlParseException, InvalidGmlFileException {
loadCityGml(path, numberOfRoundingPlaces, l, useValidation, false);
}
public void loadCityGml(String path, int numberOfRoundingPlaces, ProgressListener l, boolean useValidation,
boolean lowMemory) throws CityGmlParseException, InvalidGmlFileException {
try {
Platform.runLater(() -> {
mainWindow.getOpenBtn().setDisable(true);
mainWindow.getWriteReportButton().setDisable(true);
mainWindow.getMeshGroup().getChildren().clear();
clearTrees();
mainWindow.resetSearchBar();
mainWindow.resetFilterComboBox();
});
currentChecker = null;
currentConfig = new ParserConfiguration(numberOfRoundingPlaces, useValidation, lowMemory);
GMLValidationHandler handler = null;
List<String> validationIssues = new ArrayList<>();
if (useValidation) {
handler = new GMLValidationHandler() {
@Override
public void error(SAXParseException exception) {
if (exception.getLineNumber() >= 0) {
String s = "In line " + exception.getLineNumber() + ":";
validationIssues.add(s);
}
validationIssues.add(exception.getMessage());
}
@Override
public void warning(SAXParseException exception) {
error(exception);
}
@Override
public void fatalError(SAXParseException exception) {
error(exception);
}
};
}
model = CityGmlParser.parseCityGmlFile(path, currentConfig, l, handler);
if (!validationIssues.isEmpty()) {
StringJoiner sj = new StringJoiner("\n");
validationIssues.stream().forEach(sj::add);
throw new InvalidGmlFileException(sj.toString());
}
mainWindow.getClickHandler().setConfig(currentConfig);
sourceFile = path;
renderer.reset();
Platform.runLater(() -> {
mainWindow.addFileNameToTitle(path);
mainWindow.getCheckButton().setDisable(false);
mainWindow.getLod1Btn().setDisable(false);
mainWindow.getLod2Btn().setDisable(false);
mainWindow.getLod3Btn().setDisable(false);
mainWindow.getLod4Btn().setDisable(false);
mainWindow.getWorldBtn().setDisable(false);
mainWindow.getSaveBtn().setDisable(false);
buildTrees();
});
} finally {
Platform.runLater(() -> mainWindow.getOpenBtn().setDisable(false));
}
}
public void buildTrees() {
resetFeatureChunks();
buildBuildings(model);
buildVegetation(model.getVegetation());
buildTransportation(model.getTransportation());
buildBridges(model);
buildWater(model);
buildLand(model);
buildCityFurniture(model);
buildOtherCityObjects(model);
}
private void resetFeatureChunks() {
buildingChunkNr.set(0);
vegetationChunkNr.set(0);
transportationChunkNr.set(0);
bridgeChunkNr.set(0);
waterChunkNr.set(0);
landChunkNr.set(0);
cityFurnitureChunkNr.set(0);
otherObjectsChunkNr.set(0);
}
private void buildLand(CityDoctorModel model) {
if (model.getLand().isEmpty()) {
return;
}
TreeView<Renderable> landView = mainWindow.getTerrainView();
TreeItem<Renderable> landRoot = new TreeItem<>(new AllTerrainNode(model.getLand()));
landRoot.setExpanded(true);
landView.setRoot(landRoot);
buildLandFromList(model.getLand(), landView.getRoot());
addMoreButtonIfNecessary(model.getLand(), landView, landRoot, landChunkNr);
}
private void buildLandFromList(List<CityObject> list, TreeItem<Renderable> root) {
int landChunk = landChunkNr.get();
for (int i = landChunk * MAX_FEATURES_PER_CHUNK; i < (landChunk + 1) * MAX_FEATURES_PER_CHUNK
&& i < list.size(); i++) {
CityObject land = list.get(i);
Renderable node;
if (land instanceof ReliefObject relief) {
node = new ReliefNode(relief);
} else if (land instanceof TinObject tin) {
node = new TinNode(tin);
} else {
node = new LandUseNode((LandObject) land);
}
TreeItem<Renderable> item = new TreeItem<>(node);
item.setExpanded(true);
root.getChildren().add(item);
createGeometryNodes(land, item);
if (land instanceof ReliefObject relief) {
createTinNodes(relief, item);
}
}
}
private void createTinNodes(ReliefObject relief, TreeItem<Renderable> item) {
if (relief.getComponents().isEmpty()) {
return;
}
AllTinNode allTinText = new AllTinNode(relief.getComponents());
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allTinText);
item.getChildren().add(allBpsTextItem);
for (TinObject tin : relief.getComponents()) {
TinNode tinNode = new TinNode(tin);
TreeItem<Renderable> tinNodeItem = new TreeItem<>(tinNode);
tinNodeItem.setExpanded(true);
allBpsTextItem.getChildren().add(tinNodeItem);
createGeometryNodes(tin, tinNodeItem);
}
}
private void buildCityFurniture(CityDoctorModel model){
if (model.getCityFurniture().isEmpty()){
return;
}
TreeView<Renderable> cityFurnitureView = mainWindow.getCityFurnitureView();
TreeItem<Renderable> cityFurnitureRoot = new TreeItem<>(new AllCityFurnitureNode(model.getCityFurniture()));
cityFurnitureRoot.setExpanded(true);
cityFurnitureView.setRoot(cityFurnitureRoot);
buildTreeFromList(model.getCityFurniture(), cityFurnitureView.getRoot(), cityFurnitureChunkNr);
addMoreButtonIfNecessary(model.getCityFurniture(), cityFurnitureView, cityFurnitureRoot, cityFurnitureChunkNr);
}
private void buildOtherCityObjects(CityDoctorModel model){
if (model.getGenericCityObjects().isEmpty()){
return;
}
TreeView<Renderable> otherObjectsView = mainWindow.getOtherObjectsView();
TreeItem<Renderable> otherObjectsRoot = new TreeItem<>(new AllOtherObjectsNode(model.getGenericCityObjects()));
otherObjectsRoot.setExpanded(true);
otherObjectsView.setRoot(otherObjectsRoot);
buildTreeFromList(model.getGenericCityObjects(), otherObjectsView.getRoot(), otherObjectsChunkNr);
addMoreButtonIfNecessary(model.getGenericCityObjects(), otherObjectsView, otherObjectsRoot, otherObjectsChunkNr);
}
private void buildWater(CityDoctorModel model) {
if (model.getWater().isEmpty()) {
return;
}
TreeView<Renderable> waterView = mainWindow.getWaterView();
TreeItem<Renderable> waterRoot = new TreeItem<>(new AllWaterNode(model.getWater()));
waterRoot.setExpanded(true);
waterView.setRoot(waterRoot);
buildTreeFromList(model.getWater(), waterView.getRoot(), waterChunkNr);
addMoreButtonIfNecessary(model.getWater(), waterView, waterRoot, waterChunkNr);
}
private void buildBridges(CityDoctorModel model) {
if (model.getBridges().isEmpty()) {
return;
}
TreeView<Renderable> bridgeView = mainWindow.getBridgeView();
TreeItem<Renderable> bridgeRoot = new TreeItem<>(new AllBridgesNode(model.getBridges()));
bridgeRoot.setExpanded(true);
bridgeView.setRoot(bridgeRoot);
buildBridgeTreeFromList(model.getBridges(), bridgeView.getRoot());
addMoreButtonIfNecessary(model.getBridges(), bridgeView, bridgeRoot, bridgeChunkNr);
}
private void buildTransportation(List<TransportationObject> trans) {
if (trans.isEmpty()) {
return;
}
TreeView<Renderable> transView = mainWindow.getTransportationView();
TreeItem<Renderable> transRoot = new TreeItem<>(new AllTransportationNode(model.getTransportation()));
transRoot.setExpanded(true);
transView.setRoot(transRoot);
buildTreeFromList(trans, transView.getRoot(), transportationChunkNr);
addMoreButtonIfNecessary(trans, transView, transRoot, transportationChunkNr);
}
private void buildVegetation(List<Vegetation> veg) {
if (veg.isEmpty()) {
return;
}
TreeView<Renderable> vegetationsView = mainWindow.getVegetationView();
TreeItem<Renderable> vegRoot = new TreeItem<>(new AllVegetationNode(model.getVegetation()));
vegRoot.setExpanded(true);
vegetationsView.setRoot(vegRoot);
buildTreeFromListWithProducer(veg, vegetationsView.getRoot(), vegetationChunkNr, VegetationNode::new);
addMoreButtonIfNecessary(veg, vegetationsView, vegRoot, vegetationChunkNr);
}
private void addMoreButtonIfNecessary(List<? extends CityObject> list, TreeView<Renderable> view,
TreeItem<Renderable> root, AtomicInteger chunkCounter) {
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < list.size()) {
Runnable run = new Runnable() {
public void run() {
// 29 is the height of one item
int numberOfDisplayedItems = (int) (view.getHeight() / 29);
int selectedIndex = view.getSelectionModel().getSelectedIndex();
// remove button node
root.getChildren().remove(root.getChildren().size() - 1);
chunkCounter.incrementAndGet();
int visibleFeatures = countVisibleNodes(view);
buildTreeFromList(list, root, chunkCounter);
updateTree(root);
if (selectedIndex >= 0) {
view.getSelectionModel().select(selectedIndex);
}
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < list.size()) {
root.getChildren().add(new TreeItem<>(new ButtonRenderable(this)));
}
view.scrollTo(visibleFeatures - numberOfDisplayedItems + 1);
}
};
// add button for expansion
root.getChildren().add(new TreeItem<>(new ButtonRenderable(run)));
}
}
private void buildBuildings(CityDoctorModel model) {
if (model.getBuildings().isEmpty()) {
return;
}
TreeView<Renderable> buildingsView = mainWindow.getBuildingsView();
TreeItem<Renderable> root = new TreeItem<>(new AllBuildingsNode(model.getBuildings()));
root.setExpanded(true);
buildBuildingTreeFromList(model.getBuildings(), root);
addMoreButtonToBuildingsIfNecessary(model.getBuildings(), buildingsView, root, buildingChunkNr);
buildingsView.setRoot(root);
}
private void addMoreButtonToBuildingsIfNecessary(List<Building> list, TreeView<Renderable> view,
TreeItem<Renderable> root, AtomicInteger chunkCounter) {
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < list.size()) {
Runnable run = new Runnable() {
public void run() {
// 29 is the height of one item
int numberOfDisplayedItems = (int) (view.getHeight() / 29);
int selectedIndex = view.getSelectionModel().getSelectedIndex();
// remove button node
root.getChildren().remove(root.getChildren().size() - 1);
chunkCounter.getAndIncrement();
int visibleFeatures = countVisibleNodes(view);
buildBuildingTreeFromList(list, root);
updateTree(root);
if (selectedIndex >= 0) {
view.getSelectionModel().select(selectedIndex);
}
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < list.size()) {
root.getChildren().add(new TreeItem<>(new ButtonRenderable(this)));
}
view.scrollTo(visibleFeatures - numberOfDisplayedItems + 1);
}
};
// add button for expansion
root.getChildren().add(new TreeItem<>(new ButtonRenderable(run)));
}
}
private static int countVisibleNodes(TreeView<Renderable> view) {
return countVisibleNodes(view.getRoot()) + 1;
}
private static int countVisibleNodes(TreeItem<Renderable> root) {
int visibleFeatures = root.getChildren().size();
for (TreeItem<Renderable> child : root.getChildren()) {
if (!child.getChildren().isEmpty() && child.isExpanded()) {
visibleFeatures += countVisibleNodes(child);
}
}
return visibleFeatures;
}
private void buildTreeFromList(List<? extends CityObject> cos, TreeItem<Renderable> root,
AtomicInteger chunkCounter) {
int chunk = chunkCounter.get();
for (int i = chunk * MAX_FEATURES_PER_CHUNK; i < (chunk + 1) * MAX_FEATURES_PER_CHUNK && i < cos.size(); i++) {
CityObject co = cos.get(i);
CityObjectNode node = new CityObjectNode(co);
TreeItem<Renderable> nodeItem = new TreeItem<>(node);
nodeItem.setExpanded(true);
root.getChildren().add(nodeItem);
createGeometryNodes(co, nodeItem);
}
}
private <T extends CityObject> void buildTreeFromListWithProducer(List<T> cos, TreeItem<Renderable> root,
AtomicInteger chunkCounter, Function<T, Renderable> nodeProducer) {
int chunk = chunkCounter.get();
for (int i = chunk * MAX_FEATURES_PER_CHUNK; i < (chunk + 1) * MAX_FEATURES_PER_CHUNK && i < cos.size(); i++) {
T co = cos.get(i);
Renderable node = nodeProducer.apply(co);
TreeItem<Renderable> nodeItem = new TreeItem<>(node);
nodeItem.setExpanded(true);
root.getChildren().add(nodeItem);
createGeometryNodes(co, nodeItem);
}
}
private void clearTrees() {
mainWindow.getBuildingsView().setRoot(null);
mainWindow.getVegetationView().setRoot(null);
mainWindow.getTransportationView().setRoot(null);
mainWindow.getBridgeView().setRoot(null);
mainWindow.getWaterView().setRoot(null);
mainWindow.getTerrainView().setRoot(null);
mainWindow.getCityFurnitureView().setRoot(null);
}
private void resetFeatureChunks() {
buildingChunkNr.set(0);
vegetationChunkNr.set(0);
transportationChunkNr.set(0);
bridgeChunkNr.set(0);
waterChunkNr.set(0);
landChunkNr.set(0);
cityFurnitureChunkNr.set(0);
otherObjectsChunkNr.set(0);
}
private void buildLand(CityDoctorModel model) {
if (model.getLand().isEmpty()) {
return;
}
TreeView<Renderable> landView = mainWindow.getTerrainView();
TreeItem<Renderable> landRoot = new TreeItem<>(new AllTerrainNode(model.getLand()));
landRoot.setExpanded(true);
landView.setRoot(landRoot);
buildLandFromList(model.getLand(), landView.getRoot());
addMoreButtonIfNecessary(model.getLand(), landView, landRoot, landChunkNr);
}
private void buildLandFromList(List<CityObject> list, TreeItem<Renderable> root) {
int landChunk = landChunkNr.get();
for (int i = landChunk * MAX_FEATURES_PER_CHUNK; i < (landChunk + 1) * MAX_FEATURES_PER_CHUNK
&& i < list.size(); i++) {
CityObject land = list.get(i);
Renderable node;
if (land instanceof ReliefObject relief) {
node = new ReliefNode(relief);
} else if (land instanceof TinObject tin) {
node = new TinNode(tin);
} else {
node = new LandUseNode((LandObject) land);
}
TreeItem<Renderable> item = new TreeItem<>(node);
item.setExpanded(true);
root.getChildren().add(item);
createGeometryNodes(land, item);
if (land instanceof ReliefObject relief) {
createTinNodes(relief, item);
}
}
}
private void createTinNodes(ReliefObject relief, TreeItem<Renderable> item) {
if (relief.getComponents().isEmpty()) {
return;
}
AllTinNode allTinText = new AllTinNode(relief.getComponents());
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allTinText);
item.getChildren().add(allBpsTextItem);
for (TinObject tin : relief.getComponents()) {
TinNode tinNode = new TinNode(tin);
TreeItem<Renderable> tinNodeItem = new TreeItem<>(tinNode);
tinNodeItem.setExpanded(true);
allBpsTextItem.getChildren().add(tinNodeItem);
createGeometryNodes(tin, tinNodeItem);
}
}
private void buildCityFurniture(CityDoctorModel model) {
if (model.getCityFurniture().isEmpty()) {
return;
}
TreeView<Renderable> cityFurnitureView = mainWindow.getCityFurnitureView();
TreeItem<Renderable> cityFurnitureRoot = new TreeItem<>(new AllCityFurnitureNode(model.getCityFurniture()));
cityFurnitureRoot.setExpanded(true);
cityFurnitureView.setRoot(cityFurnitureRoot);
buildTreeFromList(model.getCityFurniture(), cityFurnitureView.getRoot(), cityFurnitureChunkNr);
addMoreButtonIfNecessary(model.getCityFurniture(), cityFurnitureView, cityFurnitureRoot, cityFurnitureChunkNr);
}
private void buildOtherCityObjects(CityDoctorModel model) {
if (model.getGenericCityObjects().isEmpty()) {
return;
}
TreeView<Renderable> otherObjectsView = mainWindow.getOtherObjectsView();
TreeItem<Renderable> otherObjectsRoot = new TreeItem<>(new AllOtherObjectsNode(model.getGenericCityObjects()));
otherObjectsRoot.setExpanded(true);
otherObjectsView.setRoot(otherObjectsRoot);
buildTreeFromList(model.getGenericCityObjects(), otherObjectsView.getRoot(), otherObjectsChunkNr);
addMoreButtonIfNecessary(model.getGenericCityObjects(), otherObjectsView, otherObjectsRoot, otherObjectsChunkNr);
}
private void buildWater(CityDoctorModel model) {
if (model.getWater().isEmpty()) {
return;
}
TreeView<Renderable> waterView = mainWindow.getWaterView();
TreeItem<Renderable> waterRoot = new TreeItem<>(new AllWaterNode(model.getWater()));
waterRoot.setExpanded(true);
waterView.setRoot(waterRoot);
buildTreeFromList(model.getWater(), waterView.getRoot(), waterChunkNr);
addMoreButtonIfNecessary(model.getWater(), waterView, waterRoot, waterChunkNr);
}
private void buildBridges(CityDoctorModel model) {
if (model.getBridges().isEmpty()) {
return;
}
TreeView<Renderable> bridgeView = mainWindow.getBridgeView();
TreeItem<Renderable> bridgeRoot = new TreeItem<>(new AllBridgesNode(model.getBridges()));
bridgeRoot.setExpanded(true);
bridgeView.setRoot(bridgeRoot);
buildBridgeTreeFromList(model.getBridges(), bridgeView.getRoot());
addMoreButtonIfNecessary(model.getBridges(), bridgeView, bridgeRoot, bridgeChunkNr);
}
private void buildTransportation(List<TransportationObject> trans) {
if (trans.isEmpty()) {
return;
}
TreeView<Renderable> transView = mainWindow.getTransportationView();
TreeItem<Renderable> transRoot = new TreeItem<>(new AllTransportationNode(model.getTransportation()));
transRoot.setExpanded(true);
transView.setRoot(transRoot);
buildTreeFromList(trans, transView.getRoot(), transportationChunkNr);
addMoreButtonIfNecessary(trans, transView, transRoot, transportationChunkNr);
}
private void buildVegetation(List<Vegetation> veg) {
if (veg.isEmpty()) {
return;
}
TreeView<Renderable> vegetationsView = mainWindow.getVegetationView();
TreeItem<Renderable> vegRoot = new TreeItem<>(new AllVegetationNode(model.getVegetation()));
vegRoot.setExpanded(true);
vegetationsView.setRoot(vegRoot);
buildTreeFromListWithProducer(veg, vegetationsView.getRoot(), vegetationChunkNr, VegetationNode::new);
addMoreButtonIfNecessary(veg, vegetationsView, vegRoot, vegetationChunkNr);
}
private void addMoreButtonIfNecessary(List<? extends CityObject> list, TreeView<Renderable> view,
TreeItem<Renderable> root, AtomicInteger chunkCounter) {
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < list.size()) {
Runnable run = new Runnable() {
public void run() {
// 29 is the height of one item
int numberOfDisplayedItems = (int) (view.getHeight() / 29);
int selectedIndex = view.getSelectionModel().getSelectedIndex();
// remove button node
root.getChildren().remove(root.getChildren().size() - 1);
chunkCounter.incrementAndGet();
int visibleFeatures = countVisibleNodes(view);
buildTreeFromList(list, root, chunkCounter);
updateTree(root);
if (selectedIndex >= 0) {
view.getSelectionModel().select(selectedIndex);
}
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < list.size()) {
root.getChildren().add(new TreeItem<>(new ButtonRenderable(this)));
}
view.scrollTo(visibleFeatures - numberOfDisplayedItems + 1);
}
};
// add button for expansion
root.getChildren().add(new TreeItem<>(new ButtonRenderable(run)));
}
}
private void buildBuildings(CityDoctorModel model) {
if (model.getBuildings().isEmpty()) {
return;
}
TreeView<Renderable> buildingsView = mainWindow.getBuildingsView();
TreeItem<Renderable> root = new TreeItem<>(new AllBuildingsNode(model.getBuildings()));
root.setExpanded(true);
buildBuildingTreeFromList(model.getBuildings(), root);
addMoreButtonToBuildingsIfNecessary(model.getBuildings(), buildingsView, root, buildingChunkNr);
buildingsView.setRoot(root);
}
private void addMoreButtonToBuildingsIfNecessary(List<Building> list, TreeView<Renderable> view,
TreeItem<Renderable> root, AtomicInteger chunkCounter) {
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < list.size()) {
Runnable run = new Runnable() {
public void run() {
// 29 is the height of one item
int numberOfDisplayedItems = (int) (view.getHeight() / 29);
int selectedIndex = view.getSelectionModel().getSelectedIndex();
// remove button node
root.getChildren().remove(root.getChildren().size() - 1);
chunkCounter.getAndIncrement();
int visibleFeatures = countVisibleNodes(view);
buildBuildingTreeFromList(list, root);
updateTree(root);
if (selectedIndex >= 0) {
view.getSelectionModel().select(selectedIndex);
}
if ((chunkCounter.get() + 1) * MAX_FEATURES_PER_CHUNK < list.size()) {
root.getChildren().add(new TreeItem<>(new ButtonRenderable(this)));
}
view.scrollTo(visibleFeatures - numberOfDisplayedItems + 1);
}
};
// add button for expansion
root.getChildren().add(new TreeItem<>(new ButtonRenderable(run)));
}
}
private static int countVisibleNodes(TreeView<Renderable> view) {
return countVisibleNodes(view.getRoot()) + 1;
}
private static int countVisibleNodes(TreeItem<Renderable> root) {
int visibleFeatures = root.getChildren().size();
for (TreeItem<Renderable> child : root.getChildren()) {
if (!child.getChildren().isEmpty() && child.isExpanded()) {
visibleFeatures += countVisibleNodes(child);
}
}
return visibleFeatures;
}
private void buildTreeFromList(List<? extends CityObject> cos, TreeItem<Renderable> root,
AtomicInteger chunkCounter) {
int chunk = chunkCounter.get();
for (int i = chunk * MAX_FEATURES_PER_CHUNK; i < (chunk + 1) * MAX_FEATURES_PER_CHUNK && i < cos.size(); i++) {
CityObject co = cos.get(i);
CityObjectNode node = new CityObjectNode(co);
TreeItem<Renderable> nodeItem = new TreeItem<>(node);
nodeItem.setExpanded(true);
root.getChildren().add(nodeItem);
createGeometryNodes(co, nodeItem);
}
}
private <T extends CityObject> void buildTreeFromListWithProducer(List<T> cos, TreeItem<Renderable> root,
AtomicInteger chunkCounter, Function<T, Renderable> nodeProducer) {
int chunk = chunkCounter.get();
for (int i = chunk * MAX_FEATURES_PER_CHUNK; i < (chunk + 1) * MAX_FEATURES_PER_CHUNK && i < cos.size(); i++) {
T co = cos.get(i);
Renderable node = nodeProducer.apply(co);
TreeItem<Renderable> nodeItem = new TreeItem<>(node);
nodeItem.setExpanded(true);
root.getChildren().add(nodeItem);
createGeometryNodes(co, nodeItem);
}
}
private void clearTrees() {
mainWindow.getBuildingsView().setRoot(null);
mainWindow.getVegetationView().setRoot(null);
mainWindow.getTransportationView().setRoot(null);
mainWindow.getBridgeView().setRoot(null);
mainWindow.getWaterView().setRoot(null);
mainWindow.getTerrainView().setRoot(null);
mainWindow.getCityFurnitureView().setRoot(null);
mainWindow.getOtherObjectsView().setRoot(null);
mainWindow.getErrorTree().getRoot().getChildren().clear();
mainWindow.getGlobalErrorsView().getItems().clear();
clearGeometryTrees();
}
private void clearGeometryTrees() {
highlightController.clearHighlights();
mainWindow.getPolygonsView().getRoot().getChildren().clear();
mainWindow.getEdgeView().getRoot().getChildren().clear();
mainWindow.getVertexView().getRoot().getChildren().clear();
}
private void buildBuildingTreeFromList(List<Building> list, TreeItem<Renderable> root) {
int buildingChunk = buildingChunkNr.get();
for (int i = buildingChunk * MAX_FEATURES_PER_CHUNK; i < (buildingChunk + 1) * MAX_FEATURES_PER_CHUNK
&& i < list.size(); i++) {
Building b = list.get(i);
BuildingNode node = new BuildingNode(b);
TreeItem<Renderable> item = new TreeItem<>(node);
item.setExpanded(true);
root.getChildren().add(item);
createGeometryNodes(b, item);
createBoundarySurfaceNodes(b.getBoundarySurfaces(), item);
createBuildingInstallationNodes(b, item);
createBuildingRoomNodes(b, item);
createBuildingFurnitureNodes(b, item);
createBuildingPartNodes(b, item);
}
}
private void buildBridgeTreeFromList(List<BridgeObject> list, TreeItem<Renderable> root) {
int bridgeChunk = bridgeChunkNr.get();
for (int i = bridgeChunk * MAX_FEATURES_PER_CHUNK; i < (bridgeChunk + 1) * MAX_FEATURES_PER_CHUNK
&& i < list.size(); i++) {
BridgeObject bridge = list.get(i);
BridgeNode node = new BridgeNode(bridge);
TreeItem<Renderable> item = new TreeItem<>(node);
item.setExpanded(true);
root.getChildren().add(item);
createGeometryNodes(bridge, item);
createBoundarySurfaceNodes(bridge.getBoundarySurfaces(), item);
createBridgeInstallationNodes(bridge, item);
createConstructiveElementsNodes(bridge, item);
createBridgePartNodes(bridge, item);
}
}
private void createBridgeInstallationNodes(BridgeObject bridge, TreeItem<Renderable> root) {
createInstallationNodes(bridge.getBridgeInstallations(), root);
}
private void createConstructiveElementsNodes(BridgeObject bridge, TreeItem<Renderable> item) {
if (bridge.getConstructiveElements().isEmpty()) {
return;
}
AllBridgeConstructiveElementsNode allBceNode = new AllBridgeConstructiveElementsNode(
bridge.getConstructiveElements());
TreeItem<Renderable> allBceNodeTextItem = new TreeItem<>(allBceNode);
item.getChildren().add(allBceNodeTextItem);
for (BridgeConstructiveElement bce : bridge.getConstructiveElements()) {
BridgeConstructiveElementNode bceNode = new BridgeConstructiveElementNode(bce);
TreeItem<Renderable> bpNodeItem = new TreeItem<>(bceNode);
bpNodeItem.setExpanded(true);
allBceNodeTextItem.getChildren().add(bpNodeItem);
createGeometryNodes(bce, bpNodeItem);
createBoundarySurfaceNodes(bce.getBoundarySurfaces(), bpNodeItem);
}
}
private void createBridgePartNodes(BridgeObject bridge, TreeItem<Renderable> item) {
if (bridge.getParts().isEmpty()) {
return;
}
AllBridgePartsNode allBpsText = new AllBridgePartsNode(bridge.getParts());
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allBpsText);
item.getChildren().add(allBpsTextItem);
for (BridgeObject bp : bridge.getParts()) {
BridgeNode bpNode = new BridgeNode(bp);
TreeItem<Renderable> bpNodeItem = new TreeItem<>(bpNode);
bpNodeItem.setExpanded(true);
allBpsTextItem.getChildren().add(bpNodeItem);
createGeometryNodes(bp, bpNodeItem);
createBoundarySurfaceNodes(bp.getBoundarySurfaces(), bpNodeItem);
}
}
private void createBuildingInstallationNodes(Building ab, TreeItem<Renderable> root) {
createInstallationNodes(ab.getBuildingInstallations(), root);
}
private void createInstallationNodes(List<Installation> installations, TreeItem<Renderable> root) {
if (installations.isEmpty()) {
return;
}
AllInstallationsNode allBiNode = new AllInstallationsNode(installations);
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allBiNode);
root.getChildren().add(allBpsTextItem);
for (Installation bi : installations) {
InstallationNode biNode = new InstallationNode(bi);
TreeItem<Renderable> biItem = new TreeItem<>(biNode);
biItem.setExpanded(true);
allBpsTextItem.getChildren().add(biItem);
createGeometryNodes(bi, biItem);
createBoundarySurfaceNodes(bi.getBoundarySurfaces(), biItem);
}
}
private void createBuildingRoomNodes(Building ab, TreeItem<Renderable> root) {
createRoomNodes(ab.getBuildingRooms(), root);
}
private void createRoomNodes(List<? extends AbstractRoom> rooms, TreeItem<Renderable> root) {
if (rooms.isEmpty()) {
return;
}
AllRoomsNode allRoNode = new AllRoomsNode(rooms);
TreeItem<Renderable> allRoNodeTextItem = new TreeItem<>(allRoNode);
root.getChildren().add(allRoNodeTextItem);
for (AbstractRoom room : rooms) {
RoomNode roomNode = new RoomNode(room);
TreeItem<Renderable> roomNodeTextItem = new TreeItem<>(roomNode);
roomNodeTextItem.setExpanded(true);
allRoNodeTextItem.getChildren().add(roomNodeTextItem);
createGeometryNodes(room, roomNodeTextItem);
createBoundarySurfaceNodes(room.getBoundarySurfaceList(), roomNodeTextItem);
}
}
private void createBuildingFurnitureNodes(Building ab, TreeItem<Renderable> root) {
createFurnitureNodes(ab.getBuildingRoomFurnitureList(),root);
}
private void createFurnitureNodes(List<? extends AbstractFurniture> furniture, TreeItem<Renderable> root){
if(furniture.isEmpty()) {
return;
}
AllFurnitureNode allFnNode = new AllFurnitureNode(furniture);
TreeItem<Renderable> allFnNodeTextItem = new TreeItem<>(allFnNode);
root.getChildren().add(allFnNodeTextItem);
for (AbstractFurniture fn : furniture) {
FurnitureNode fnNode = new FurnitureNode(fn);
TreeItem<Renderable> fnNodeTextItem = new TreeItem<>(fnNode);
fnNodeTextItem.setExpanded(true);
allFnNodeTextItem.getChildren().add(fnNodeTextItem);
createGeometryNodes(fn, fnNodeTextItem);
createBoundarySurfaceNodes(fn.getBoundarySurfaceList(), fnNodeTextItem);
}
}
private void createBoundarySurfaceNodes(List<BoundarySurface> bsList, TreeItem<Renderable> root) {
if (bsList.isEmpty()) {
return;
}
AllBoundarySurfacesNode allBsText = new AllBoundarySurfacesNode(bsList);
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allBsText);
root.getChildren().add(allBpsTextItem);
allBpsTextItem.getChildren().add(new TreeItem<>());
allBpsTextItem.expandedProperty().addListener((obs, old, newV) -> {
if (Boolean.TRUE.equals(newV) && allBpsTextItem.getChildren().size() == 1) {
Platform.runLater(() -> {
allBpsTextItem.getChildren().clear();
for (BoundarySurface bs : bsList) {
BoundarySurfaceNode bsText = new BoundarySurfaceNode(bs);
TreeItem<Renderable> bsTextItem = new TreeItem<>(bsText);
bsTextItem.setExpanded(true);
allBpsTextItem.getChildren().add(bsTextItem);
createGeometryNodes(bs, bsTextItem);
createOpeningNodes(bs.getOpenings(), bsTextItem);
}
updateTree(allBpsTextItem);
});
}
});
}
private void createOpeningNodes(List<Opening> openings, TreeItem<Renderable> root) {
if (openings == null || openings.isEmpty()) {
return;
}
AllOpeningsNode allOpeningsNode = new AllOpeningsNode(openings);
TreeItem<Renderable> allOpeningsItem = new TreeItem<>(allOpeningsNode);
for (Opening o : openings) {
OpeningNode openingNode = new OpeningNode(o);
TreeItem<Renderable> openingsItem = new TreeItem<>(openingNode);
allOpeningsItem.getChildren().add(openingsItem);
}
root.getChildren().add(allOpeningsItem);
}
private void createBuildingPartNodes(Building ab, TreeItem<Renderable> item) {
if (ab.getBuildingParts().isEmpty()) {
return;
}
AllBuildingPartsNode allBpsText = new AllBuildingPartsNode(ab.getBuildingParts());
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allBpsText);
item.getChildren().add(allBpsTextItem);
for (BuildingPart bp : ab.getBuildingParts()) {
BuildingPartNode bpNode = new BuildingPartNode(bp);
TreeItem<Renderable> bpNodeItem = new TreeItem<>(bpNode);
bpNodeItem.setExpanded(true);
allBpsTextItem.getChildren().add(bpNodeItem);
createGeometryNodes(bp, bpNodeItem);
createBoundarySurfaceNodes(bp.getBoundarySurfaces(), bpNodeItem);
}
}
private void createGeometryNodes(CityObject co, TreeItem<Renderable> item) {
for (Geometry geom : co.getGeometries()) {
GeometryNode geomNode = new GeometryNode(geom);
TreeItem<Renderable> geomItem = new TreeItem<>(geomNode);
item.getChildren().add(geomItem);
}
}
public void startChecks(ValidationConfiguration config, ProgressListener l) {
if (model == null) {
logger.warn(Localization.getText("CityDoctorController.noDatamodel"));
return;
}
if (sourceFile == null) {
logger.warn(Localization.getText("CityDoctorController.noSourceFile"));
return;
}
try {
Platform.runLater(() -> {
mainWindow.getCheckButton().setDisable(true);
mainWindow.getErrorTree().getRoot().getChildren().clear();
mainWindow.getGlobalErrorsView().getItems().clear();
});
config.setNumberOfRoundingPlacesInGlobalParameters(currentConfig.getNumberOfRoundingPlaces());
config.setParserConfig(currentConfig);
currentChecker = new Checker(config, model);
currentChecker.runChecks(l);
Platform.runLater(() -> {
// apply check results to tree views
updateFeatureTrees();
updateTree(mainWindow.getPolygonsView().getRoot());
for (CheckError e : model.getGlobalErrors()) {
if (e instanceof SchematronError se) {
mainWindow.getGlobalErrorsView().getItems().add(se.getErrorIdString());
}
}
renderer.refresh();
mainWindow.getWriteReportButton().setDisable(false);
});
} finally {
Platform.runLater(() -> mainWindow.getCheckButton().setDisable(false));
}
}
void updateFeatureTrees() {
updateTree(mainWindow.getBuildingsView().getRoot());
updateTree(mainWindow.getVegetationView().getRoot());
updateTree(mainWindow.getBridgeView().getRoot());
updateTree(mainWindow.getTerrainView().getRoot());
updateTree(mainWindow.getCityFurnitureView().getRoot());
updateTree(mainWindow.getTransportationView().getRoot());
updateTree(mainWindow.getWaterView().getRoot());
mainWindow.getErrorTree().getRoot().getChildren().clear();
mainWindow.getGlobalErrorsView().getItems().clear();
clearGeometryTrees();
}
private void clearGeometryTrees() {
highlightController.clearHighlights();
mainWindow.getPolygonsView().getRoot().getChildren().clear();
mainWindow.getEdgeView().getRoot().getChildren().clear();
mainWindow.getVertexView().getRoot().getChildren().clear();
}
private void buildBuildingTreeFromList(List<Building> list, TreeItem<Renderable> root) {
int buildingChunk = buildingChunkNr.get();
for (int i = buildingChunk * MAX_FEATURES_PER_CHUNK; i < (buildingChunk + 1) * MAX_FEATURES_PER_CHUNK
&& i < list.size(); i++) {
Building b = list.get(i);
BuildingNode node = new BuildingNode(b);
TreeItem<Renderable> item = new TreeItem<>(node);
item.setExpanded(true);
root.getChildren().add(item);
createGeometryNodes(b, item);
createBoundarySurfaceNodes(b.getBoundarySurfaces(), item);
createBuildingInstallationNodes(b, item);
createBuildingRoomNodes(b, item);
createBuildingFurnitureNodes(b, item);
createBuildingPartNodes(b, item);
}
}
private void buildBridgeTreeFromList(List<BridgeObject> list, TreeItem<Renderable> root) {
int bridgeChunk = bridgeChunkNr.get();
for (int i = bridgeChunk * MAX_FEATURES_PER_CHUNK; i < (bridgeChunk + 1) * MAX_FEATURES_PER_CHUNK
&& i < list.size(); i++) {
BridgeObject bridge = list.get(i);
BridgeNode node = new BridgeNode(bridge);
TreeItem<Renderable> item = new TreeItem<>(node);
item.setExpanded(true);
root.getChildren().add(item);
createGeometryNodes(bridge, item);
createBoundarySurfaceNodes(bridge.getBoundarySurfaces(), item);
createBridgeInstallationNodes(bridge, item);
createConstructiveElementsNodes(bridge, item);
createBridgePartNodes(bridge, item);
}
}
private void createBridgeInstallationNodes(BridgeObject bridge, TreeItem<Renderable> root) {
createInstallationNodes(bridge.getBridgeInstallations(), root);
}
private void createConstructiveElementsNodes(BridgeObject bridge, TreeItem<Renderable> item) {
if (bridge.getConstructiveElements().isEmpty()) {
return;
}
AllBridgeConstructiveElementsNode allBceNode = new AllBridgeConstructiveElementsNode(
bridge.getConstructiveElements());
TreeItem<Renderable> allBceNodeTextItem = new TreeItem<>(allBceNode);
item.getChildren().add(allBceNodeTextItem);
for (BridgeConstructiveElement bce : bridge.getConstructiveElements()) {
BridgeConstructiveElementNode bceNode = new BridgeConstructiveElementNode(bce);
TreeItem<Renderable> bpNodeItem = new TreeItem<>(bceNode);
bpNodeItem.setExpanded(true);
allBceNodeTextItem.getChildren().add(bpNodeItem);
createGeometryNodes(bce, bpNodeItem);
createBoundarySurfaceNodes(bce.getBoundarySurfaces(), bpNodeItem);
}
}
private void createBridgePartNodes(BridgeObject bridge, TreeItem<Renderable> item) {
if (bridge.getParts().isEmpty()) {
return;
}
AllBridgePartsNode allBpsText = new AllBridgePartsNode(bridge.getParts());
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allBpsText);
item.getChildren().add(allBpsTextItem);
for (BridgeObject bp : bridge.getParts()) {
BridgeNode bpNode = new BridgeNode(bp);
TreeItem<Renderable> bpNodeItem = new TreeItem<>(bpNode);
bpNodeItem.setExpanded(true);
allBpsTextItem.getChildren().add(bpNodeItem);
createGeometryNodes(bp, bpNodeItem);
createBoundarySurfaceNodes(bp.getBoundarySurfaces(), bpNodeItem);
}
}
private void createBuildingInstallationNodes(Building ab, TreeItem<Renderable> root) {
createInstallationNodes(ab.getBuildingInstallations(), root);
}
private void createInstallationNodes(List<Installation> installations, TreeItem<Renderable> root) {
if (installations.isEmpty()) {
return;
}
AllInstallationsNode allBiNode = new AllInstallationsNode(installations);
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allBiNode);
root.getChildren().add(allBpsTextItem);
for (Installation bi : installations) {
InstallationNode biNode = new InstallationNode(bi);
TreeItem<Renderable> biItem = new TreeItem<>(biNode);
biItem.setExpanded(true);
allBpsTextItem.getChildren().add(biItem);
createGeometryNodes(bi, biItem);
createBoundarySurfaceNodes(bi.getBoundarySurfaces(), biItem);
}
}
private void createBuildingRoomNodes(Building ab, TreeItem<Renderable> root) {
createRoomNodes(ab.getBuildingRooms(), root);
}
private void createRoomNodes(List<? extends AbstractRoom> rooms, TreeItem<Renderable> root) {
if (rooms.isEmpty()) {
return;
}
AllRoomsNode allRoNode = new AllRoomsNode(rooms);
TreeItem<Renderable> allRoNodeTextItem = new TreeItem<>(allRoNode);
root.getChildren().add(allRoNodeTextItem);
for (AbstractRoom room : rooms) {
RoomNode roomNode = new RoomNode(room);
TreeItem<Renderable> roomNodeTextItem = new TreeItem<>(roomNode);
roomNodeTextItem.setExpanded(true);
allRoNodeTextItem.getChildren().add(roomNodeTextItem);
createGeometryNodes(room, roomNodeTextItem);
createBoundarySurfaceNodes(room.getBoundarySurfaceList(), roomNodeTextItem);
}
}
private void createBuildingFurnitureNodes(Building ab, TreeItem<Renderable> root) {
createFurnitureNodes(ab.getBuildingRoomFurnitureList(), root);
}
private void createFurnitureNodes(List<? extends AbstractFurniture> furniture, TreeItem<Renderable> root) {
if (furniture.isEmpty()) {
return;
}
AllFurnitureNode allFnNode = new AllFurnitureNode(furniture);
TreeItem<Renderable> allFnNodeTextItem = new TreeItem<>(allFnNode);
root.getChildren().add(allFnNodeTextItem);
for (AbstractFurniture fn : furniture) {
FurnitureNode fnNode = new FurnitureNode(fn);
TreeItem<Renderable> fnNodeTextItem = new TreeItem<>(fnNode);
fnNodeTextItem.setExpanded(true);
allFnNodeTextItem.getChildren().add(fnNodeTextItem);
createGeometryNodes(fn, fnNodeTextItem);
createBoundarySurfaceNodes(fn.getBoundarySurfaceList(), fnNodeTextItem);
}
}
private void createBoundarySurfaceNodes(List<BoundarySurface> bsList, TreeItem<Renderable> root) {
if (bsList.isEmpty()) {
return;
}
AllBoundarySurfacesNode allBsText = new AllBoundarySurfacesNode(bsList);
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allBsText);
root.getChildren().add(allBpsTextItem);
allBpsTextItem.getChildren().add(new TreeItem<>());
allBpsTextItem.expandedProperty().addListener((obs, old, newV) -> {
if (Boolean.TRUE.equals(newV) && allBpsTextItem.getChildren().size() == 1) {
Platform.runLater(() -> {
allBpsTextItem.getChildren().clear();
for (BoundarySurface bs : bsList) {
BoundarySurfaceNode bsText = new BoundarySurfaceNode(bs);
TreeItem<Renderable> bsTextItem = new TreeItem<>(bsText);
bsTextItem.setExpanded(true);
allBpsTextItem.getChildren().add(bsTextItem);
createGeometryNodes(bs, bsTextItem);
createOpeningNodes(bs.getOpenings(), bsTextItem);
}
updateTree(allBpsTextItem);
});
}
});
}
private void createOpeningNodes(List<Opening> openings, TreeItem<Renderable> root) {
if (openings == null || openings.isEmpty()) {
return;
}
AllOpeningsNode allOpeningsNode = new AllOpeningsNode(openings);
TreeItem<Renderable> allOpeningsItem = new TreeItem<>(allOpeningsNode);
for (Opening o : openings) {
OpeningNode openingNode = new OpeningNode(o);
TreeItem<Renderable> openingsItem = new TreeItem<>(openingNode);
allOpeningsItem.getChildren().add(openingsItem);
}
root.getChildren().add(allOpeningsItem);
}
private void createBuildingPartNodes(Building ab, TreeItem<Renderable> item) {
if (ab.getBuildingParts().isEmpty()) {
return;
}
AllBuildingPartsNode allBpsText = new AllBuildingPartsNode(ab.getBuildingParts());
TreeItem<Renderable> allBpsTextItem = new TreeItem<>(allBpsText);
item.getChildren().add(allBpsTextItem);
for (BuildingPart bp : ab.getBuildingParts()) {
BuildingPartNode bpNode = new BuildingPartNode(bp);
TreeItem<Renderable> bpNodeItem = new TreeItem<>(bpNode);
bpNodeItem.setExpanded(true);
allBpsTextItem.getChildren().add(bpNodeItem);
createGeometryNodes(bp, bpNodeItem);
createBoundarySurfaceNodes(bp.getBoundarySurfaces(), bpNodeItem);
}
}
private void createGeometryNodes(CityObject co, TreeItem<Renderable> item) {
for (Geometry geom : co.getGeometries()) {
GeometryNode geomNode = new GeometryNode(geom);
TreeItem<Renderable> geomItem = new TreeItem<>(geomNode);
item.getChildren().add(geomItem);
}
}
public void startChecks(ValidationConfiguration config, ProgressListener l) {
if (model == null) {
logger.warn(Localization.getText("CityDoctorController.noDatamodel"));
return;
}
if (sourceFile == null) {
logger.warn(Localization.getText("CityDoctorController.noSourceFile"));
return;
}
try {
Platform.runLater(() -> {
mainWindow.getCheckButton().setDisable(true);
mainWindow.getErrorTree().getRoot().getChildren().clear();
mainWindow.getGlobalErrorsView().getItems().clear();
});
config.setNumberOfRoundingPlacesInGlobalParameters(currentConfig.getNumberOfRoundingPlaces());
config.setParserConfig(currentConfig);
currentChecker = new Checker(config, model);
currentChecker.runChecks(l);
Platform.runLater(() -> {
// apply check results to tree views
updateFeatureTrees();
updateTree(mainWindow.getPolygonsView().getRoot());
for (CheckError e : model.getGlobalErrors()) {
if (e instanceof SchematronError se) {
mainWindow.getGlobalErrorsView().getItems().add(se.getErrorIdString());
}
}
renderer.refresh();
mainWindow.getWriteReportButton().setDisable(false);
});
} finally {
Platform.runLater(() -> mainWindow.getCheckButton().setDisable(false));
}
}
void updateFeatureTrees() {
updateTree(mainWindow.getBuildingsView().getRoot());
updateTree(mainWindow.getVegetationView().getRoot());
updateTree(mainWindow.getBridgeView().getRoot());
updateTree(mainWindow.getTerrainView().getRoot());
updateTree(mainWindow.getCityFurnitureView().getRoot());
updateTree(mainWindow.getTransportationView().getRoot());
updateTree(mainWindow.getWaterView().getRoot());
updateTree(mainWindow.getOtherObjectsView().getRoot());
renderer.updateErrors();
}
private void updateTree(TreeItem<Renderable> root) {
if (root == null) {
return;
}
for (TreeItem<Renderable> item : root.getChildren()) {
updateItem(item);
}
}
private void updateItem(TreeItem<Renderable> item) {
if (item.getValue() == null) {
return;
}
item.getValue().refreshTextColor();
for (TreeItem<Renderable> child : item.getChildren()) {
updateItem(child);
}
}
public void writePdfReport(File pdfFile) {
if (currentChecker == null) {
return;
}
currentChecker.writePdfReport(pdfFile.getAbsolutePath());
}
public void writeXmlReport(File xmlFile) {
if (currentChecker == null) {
return;
}
currentChecker.writeXmlReport(xmlFile.getAbsolutePath());
}
private Map<ErrorId, ErrorStat> getErrorStats() {
Map<ErrorId, ErrorStat> stats = new HashMap<>();
addErrorStats(stats, model.getBridges());
addErrorStats(stats, model.getBuildings());
addErrorStats(stats, model.getLand());
addErrorStats(stats, model.getTransportation());
addErrorStats(stats, model.getVegetation());
addErrorStats(stats, model.getWater());
renderer.updateErrors();
}
private void updateTree(TreeItem<Renderable> root) {
if (root == null) {
return;
}
for (TreeItem<Renderable> item : root.getChildren()) {
updateItem(item);
}
}
private void updateItem(TreeItem<Renderable> item) {
if (item.getValue() == null) {
return;
}
item.getValue().refreshTextColor();
for (TreeItem<Renderable> child : item.getChildren()) {
updateItem(child);
}
}
public void writePdfReport(File pdfFile) {
if (currentChecker == null) {
return;
}
currentChecker.writePdfReport(pdfFile.getAbsolutePath());
}
public void writeXmlReport(File xmlFile) {
if (currentChecker == null) {
return;
}
currentChecker.writeXmlReport(xmlFile.getAbsolutePath());
}
private Map<ErrorId, ErrorStat> getErrorStats() {
Map<ErrorId, ErrorStat> stats = new HashMap<>();
addErrorStats(stats, model.getBridges());
addErrorStats(stats, model.getBuildings());
addErrorStats(stats, model.getLand());
addErrorStats(stats, model.getTransportation());
addErrorStats(stats, model.getVegetation());
addErrorStats(stats, model.getWater());
addErrorStats(stats, model.getCityFurniture());
addErrorStats(stats, model.getGenericCityObjects());
return stats;
}
private void addErrorStats(Map<ErrorId, ErrorStat> stats, List<? extends CityObject> cos) {
List<CheckError> errors = new ArrayList<>();
for (CityObject co : cos) {
co.collectContainedErrors(errors);
}
Set<CheckError> filteredErrors = new HashSet<>(errors);
for (CheckError error : filteredErrors) {
ErrorStat stat = stats.computeIfAbsent(error.getErrorId(), ErrorStat::new);
stat.incrementCount();
}
}
public void export(Building b) {
if (model == null) {
return;
}
CityDoctorModel newModel = new CityDoctorModel(model.getParserConfig(), model.getFile());
newModel.setCityModel(new CityModel());
newModel.addBuilding(b);
FileChooser fc = new FileChooser();
fc.getExtensionFilters().add(new ExtensionFilter("CityGML", "*.gml"));
fc.getExtensionFilters().add(new ExtensionFilter("OFF - Object File Format", "*.off"));
File dir = new File(Settings.get(Settings.LAST_OPEN_FOLDER, ""));
if (dir.exists() && dir.isDirectory()) {
fc.setInitialDirectory(dir);
} else {
Settings.set(Settings.LAST_OPEN_FOLDER, "");
}
File f = fc.showSaveDialog(mainWindow.getMainStage());
if (f != null) {
Settings.set(Settings.LAST_OPEN_FOLDER, f.getParent());
try {
newModel.saveAs(f.getAbsolutePath(), true);
} catch (CityDoctorWriteException e) {
logger.error(e);
}
}
}
public void showWorld() {
if (model != null) {
renderer.render(model);
}
}
public void storeModel(File f, boolean saveQualityAde) throws CityDoctorWriteException {
if (model == null) {
return;
}
model.saveAs(f.getAbsolutePath(), saveQualityAde);
}
public void searchFeature(String searchString, FeatureType selectedTab) {
if (model == null || searchString == null || searchString.trim().isEmpty()) {
return;
}
mainWindow.unselectEverything();
resetFeatureChunks();
if (selectedTab == FeatureType.BUILDING) {
List<Building> foundBuildings = filterFeatures(searchString, model.getBuildings());
TreeView<Renderable> buildingsView = mainWindow.getBuildingsView();
TreeItem<Renderable> root = buildingsView.getRoot();
root.getChildren().clear();
buildBuildingTreeFromList(foundBuildings, root);
updateTree(root);
addMoreButtonToBuildingsIfNecessary(foundBuildings, buildingsView, root, buildingChunkNr);
} else {
TreeView<Renderable> view;
List<? extends CityObject> cos;
AtomicInteger chunkCounter;
switch (selectedTab) {
case VEGETATION:
view = mainWindow.getVegetationView();
cos = filterFeatures(searchString, model.getVegetation());
chunkCounter = vegetationChunkNr;
break;
case BRIDGE:
view = mainWindow.getBridgeView();
cos = filterFeatures(searchString, model.getBridges());
chunkCounter = bridgeChunkNr;
break;
case TRANSPORTATION:
view = mainWindow.getTransportationView();
cos = filterFeatures(searchString, model.getTransportation());
chunkCounter = transportationChunkNr;
break;
case WATER:
view = mainWindow.getWaterView();
cos = filterFeatures(searchString, model.getWater());
chunkCounter = waterChunkNr;
break;
case LAND:
view = mainWindow.getTerrainView();
cos = filterFeatures(searchString, model.getLand());
chunkCounter = landChunkNr;
break;
case CITY_FURNITURE:
view = mainWindow.getCityFurnitureView();
cos = filterFeatures(searchString, model.getCityFurniture());
chunkCounter = cityFurnitureChunkNr;
break;
case GENERIC_CITY_OBJECT:
view = mainWindow.getOtherObjectsView();
cos = filterFeatures(searchString, model.getGenericCityObjects());
chunkCounter = otherObjectsChunkNr;
break;
default:
throw new IllegalStateException("Unknown selected feature tab");
}
TreeItem<Renderable> root = view.getRoot();
root.getChildren().clear();
buildTreeFromList(cos, root, chunkCounter);
updateTree(root);
addMoreButtonIfNecessary(cos, view, root, chunkCounter);
}
}
private <T extends CityObject> List<T> filterFeatures(String searchString, List<T> features) {
List<T> foundFeatures = new ArrayList<>();
for (T t : features) {
if (t.getGmlId().getGmlString().contains(searchString)) {
foundFeatures.add(t);
}
}
return foundFeatures;
}
public void resetSearch(FeatureType selectedTab) {
if (model == null) {
return;
}
resetFeatureChunks();
if (selectedTab == FeatureType.BUILDING) {
// buildings are handled differently, because of surface and building
// installations
TreeItem<Renderable> root = mainWindow.getBuildingsView().getRoot();
if (root != null) {
root.getChildren().clear();
buildBuildingTreeFromList(model.getBuildings(), root);
updateTree(root);
addMoreButtonToBuildingsIfNecessary(model.getBuildings(), mainWindow.getBuildingsView(), root,
buildingChunkNr);
}
} else {
TreeView<Renderable> view;
List<? extends CityObject> cos;
AtomicInteger chunkCounter;
switch (selectedTab) {
case VEGETATION:
view = mainWindow.getVegetationView();
cos = model.getVegetation();
chunkCounter = vegetationChunkNr;
break;
case BRIDGE:
view = mainWindow.getBridgeView();
cos = model.getBridges();
chunkCounter = bridgeChunkNr;
break;
case TRANSPORTATION:
view = mainWindow.getTransportationView();
cos = model.getTransportation();
chunkCounter = transportationChunkNr;
break;
case WATER:
view = mainWindow.getWaterView();
cos = model.getWater();
chunkCounter = waterChunkNr;
break;
case LAND:
view = mainWindow.getTerrainView();
cos = model.getLand();
chunkCounter = landChunkNr;
break;
case CITY_FURNITURE:
view = mainWindow.getCityFurnitureView();
cos = model.getCityFurniture();
chunkCounter = cityFurnitureChunkNr;
break;
case GENERIC_CITY_OBJECT:
view = mainWindow.getOtherObjectsView();
cos = model.getGenericCityObjects();
chunkCounter = otherObjectsChunkNr;
break;
default:
throw new IllegalStateException("Unknown selected feature tab");
}
TreeItem<Renderable> root = view.getRoot();
root.getChildren().clear();
buildTreeFromList(cos, root, chunkCounter);
updateTree(root);
addMoreButtonIfNecessary(cos, view, root, chunkCounter);
}
}
public Series<String, Number> createErrorSeries() {
Series<String, Number> series = new Series<>();
if (model == null) {
return series;
}
Map<ErrorId, ErrorStat> errorStats = getErrorStats();
for (ErrorStat es : errorStats.values()) {
series.getData().add(new Data<>(es.getErrorId().toString(), es.getCount()));
}
return series;
}
public String getFileName() {
return model.getFileName();
}
public void fillTreeViewWithErrorBuildings() {
TreeView<Renderable> buildingsView = mainWindow.getBuildingsView();
TreeItem<Renderable> root = buildingsView.getRoot();
if (model == null) {
return;
}
if (root == null) {
return;
}
mainWindow.resetSearchBar();
root.getChildren().clear();
List<Building> errorBuildings = new ArrayList<>();
for (Building b : model.getBuildings()) {
if (b.containsAnyError()) {
errorBuildings.add(b);
}
}
resetFeatureChunks();
buildBuildingTreeFromList(errorBuildings, root);
updateTree(root);
addMoreButtonToBuildingsIfNecessary(errorBuildings, buildingsView, root, buildingChunkNr);
}
public void fillTreeViewWithErrorVegetation() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getVegetationView(), model.getVegetation(), vegetationChunkNr);
}
public void fillTreeViewWithErrorBridges() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getBridgeView(), model.getBridges(), bridgeChunkNr);
}
public void fillTreeViewWithErrorLand() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getTerrainView(), model.getLand(), landChunkNr);
}
public void fillTreeViewWithErrorCityFurniture(){
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getCityFurnitureView(),
model.getCityFurniture(), cityFurnitureChunkNr);
}
public void fillTreeViewWithErrorOtherObjects(){
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getOtherObjectsView(),
model.getGenericCityObjects(), otherObjectsChunkNr);
}
public void fillTreeViewWithErrorTransportation() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getTransportationView(), model.getTransportation(),
transportationChunkNr);
}
public void fillTreeViewWithErrorWater() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getWaterView(), model.getWater(), waterChunkNr);
}
private void fillTreeViewWithErrorCityObjects(TreeView<Renderable> treeView, List<? extends CityObject> objects,
AtomicInteger chunkCounter) {
TreeItem<Renderable> root = treeView.getRoot();
if (root == null) {
return;
}
mainWindow.resetSearchBar();
root.getChildren().clear();
List<CityObject> errorObjects = new ArrayList<>();
for (CityObject co : objects) {
if (co.containsAnyError()) {
errorObjects.add(co);
}
}
buildTreeFromList(errorObjects, root, chunkCounter);
updateTree(root);
addMoreButtonIfNecessary(errorObjects, treeView, root, chunkCounter);
}
public void fillTreeViewFromFeatures(FeatureType selectedTab) {
if (model == null) {
return;
}
mainWindow.resetSearchBar();
switch (selectedTab) {
case BUILDING:
buildBuildings(model);
updateTree(mainWindow.getBuildingsView().getRoot());
break;
case VEGETATION:
buildVegetation(model.getVegetation());
updateTree(mainWindow.getVegetationView().getRoot());
break;
case BRIDGE:
buildBridges(model);
updateTree(mainWindow.getBridgeView().getRoot());
break;
case LAND:
buildLand(model);
updateTree(mainWindow.getTerrainView().getRoot());
break;
case TRANSPORTATION:
buildTransportation(model.getTransportation());
updateTree(mainWindow.getTransportationView().getRoot());
break;
case WATER:
buildWater(model);
updateTree(mainWindow.getWaterView().getRoot());
break;
case CITY_FURNITURE:
buildCityFurniture(model);
updateTree(mainWindow.getCityFurnitureView().getRoot());
break;
return stats;
}
private void addErrorStats(Map<ErrorId, ErrorStat> stats, List<? extends CityObject> cos) {
List<CheckError> errors = new ArrayList<>();
for (CityObject co : cos) {
co.collectContainedErrors(errors);
}
Set<CheckError> filteredErrors = new HashSet<>(errors);
for (CheckError error : filteredErrors) {
ErrorStat stat = stats.computeIfAbsent(error.getErrorId(), ErrorStat::new);
stat.incrementCount();
}
}
public void export(Building b) {
if (model == null) {
return;
}
CityDoctorModel newModel = new CityDoctorModel(model.getParserConfig(), model.getFile());
newModel.setCityModel(new CityModel());
newModel.addBuilding(b);
FileChooser fc = new FileChooser();
fc.getExtensionFilters().add(new ExtensionFilter("CityGML", "*.gml"));
fc.getExtensionFilters().add(new ExtensionFilter("OFF - Object File Format", "*.off"));
File dir = new File(Settings.get(Settings.LAST_OPEN_FOLDER, ""));
if (dir.exists() && dir.isDirectory()) {
fc.setInitialDirectory(dir);
} else {
Settings.set(Settings.LAST_OPEN_FOLDER, "");
}
File f = fc.showSaveDialog(mainWindow.getMainStage());
if (f != null) {
Settings.set(Settings.LAST_OPEN_FOLDER, f.getParent());
try {
newModel.saveAs(f.getAbsolutePath(), true);
} catch (CityDoctorWriteException e) {
logger.error(e);
}
}
}
public void showWorld() {
if (model != null) {
renderer.render(model);
}
}
public void storeModel(File f, boolean saveQualityAde) throws CityDoctorWriteException {
if (model == null) {
return;
}
model.saveAs(f.getAbsolutePath(), saveQualityAde);
}
public void searchFeature(String searchString, FeatureType selectedTab) {
if (model == null || searchString == null || searchString.trim().isEmpty()) {
return;
}
mainWindow.unselectEverything();
resetFeatureChunks();
if (selectedTab == FeatureType.BUILDING) {
List<Building> foundBuildings = filterFeatures(searchString, model.getBuildings());
TreeView<Renderable> buildingsView = mainWindow.getBuildingsView();
TreeItem<Renderable> root = buildingsView.getRoot();
root.getChildren().clear();
buildBuildingTreeFromList(foundBuildings, root);
updateTree(root);
addMoreButtonToBuildingsIfNecessary(foundBuildings, buildingsView, root, buildingChunkNr);
} else {
TreeView<Renderable> view;
List<? extends CityObject> cos;
AtomicInteger chunkCounter;
switch (selectedTab) {
case VEGETATION:
view = mainWindow.getVegetationView();
cos = filterFeatures(searchString, model.getVegetation());
chunkCounter = vegetationChunkNr;
break;
case BRIDGE:
view = mainWindow.getBridgeView();
cos = filterFeatures(searchString, model.getBridges());
chunkCounter = bridgeChunkNr;
break;
case TRANSPORTATION:
view = mainWindow.getTransportationView();
cos = filterFeatures(searchString, model.getTransportation());
chunkCounter = transportationChunkNr;
break;
case WATER:
view = mainWindow.getWaterView();
cos = filterFeatures(searchString, model.getWater());
chunkCounter = waterChunkNr;
break;
case LAND:
view = mainWindow.getTerrainView();
cos = filterFeatures(searchString, model.getLand());
chunkCounter = landChunkNr;
break;
case CITY_FURNITURE:
view = mainWindow.getCityFurnitureView();
cos = filterFeatures(searchString, model.getCityFurniture());
chunkCounter = cityFurnitureChunkNr;
break;
case GENERIC_CITY_OBJECT:
view = mainWindow.getOtherObjectsView();
cos = filterFeatures(searchString, model.getGenericCityObjects());
chunkCounter = otherObjectsChunkNr;
break;
default:
throw new IllegalStateException("Unknown selected feature tab");
}
TreeItem<Renderable> root = view.getRoot();
root.getChildren().clear();
buildTreeFromList(cos, root, chunkCounter);
updateTree(root);
addMoreButtonIfNecessary(cos, view, root, chunkCounter);
}
}
private <T extends CityObject> List<T> filterFeatures(String searchString, List<T> features) {
List<T> foundFeatures = new ArrayList<>();
for (T t : features) {
if (t.getGmlId().getGmlString().contains(searchString)) {
foundFeatures.add(t);
}
}
return foundFeatures;
}
public void resetSearch(FeatureType selectedTab) {
if (model == null) {
return;
}
resetFeatureChunks();
if (selectedTab == FeatureType.BUILDING) {
// buildings are handled differently, because of surface and building
// installations
TreeItem<Renderable> root = mainWindow.getBuildingsView().getRoot();
if (root != null) {
root.getChildren().clear();
buildBuildingTreeFromList(model.getBuildings(), root);
updateTree(root);
addMoreButtonToBuildingsIfNecessary(model.getBuildings(), mainWindow.getBuildingsView(), root,
buildingChunkNr);
}
} else {
TreeView<Renderable> view;
List<? extends CityObject> cos;
AtomicInteger chunkCounter;
switch (selectedTab) {
case VEGETATION:
view = mainWindow.getVegetationView();
cos = model.getVegetation();
chunkCounter = vegetationChunkNr;
break;
case BRIDGE:
view = mainWindow.getBridgeView();
cos = model.getBridges();
chunkCounter = bridgeChunkNr;
break;
case TRANSPORTATION:
view = mainWindow.getTransportationView();
cos = model.getTransportation();
chunkCounter = transportationChunkNr;
break;
case WATER:
view = mainWindow.getWaterView();
cos = model.getWater();
chunkCounter = waterChunkNr;
break;
case LAND:
view = mainWindow.getTerrainView();
cos = model.getLand();
chunkCounter = landChunkNr;
break;
case CITY_FURNITURE:
view = mainWindow.getCityFurnitureView();
cos = model.getCityFurniture();
chunkCounter = cityFurnitureChunkNr;
break;
case GENERIC_CITY_OBJECT:
view = mainWindow.getOtherObjectsView();
cos = model.getGenericCityObjects();
chunkCounter = otherObjectsChunkNr;
break;
default:
throw new IllegalStateException("Unknown selected feature tab");
}
TreeItem<Renderable> root = view.getRoot();
root.getChildren().clear();
buildTreeFromList(cos, root, chunkCounter);
updateTree(root);
addMoreButtonIfNecessary(cos, view, root, chunkCounter);
}
}
public Series<String, Number> createErrorSeries() {
Series<String, Number> series = new Series<>();
if (model == null) {
return series;
}
Map<ErrorId, ErrorStat> errorStats = getErrorStats();
for (ErrorStat es : errorStats.values()) {
series.getData().add(new Data<>(es.getErrorId().toString(), es.getCount()));
}
return series;
}
public String getFileName() {
return model.getFileName();
}
public void fillTreeViewWithErrorBuildings() {
TreeView<Renderable> buildingsView = mainWindow.getBuildingsView();
TreeItem<Renderable> root = buildingsView.getRoot();
if (model == null) {
return;
}
if (root == null) {
return;
}
mainWindow.resetSearchBar();
root.getChildren().clear();
List<Building> errorBuildings = new ArrayList<>();
for (Building b : model.getBuildings()) {
if (b.containsAnyError()) {
errorBuildings.add(b);
}
}
resetFeatureChunks();
buildBuildingTreeFromList(errorBuildings, root);
updateTree(root);
addMoreButtonToBuildingsIfNecessary(errorBuildings, buildingsView, root, buildingChunkNr);
}
public void fillTreeViewWithErrorVegetation() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getVegetationView(), model.getVegetation(), vegetationChunkNr);
}
public void fillTreeViewWithErrorBridges() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getBridgeView(), model.getBridges(), bridgeChunkNr);
}
public void fillTreeViewWithErrorLand() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getTerrainView(), model.getLand(), landChunkNr);
}
public void fillTreeViewWithErrorCityFurniture() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getCityFurnitureView(),
model.getCityFurniture(), cityFurnitureChunkNr);
}
public void fillTreeViewWithErrorOtherObjects() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getOtherObjectsView(),
model.getGenericCityObjects(), otherObjectsChunkNr);
}
public void fillTreeViewWithErrorTransportation() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getTransportationView(), model.getTransportation(),
transportationChunkNr);
}
public void fillTreeViewWithErrorWater() {
if (model == null) {
return;
}
fillTreeViewWithErrorCityObjects(mainWindow.getWaterView(), model.getWater(), waterChunkNr);
}
private void fillTreeViewWithErrorCityObjects(TreeView<Renderable> treeView, List<? extends CityObject> objects,
AtomicInteger chunkCounter) {
TreeItem<Renderable> root = treeView.getRoot();
if (root == null) {
return;
}
mainWindow.resetSearchBar();
root.getChildren().clear();
List<CityObject> errorObjects = new ArrayList<>();
for (CityObject co : objects) {
if (co.containsAnyError()) {
errorObjects.add(co);
}
}
buildTreeFromList(errorObjects, root, chunkCounter);
updateTree(root);
addMoreButtonIfNecessary(errorObjects, treeView, root, chunkCounter);
}
public void fillTreeViewFromFeatures(FeatureType selectedTab) {
if (model == null) {
return;
}
mainWindow.resetSearchBar();
switch (selectedTab) {
case BUILDING:
buildBuildings(model);
updateTree(mainWindow.getBuildingsView().getRoot());
break;
case VEGETATION:
buildVegetation(model.getVegetation());
updateTree(mainWindow.getVegetationView().getRoot());
break;
case BRIDGE:
buildBridges(model);
updateTree(mainWindow.getBridgeView().getRoot());
break;
case LAND:
buildLand(model);
updateTree(mainWindow.getTerrainView().getRoot());
break;
case TRANSPORTATION:
buildTransportation(model.getTransportation());
updateTree(mainWindow.getTransportationView().getRoot());
break;
case WATER:
buildWater(model);
updateTree(mainWindow.getWaterView().getRoot());
break;
case CITY_FURNITURE:
buildCityFurniture(model);
updateTree(mainWindow.getCityFurnitureView().getRoot());
break;
case GENERIC_CITY_OBJECT:
buildOtherCityObjects(model);
updateTree(mainWindow.getOtherObjectsView().getRoot());
break;
default:
throw new IllegalStateException();
}
}
public CityDoctorModel getModel() {
return model;
}
public void showView(View v) {
v.fireOnShowEvent(model, currentChecker, mainWindow);
}
public void askAndSave() {
boolean saveWithQualityAde = true;
if (model.isValidated()) {
ButtonType yesBtn = ButtonType.YES;
ButtonType noBtn = ButtonType.NO;
Dialog<ButtonType> dialog = new Dialog<>();
dialog.setTitle("Save with QualityADE?");
dialog.setContentText("Save with QualityADE information?");
dialog.getDialogPane().getButtonTypes().add(yesBtn);
dialog.getDialogPane().getButtonTypes().add(noBtn);
Optional<ButtonType> result = dialog.showAndWait();
if (result.isPresent() && result.get() == ButtonType.NO) {
saveWithQualityAde = false;
}
}
FileChooser fc = new FileChooser();
fc.getExtensionFilters().add(new ExtensionFilter("CityGML", "*.gml"));
fc.getExtensionFilters().add(new ExtensionFilter("OFF - Object File Format", "*.off"));
File dir = new File(Settings.get(Settings.LAST_OPEN_FOLDER, ""));
if (dir.exists() && dir.isDirectory()) {
fc.setInitialDirectory(dir);
} else {
Settings.set(Settings.LAST_OPEN_FOLDER, "");
}
File f = fc.showSaveDialog(mainWindow.getMainStage());
if (f != null) {
Settings.set(Settings.LAST_OPEN_FOLDER, f.getParent());
try {
storeModel(f, saveWithQualityAde);
} catch (CityDoctorWriteException e) {
mainWindow.showExceptionDialog(e);
}
}
}
public void delete(TreeItem<Renderable> selectedItem) {
if (model == null) {
return;
}
Renderable render = selectedItem.getValue();
if (render instanceof BuildingNode node) {
model.getBuildings().remove(node.getBuilding());
mainWindow.getBuildingsView().getRoot().getChildren().remove(selectedItem);
}
}
public void errorFilterIndexChanged(Number newV) {
mainWindow.getMeshGroup().getChildren().clear();
mainWindow.unselectEverything();
if (newV.intValue() == 0) {
fillTreeViewFromFeatures(mainWindow.getSelectedTab());
} else if (newV.intValue() == 1) {
switch (mainWindow.getSelectedTab()) {
case BUILDING:
fillTreeViewWithErrorBuildings();
break;
case VEGETATION:
fillTreeViewWithErrorVegetation();
break;
case BRIDGE:
fillTreeViewWithErrorBridges();
break;
case LAND:
fillTreeViewWithErrorLand();
break;
case TRANSPORTATION:
fillTreeViewWithErrorTransportation();
break;
case WATER:
fillTreeViewWithErrorWater();
break;
case CITY_FURNITURE:
fillTreeViewWithErrorCityFurniture();
break;
default:
throw new IllegalStateException();
}
}
public CityDoctorModel getModel() {
return model;
}
public void showView(View v) {
v.fireOnShowEvent(model, currentChecker, mainWindow);
}
public void askAndSave() {
boolean saveWithQualityAde = true;
if (model.isValidated()) {
ButtonType yesBtn = ButtonType.YES;
ButtonType noBtn = ButtonType.NO;
Dialog<ButtonType> dialog = new Dialog<>();
dialog.setTitle("Save with QualityADE?");
dialog.setContentText("Save with QualityADE information?");
dialog.getDialogPane().getButtonTypes().add(yesBtn);
dialog.getDialogPane().getButtonTypes().add(noBtn);
Optional<ButtonType> result = dialog.showAndWait();
if (result.isPresent() && result.get() == ButtonType.NO) {
saveWithQualityAde = false;
}
}
FileChooser fc = new FileChooser();
fc.getExtensionFilters().add(new ExtensionFilter("CityGML", "*.gml"));
fc.getExtensionFilters().add(new ExtensionFilter("OFF - Object File Format", "*.off"));
File dir = new File(Settings.get(Settings.LAST_OPEN_FOLDER, ""));
if (dir.exists() && dir.isDirectory()) {
fc.setInitialDirectory(dir);
} else {
Settings.set(Settings.LAST_OPEN_FOLDER, "");
}
File f = fc.showSaveDialog(mainWindow.getMainStage());
if (f != null) {
Settings.set(Settings.LAST_OPEN_FOLDER, f.getParent());
try {
storeModel(f, saveWithQualityAde);
} catch (CityDoctorWriteException e) {
mainWindow.showExceptionDialog(e);
}
}
}
public void delete(TreeItem<Renderable> selectedItem) {
if (model == null) {
return;
}
Renderable render = selectedItem.getValue();
if (render instanceof BuildingNode node) {
model.getBuildings().remove(node.getBuilding());
mainWindow.getBuildingsView().getRoot().getChildren().remove(selectedItem);
}
}
public void errorFilterIndexChanged(Number newV) {
mainWindow.getMeshGroup().getChildren().clear();
mainWindow.unselectEverything();
if (newV.intValue() == 0) {
fillTreeViewFromFeatures(mainWindow.getSelectedTab());
} else if (newV.intValue() == 1) {
switch (mainWindow.getSelectedTab()) {
case BUILDING:
fillTreeViewWithErrorBuildings();
break;
case VEGETATION:
fillTreeViewWithErrorVegetation();
break;
case BRIDGE:
fillTreeViewWithErrorBridges();
break;
case LAND:
fillTreeViewWithErrorLand();
break;
case TRANSPORTATION:
fillTreeViewWithErrorTransportation();
break;
case WATER:
fillTreeViewWithErrorWater();
break;
case CITY_FURNITURE:
fillTreeViewWithErrorCityFurniture();
break;
case GENERIC_CITY_OBJECT:
fillTreeViewWithErrorOtherObjects();
break;
default:
throw new IllegalStateException("Unknown selected feature tab: " + mainWindow.getSelectedTab());
}
} else {
throw new IllegalStateException("Unknown filter index selected: " + newV);
}
}
public ParserConfiguration getCurrentConfig() {
return currentConfig;
}
default:
throw new IllegalStateException("Unknown selected feature tab: " + mainWindow.getSelectedTab());
}
} else {
throw new IllegalStateException("Unknown filter index selected: " + newV);
}
}
public ParserConfiguration getCurrentConfig() {
return currentConfig;
}
}
......@@ -80,937 +80,937 @@ import javafx.stage.Stage;
public class MainWindow extends Application {
private static final Logger logger = LogManager.getLogger(MainWindow.class);
private static final Logger logger = LogManager.getLogger(MainWindow.class);
private static final double CAMERA_TRANSLATE_Z = -100.0;
private static final double CAMERA_INITIAL_X_ANGLE = 20.0;
private static final double CAMERA_INITIAL_Y_ANGLE = 120.0;
private static final double CAMERA_TRANSLATE_Z = -100.0;
private static final double CAMERA_INITIAL_X_ANGLE = 20.0;
private static final double CAMERA_INITIAL_Y_ANGLE = 120.0;
@FXML
private TreeView<Renderable> buildingsView;
@FXML
private TreeView<Renderable> buildingsView;
@FXML
private TreeView<Renderable> vegetationView;
@FXML
private TreeView<Renderable> vegetationView;
@FXML
private TreeView<Renderable> transView;
@FXML
private TreeView<Renderable> transView;
@FXML
private TreeView<Renderable> bridgeView;
@FXML
private TreeView<Renderable> bridgeView;
@FXML
private TreeView<Renderable> waterView;
@FXML
private TreeView<Renderable> waterView;
@FXML
private TreeView<Renderable> terrainView;
@FXML
private TreeView<Renderable> terrainView;
@FXML
private TreeView<Renderable> cityFurnitureView;
@FXML
private TreeView<Renderable> cityFurnitureView;
@FXML
TreeView<Renderable> otherObjectsView;
@FXML
TreeView<Renderable> otherObjectsView;
@FXML
private TreeView<Renderable> polygonView;
@FXML
private TreeView<Renderable> polygonView;
@FXML
private TreeView<Renderable> edgeView;
@FXML
private TreeView<Renderable> edgeView;
@FXML
private TreeView<Renderable> vertexView;
@FXML
private TreeView<Renderable> vertexView;
@FXML
private Pane meshView;
@FXML
private Pane meshView;
@FXML
private SplitPane mainContainer;
@FXML
private SplitPane mainContainer;
@FXML
private TabPane detailsTabPane;
@FXML
private TabPane detailsTabPane;
@FXML
private TabPane featurePane;
@FXML
private TabPane featurePane;
@FXML
private TextField searchField;
@FXML
private TextField searchField;
@FXML
private TreeView<Renderable> errorView;
@FXML
private TreeView<Renderable> errorView;
@FXML
private Button clearBtn;
@FXML
private Button clearBtn;
@FXML
private Button searchBtn;
@FXML
private Button searchBtn;
@FXML
private ToggleButton allButton;
@FXML
private ToggleButton allButton;
@FXML
private ToggleButton errorButton;
@FXML
private ToggleButton errorButton;
@FXML
private ComboBox<String> showCityObjectsCombo;
@FXML
private ComboBox<String> showCityObjectsCombo;
@FXML
private HBox viewPane;
@FXML
private HBox viewPane;
@FXML
private ListView<String> globalErrorsView;
@FXML
private ListView<String> globalErrorsView;
@FXML
private BorderPane mainPane;
@FXML
private BorderPane mainPane;
@FXML
private HBox viewButtonBox;
@FXML
private HBox viewButtonBox;
@FXML
private ComboBox<Locale> languageSelector;
@FXML
private ComboBox<Locale> languageSelector;
@FXML
private Tab buildingsTab;
@FXML
private Tab buildingsTab;
@FXML
private Tab vegetationTab;
@FXML
private Tab vegetationTab;
@FXML
private Tab transportationTab;
@FXML
private Tab transportationTab;
@FXML
private Tab bridgeTab;
@FXML
private Tab bridgeTab;
@FXML
private Tab waterTab;
@FXML
private Tab waterTab;
@FXML
private Tab terrainTab;
@FXML
private Tab terrainTab;
@FXML
private Tab cityFurnitureTab;
@FXML
private Tab cityFurnitureTab;
@FXML
private Tab otherObjectsTab;
@FXML
private Tab otherObjectsTab;
@FXML
private Tab errorsTab;
@FXML
private Tab errorsTab;
@FXML
private Tab polygonsTab;
@FXML
private Tab polygonsTab;
@FXML
private Tab edgesTab;
@FXML
private Tab edgesTab;
@FXML
private Tab verticesTab;
@FXML
private Tab verticesTab;
@FXML
private Tab logTab;
@FXML
private Tab logTab;
@FXML
private Tab globalErrorsTab;
@FXML
private Tab globalErrorsTab;
@FXML
private Label viewLabel;
@FXML
private Label viewLabel;
@FXML
private Label showLabel;
@FXML
private Label showLabel;
@FXML
private Label searchLabel;
@FXML
private Label searchLabel;
@FXML
private Label memoryLabel;
@FXML
private Label memoryLabel;
@FXML
private ProgressBar memoryBar;
@FXML
private ProgressBar memoryBar;
@FXML
private Label memoryConsumptionLabel;
@FXML
private Label memoryConsumptionLabel;
@FXML
private Label availableLabel;
@FXML
private Label availableLabel;
private Group meshGroup;
private Group meshGroup;
private Group world;
private PerspectiveCamera camera;
private Rotate cameraXRotation = new Rotate();
private Rotate cameraZRotation = new Rotate();
private Group world;
private PerspectiveCamera camera;
private Rotate cameraXRotation = new Rotate();
private Rotate cameraZRotation = new Rotate();
private double dragX;
private double dragY;
private double dragX;
private double dragY;
private double cameraXRot = CAMERA_INITIAL_X_ANGLE;
private double cameraYRot = CAMERA_INITIAL_Y_ANGLE;
private double translateZ = CAMERA_TRANSLATE_Z;
private double[] clickStart = new double[2];
private double cameraXRot = CAMERA_INITIAL_X_ANGLE;
private double cameraYRot = CAMERA_INITIAL_Y_ANGLE;
private double translateZ = CAMERA_TRANSLATE_Z;
private double[] clickStart = new double[2];
@FXML
private TextArea logArea;
@FXML
private ToolBar viewBar;
private Stage stage;
private ExceptionDialog exceptionDialog;
private Renderer renderer;
private CityDoctorController controller;
private HighlightController highlightController;
private FeatureType selectedTab = FeatureType.BUILDING;
private MainToolBar mainToolBar;
private SubScene geomScene;
private static boolean loadFileAtStartup = false;
private static String inputFile;
private static String xmlOutput;
private static String pdfOutput;
private static ValidationConfiguration config;
private VertexClickHandler clickHandler;
private ChangeListener<Number> filterChangeListener;
public static void main(String[] args) {
setLocaleFromSettings();
ArgumentParser argParser = new ArgumentParser(args);
inputFile = CityDoctorValidation.getInputFile(argParser, true);
if (inputFile != null) {
loadFileAtStartup = true;
xmlOutput = CityDoctorValidation.getXmlOutput(argParser);
pdfOutput = CityDoctorValidation.getPdfOutput(argParser);
try {
config = CityDoctorValidation.getValidationConfig(argParser, true);
} catch (FileNotFoundException e) {
Platform.runLater(() -> {
List<String> configFiles = argParser.getValues("config");
Alert alert = new Alert(AlertType.ERROR);
alert.setContentText(Localization.getText("MainWindow.missingConfig") + configFiles);
alert.showAndWait();
});
System.exit(4);
}
}
Application.launch(args);
}
private static void setLocaleFromSettings() {
String localeString = Settings.get(Settings.LANGUAGE);
if (localeString != null) {
try {
Locale loc = new Locale(localeString);
Locale.setDefault(loc);
} catch (Exception e) {
logger.warn("Could not set language to {}, using system language", localeString);
}
}
}
@Override
public void start(Stage stage) throws IOException {
this.stage = stage;
stage.getIcons().add(new Image(MainWindow.class.getResourceAsStream("icons/citydoctor_logo.png")));
FXMLLoader loader = new FXMLLoader(MainWindow.class.getResource("MainWindow.fxml"));
loader.setController(this);
BorderPane bp = loader.load();
highlightController = new HighlightController(world);
renderer = new Renderer(this, highlightController);
clickHandler = new VertexClickHandler(errorView, renderer, stage);
controller = new CityDoctorController(this, highlightController, renderer);
mainToolBar = new MainToolBar(stage, controller, featurePane, renderer, this);
viewPane.getChildren().add(mainToolBar.getToolBar());
HBox.setHgrow(mainToolBar.getToolBar(), Priority.ALWAYS);
ValidationView valView = new ValidationView(this, controller);
ViewRegistration.registerView(valView);
setupViews(valView);
createLanguageSelector();
setLabelsInCorrectLanguage();
Scene scene = new Scene(bp, 1280, 800);
createDropTarget(scene, valView);
String version = Localization.getText(Localization.VERSION);
stage.setTitle("CityDoctor " + version);
stage.setScene(scene);
stage.show();
checkForStartupLoading();
memoryBar.setOnMouseClicked(me -> System.gc());
Timer timer = new Timer(true);
// check memory every second
TimerTask task = new TimerTask() {
Runtime runtime = Runtime.getRuntime();
@Override
public void run() {
long totalMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
long usedMemory = totalMemory - freeMemory;
double percentage = usedMemory / (double) totalMemory;
if (totalMemory / 1024 / 1024 >= 1024) {
// gb
double totalMemoryGb = totalMemory / (1024d * 1024d * 1024d);
double usedMemoryGb = usedMemory / (1024d * 1024d * 1024d);
String memoryString = String.format("%.1f GB / %.1f GB", usedMemoryGb, totalMemoryGb);
Platform.runLater(() -> {
memoryConsumptionLabel.setText(memoryString);
memoryBar.setProgress(percentage);
});
} else if (totalMemory / 1024 >= 1024) {
// mb
double totalMemoryMb = totalMemory / (1024d * 1024d);
double usedMemoryMb = usedMemory / (1024d * 1024d);
String memoryString = String.format("%.1f MB / %.1f MB", usedMemoryMb, totalMemoryMb);
Platform.runLater(() -> {
memoryConsumptionLabel.setText(memoryString);
memoryBar.setProgress(percentage);
});
}
}
};
timer.schedule(task, 0, 1000);
}
private void createDropTarget(Scene scene, ValidationView valView) {
setDragOverInteraction(scene, valView);
scene.setOnDragDropped(event -> {
if (ViewRegistration.getCurrentActiveView() != valView) {
return;
}
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasFiles() && db.getFiles().size() == 1) {
File f = db.getFiles().get(0);
Thread t = new Thread(() -> {
try {
controller.loadCityGml(f.getAbsolutePath(), 8, (ProgressListener) null, false);
} catch (Exception e) {
if (logger.isErrorEnabled()) {
logger.error(Localization.getText("OpenFileDialog.loadFailed"), e);
}
Platform.runLater(() -> {
ExceptionDialog exDialog = new ExceptionDialog();
exDialog.show(e);
});
}
});
t.start();
success = true;
}
// let the source know whether the string was successfully transferred and used
event.setDropCompleted(success);
event.consume();
});
}
private void setDragOverInteraction(Scene scene, ValidationView valView) {
scene.setOnDragOver(event -> {
if (ViewRegistration.getCurrentActiveView() != valView) {
return;
}
if (event.getGestureSource() != scene && event.getDragboard().hasFiles()
&& event.getDragboard().getFiles().size() == 1) {
// allow for both copying and moving, whatever user chooses
event.acceptTransferModes(TransferMode.LINK);
}
event.consume();
});
}
private void setLabelsInCorrectLanguage() {
buildingsTab.setText(Localization.getText("MainWindow.buildingsTab"));
vegetationTab.setText(Localization.getText("MainWindow.vegetationTab"));
transportationTab.setText(Localization.getText("MainWindow.transportationTab"));
bridgeTab.setText(Localization.getText("MainWindow.bridgeTab"));
waterTab.setText(Localization.getText("MainWindow.waterTab"));
terrainTab.setText(Localization.getText("MainWindow.terrainTab"));
cityFurnitureTab.setText(Localization.getText("MainWindow.cityfurnitureTab"));
otherObjectsTab.setText(Localization.getText("MainWindow.otherObjectsTab"));
viewLabel.setText(Localization.getText("MainWindow.viewLabel"));
showLabel.setText(Localization.getText("MainWindow.showLabel"));
searchLabel.setText(Localization.getText("MainWindow.searchLabel"));
searchBtn.setText(Localization.getText("MainWindow.searchBtn"));
clearBtn.setText(Localization.getText("MainWindow.clearBtn"));
errorsTab.setText(Localization.getText("MainWindow.errorsTab"));
polygonsTab.setText(Localization.getText("MainWindow.polygonsTab"));
edgesTab.setText(Localization.getText("MainWindow.edgesTab"));
verticesTab.setText(Localization.getText("MainWindow.verticesTab"));
logTab.setText(Localization.getText("MainWindow.logTab"));
globalErrorsTab.setText(Localization.getText("MainWindow.globalErrorsTab"));
memoryLabel.setText(Localization.getText("MainWindow.memoryLabel"));
availableLabel.setText(String.format("%s %.1f GB", Localization.getText("MainWindow.availableLabel"),
Runtime.getRuntime().maxMemory() / 1024d / 1024d / 1024d));
}
private void createLanguageSelector() {
languageSelector.setButtonCell(new LanguageSelectorCell());
languageSelector.setCellFactory(view -> new LanguageSelectorCell());
languageSelector.getItems().add(Locale.GERMAN);
languageSelector.getItems().add(Locale.ENGLISH);
if (Locale.getDefault().getLanguage().equals(Locale.GERMAN.getLanguage())) {
languageSelector.getSelectionModel().select(Locale.GERMAN);
} else if (Locale.getDefault().getLanguage().equals(Locale.ENGLISH.getLanguage())) {
languageSelector.getSelectionModel().select(Locale.ENGLISH);
} else {
languageSelector.getSelectionModel().select(Locale.ENGLISH);
Settings.set(Settings.LANGUAGE, Locale.ENGLISH.getLanguage());
}
languageSelector.getSelectionModel().selectedItemProperty().addListener((obs, oldV, newV) -> {
Alert alert = new Alert(AlertType.CONFIRMATION, Localization.getText("MainWindow.languageChange"),
ButtonType.OK);
alert.showAndWait();
Settings.set(Settings.LANGUAGE, newV.getLanguage());
});
}
private void checkForStartupLoading() {
if (loadFileAtStartup) {
logger.info(Localization.getText("MainWindow.loadGivenFile"));
Thread t = new Thread(() -> {
try {
// wait a bit for the gui to show
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
try {
controller.loadCityGml(inputFile, config.getNumberOfRoundingPlaces(), null,
config.isXmlValidation());
logger.info(Localization.getText("MainWindow.finishedLoading"));
logger.info(Localization.getText("MainWindow.checking"));
controller.startChecks(config, null);
if (xmlOutput != null) {
logger.info(Localization.getText("MainWindow.writeXml"));
controller.writeXmlReport(new File(xmlOutput));
logger.info(Localization.getText("MainWindow.finishedXml"));
}
if (pdfOutput != null) {
logger.info(Localization.getText("MainWindow.writePdf"));
controller.writePdfReport(new File(pdfOutput));
logger.info(Localization.getText("MainWindow.finishedPdf"));
}
} catch (CityGmlParseException | InvalidGmlFileException e) {
logger.error(Localization.getText("MainWindow.loadFailed"), e.getMessage());
}
});
t.start();
}
}
private void setupViews(ValidationView valView) {
ToggleGroup group = new ToggleGroup();
for (View v : ViewRegistration.getRegisteredViews()) {
RadioButton radioButton = new RadioButton();
radioButton.getStyleClass().remove("radio-button");
radioButton.getStyleClass().add("toggle-button");
ImageView view = new ImageView(v.getViewLogo());
view.setFitHeight(32);
view.setFitWidth(32);
radioButton.setGraphic(view);
group.getToggles().add(radioButton);
if (v == valView) {
ViewRegistration.setCurrentActiveView(valView);
group.selectToggle(radioButton);
}
viewButtonBox.getChildren().add(radioButton);
radioButton.selectedProperty().addListener((obs, oldV, newV) -> {
if (Boolean.TRUE.equals(newV)) {
showView(v);
ViewRegistration.setCurrentActiveView(v);
} else {
v.onHide();
}
});
}
}
private void showView(View v) {
viewPane.getChildren().clear();
controller.showView(v);
Optional<HBox> toolbar = v.getToolbar();
if (toolbar.isPresent()) {
viewPane.getChildren().add(toolbar.get());
HBox.setHgrow(toolbar.get(), Priority.ALWAYS);
} else {
HBox placeHolderToolbar = new HBox();
placeHolderToolbar.setMaxHeight(Double.MAX_VALUE);
viewPane.getChildren().add(placeHolderToolbar);
HBox.setHgrow(placeHolderToolbar, Priority.ALWAYS);
}
mainPane.setCenter(v.getMainScreen());
}
public void initialize() {
GuiLogger.setTextArea(logArea);
loadFrameConfig();
setup3dView();
setupTrees();
setupSearchField();
setupSearchButtons();
setupShowCityComboBox();
detailsTabPane.getSelectionModel().selectedIndexProperty()
.addListener((ov, oldI, newI) -> Platform.runLater(() -> {
if (newI.intValue() == 0 && errorView.getSelectionModel().getSelectedItem() != null) {
// the first tab is selected, meaning the error tab
// redisplay the selected error
errorView.getSelectionModel().getSelectedItem().getValue().visit(renderer);
}
}));
}
private void setupShowCityComboBox() {
showCityObjectsCombo.getItems().addAll(Localization.getText("MainWindow.all"),
Localization.getText("MainWindow.withErrors"));
showCityObjectsCombo.getSelectionModel().selectFirst();
showCityObjectsCombo.setCellFactory(param -> new ListCell<>() {
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item);
if (item.contentEquals(Localization.getText("MainWindow.withErrors"))) {
setTextFill(Color.RED);
}
} else {
setText(null);
}
}
});
filterChangeListener = setupFilterSelectionListener();
showCityObjectsCombo.getSelectionModel().selectedIndexProperty().addListener(filterChangeListener);
}
public void resetFilterComboBox() {
showCityObjectsCombo.getSelectionModel().selectedIndexProperty().removeListener(filterChangeListener);
showCityObjectsCombo.getSelectionModel().selectFirst();
showCityObjectsCombo.getSelectionModel().selectedIndexProperty().addListener(filterChangeListener);
}
private ChangeListener<Number> setupFilterSelectionListener() {
return (obs, oldV, newV) -> controller.errorFilterIndexChanged(newV);
}
private void setupSearchButtons() {
searchBtn.setOnAction(ae -> controller.searchFeature(searchField.getText(), selectedTab));
clearBtn.setOnAction(ae -> {
if (searchField.getText().isEmpty()) {
// do not reset search if nothing has bee searched
return;
}
controller.resetSearch(selectedTab);
searchField.setText("");
});
}
private void setupSearchField() {
searchField.textProperty().addListener((obs, oldV, newV) -> {
if (newV.isEmpty()) {
controller.resetSearch(selectedTab);
}
});
featurePane.getSelectionModel().selectedIndexProperty().addListener((obs, oldV, newV) -> {
if (!searchField.getText().isEmpty()) {
resetSearchBar();
controller.resetSearch(selectedTab);
}
int index = newV.intValue();
switch (index) {
case 0:
selectedTab = FeatureType.BUILDING;
break;
case 1:
selectedTab = FeatureType.VEGETATION;
break;
case 2:
selectedTab = FeatureType.TRANSPORTATION;
break;
case 3:
selectedTab = FeatureType.BRIDGE;
break;
case 4:
selectedTab = FeatureType.WATER;
break;
case 5:
selectedTab = FeatureType.LAND;
break;
case 6:
selectedTab = FeatureType.CITY_FURNITURE;
break;
case 7:
selectedTab = FeatureType.GENERIC_CITY_OBJECT;
break;
default:
throw new IllegalStateException("Unknown tab index: " + index);
}
});
}
public void resetSearchBar() {
searchField.setText("");
}
private void loadFrameConfig() {
stage.setMaximized(Boolean.valueOf(Settings.get(Settings.MAXIMIZED, Boolean.FALSE.toString())));
stage.maximizedProperty()
.addListener((obs, oldV, newV) -> Settings.set(Settings.MAXIMIZED, Boolean.toString(newV)));
String widthString = Settings.get(Settings.FRAME_WIDTH);
if (widthString != null) {
stage.setWidth(Double.parseDouble(widthString));
}
stage.widthProperty().addListener(
(obs, oldV, newV) -> Settings.set(Settings.FRAME_WIDTH, Double.toString(newV.doubleValue())));
String heightString = Settings.get(Settings.FRAME_HEIGHT);
if (heightString != null) {
stage.setHeight(Double.parseDouble(heightString));
}
stage.heightProperty().addListener(
(obs, oldV, newV) -> Settings.set(Settings.FRAME_HEIGHT, Double.toString(newV.doubleValue())));
}
public void showExceptionDialog(Throwable e) {
if (exceptionDialog == null) {
exceptionDialog = new ExceptionDialog();
}
exceptionDialog.show(e);
}
private void setupTrees() {
setupSelectListener(errorView);
errorView.setRoot(new TreeItem<>());
errorView.setCellFactory(param -> new RenderableTreeCell());
buildingsView.setShowRoot(true);
setupSelectListener(buildingsView);
buildingsView.setCellFactory(param -> new RenderableTreeCell());
ContextMenu cMenu = new ContextMenu();
MenuItem mi = new MenuItem(Localization.getText("MainWindow.export"));
mi.setOnAction(ea -> {
Renderable render = buildingsView.getSelectionModel().getSelectedItem().getValue();
if (render instanceof BuildingNode node) {
controller.export(node.getBuilding());
}
});
cMenu.getItems().add(mi);
MenuItem deleteMi = new MenuItem("Delete");
deleteMi.setOnAction(ae -> controller.delete(buildingsView.getSelectionModel().getSelectedItem()));
cMenu.getItems().add(deleteMi);
buildingsView.setContextMenu(cMenu);
vegetationView.setShowRoot(true);
setupSelectListener(vegetationView);
vegetationView.setCellFactory(param -> new RenderableTreeCell());
transView.setShowRoot(true);
setupSelectListener(transView);
transView.setCellFactory(param -> new RenderableTreeCell());
bridgeView.setShowRoot(true);
setupSelectListener(bridgeView);
bridgeView.setCellFactory(param -> new RenderableTreeCell());
waterView.setShowRoot(true);
setupSelectListener(waterView);
waterView.setCellFactory(param -> new RenderableTreeCell());
terrainView.setShowRoot(true);
setupSelectListener(terrainView);
terrainView.setCellFactory(param -> new RenderableTreeCell());
cityFurnitureView.setShowRoot(true);
setupSelectListener(cityFurnitureView);
cityFurnitureView.setCellFactory(param -> new RenderableTreeCell());
otherObjectsView.setShowRoot(true);
setupSelectListener(otherObjectsView);
otherObjectsView.setCellFactory(param -> new RenderableTreeCell());
setupSelectListener(vertexView);
vertexView.setRoot(new TreeItem<>());
vertexView.setCellFactory(param -> new RenderableTreeCell());
setupSelectListener(polygonView);
polygonView.setRoot(new TreeItem<>());
polygonView.setCellFactory(param -> new RenderableTreeCell());
setupSelectListener(edgeView);
edgeView.setRoot(new TreeItem<>());
edgeView.setCellFactory(param -> new RenderableTreeCell());
}
private void setupSelectListener(TreeView<Renderable> view) {
view.getSelectionModel().selectedItemProperty().addListener((obs, oldI, newI) -> {
if (newI != null) {
newI.getValue().visit(renderer);
}
});
}
private void setup3dView() {
Group root = new Group();
geomScene = new SubScene(root, 500, 300, true, SceneAntialiasing.BALANCED);
geomScene.heightProperty().bind(meshView.heightProperty());
geomScene.widthProperty().bind(meshView.widthProperty());
geomScene.setFill(Color.AZURE);
meshView.getChildren().add(geomScene);
geomScene.addEventFilter(MouseEvent.MOUSE_PRESSED, me -> {
clickStart[0] = me.getScreenX();
clickStart[1] = me.getScreenY();
});
geomScene.addEventFilter(MouseEvent.MOUSE_RELEASED, me -> {
if (Math.abs(clickStart[0] - me.getScreenX()) > 3 || Math.abs(clickStart[1] - me.getScreenY()) > 3) {
// skip when mouse moved too much
return;
}
Node node = me.getPickResult().getIntersectedNode();
if (node != null) {
Object o = node.getUserData();
if (o instanceof ClickDispatcher cd) {
cd.click(me, clickHandler);
}
}
});
world = new Group();
root.getChildren().add(world);
meshGroup = new Group();
world.getChildren().add(meshGroup);
AmbientLight al = new AmbientLight(Color.WHITE);
root.getChildren().add(al);
buildCamera();
cameraXRotation.setAxis(Rotate.X_AXIS);
cameraZRotation.setAxis(Rotate.Z_AXIS);
world.getTransforms().add(cameraXRotation);
world.getTransforms().add(cameraZRotation);
root.getChildren().add(camera);
geomScene.setCamera(camera);
setupMeshViewControls();
}
private void setupMeshViewControls() {
meshView.setOnMousePressed(me -> {
if (me.getButton() == MouseButton.PRIMARY) {
dragX = me.getScreenX();
dragY = me.getScreenY();
}
});
meshView.setOnScroll(se -> {
if (se.getDeltaY() < 0) {
translateZ += translateZ * 0.05;
} else {
translateZ -= translateZ * 0.05;
}
camera.setTranslateZ(translateZ);
highlightController.changeScaling(translateZ);
});
meshView.setOnMouseDragged(me -> {
if (me.getButton() == MouseButton.PRIMARY) {
double deltaX = me.getScreenX() - dragX;
double deltaY = me.getScreenY() - dragY;
dragX = me.getScreenX();
dragY = me.getScreenY();
cameraXRot += (deltaX / 3d) % 360;
cameraYRot += (deltaY / 3d) % 360;
cameraZRotation.setAngle(cameraXRot);
cameraXRotation.setAngle(cameraYRot);
}
});
}
private void buildCamera() {
camera = new PerspectiveCamera(true);
camera.setNearClip(0.1);
camera.setFarClip(10000d);
camera.setTranslateZ(translateZ);
cameraZRotation.setAngle(cameraXRot);
cameraXRotation.setAngle(cameraYRot);
}
public void addFileNameToTitle(String fileName) {
String version = Localization.getText(Localization.VERSION);
stage.setTitle("CityDoctor " + version + " - " + fileName);
}
public TreeView<Renderable> getBuildingsView() {
return buildingsView;
}
public TreeView<Renderable> getVegetationView() {
return vegetationView;
}
public TreeView<Renderable> getTransportationView() {
return transView;
}
public TreeView<Renderable> getBridgeView() {
return bridgeView;
}
public TreeView<Renderable> getWaterView() {
return waterView;
}
public TreeView<Renderable> getTerrainView() {
return terrainView;
}
public TreeView<Renderable> getCityFurnitureView() {
return cityFurnitureView;
}
public TreeView<Renderable> getOtherObjectsView(){
return otherObjectsView;
}
public TreeView<Renderable> getPolygonsView() {
return polygonView;
}
public TreeView<Renderable> getEdgeView() {
return edgeView;
}
public TreeView<Renderable> getVertexView() {
return vertexView;
}
public Button getCheckButton() {
return mainToolBar.getCheckButton();
}
public ToggleButton getGridButton() {
return mainToolBar.getGridButton();
}
public Group getMeshGroup() {
return meshGroup;
}
public ToggleButton getCullingButton() {
return mainToolBar.getCullingButton();
}
public Button getWriteReportButton() {
return mainToolBar.getWriteReportButton();
}
public TreeView<Renderable> getErrorTree() {
return errorView;
}
public Stage getMainStage() {
return stage;
}
public void unselectEverything() {
buildingsView.getSelectionModel().clearSelection();
vegetationView.getSelectionModel().clearSelection();
transView.getSelectionModel().clearSelection();
waterView.getSelectionModel().clearSelection();
terrainView.getSelectionModel().clearSelection();
cityFurnitureView.getSelectionModel().clearSelection();
otherObjectsView.getSelectionModel().clearSelection();
bridgeView.getSelectionModel().clearSelection();
polygonView.getSelectionModel().clearSelection();
edgeView.getSelectionModel().clearSelection();
vertexView.getSelectionModel().clearSelection();
errorView.getSelectionModel().clearSelection();
}
public ToggleButton getLod1Btn() {
return mainToolBar.getLod1Btn();
}
public ToggleButton getLod2Btn() {
return mainToolBar.getLod2Btn();
}
public ToggleButton getLod3Btn() {
return mainToolBar.getLod3Btn();
}
public ToggleButton getLod4Btn() {
return mainToolBar.getLod4Btn();
}
public Button getWorldBtn() {
return mainToolBar.getWorldBtn();
}
public Button getSaveBtn() {
return mainToolBar.getSaveBtn();
}
public SplitPane getMainContainer() {
return mainContainer;
}
public ListView<String> getGlobalErrorsView() {
return globalErrorsView;
}
public void takeViewScreenshot() throws IOException {
WritableImage snapshot = geomScene.snapshot(null, null);
File outputFile = new File("img.png");
BufferedImage bImage = SwingFXUtils.fromFXImage(snapshot, null);
ImageIO.write(bImage, "png", outputFile);
}
public void zoomOutForBoundingBox(BoundingBox b) {
double longestSide = b.getDiagonalLength() * 0.4;
double d = longestSide / Math.tan(Math.toRadians(30) / 2);
translateZ = -d;
camera.setTranslateZ(translateZ);
highlightController.changeScaling(-translateZ);
}
public MainToolBar getMainToolbar() {
return mainToolBar;
}
public void clearHighlights() {
highlightController.clearHighlights();
}
public CityDoctorController getController() {
return controller;
}
public Button getOpenBtn() {
return mainToolBar.getOpenBtn();
}
public VertexClickHandler getClickHandler() {
return clickHandler;
}
public FeatureType getSelectedTab() {
return selectedTab;
}
@FXML
private TextArea logArea;
@FXML
private ToolBar viewBar;
private Stage stage;
private ExceptionDialog exceptionDialog;
private Renderer renderer;
private CityDoctorController controller;
private HighlightController highlightController;
private FeatureType selectedTab = FeatureType.BUILDING;
private MainToolBar mainToolBar;
private SubScene geomScene;
private static boolean loadFileAtStartup = false;
private static String inputFile;
private static String xmlOutput;
private static String pdfOutput;
private static ValidationConfiguration config;
private VertexClickHandler clickHandler;
private ChangeListener<Number> filterChangeListener;
public static void main(String[] args) {
setLocaleFromSettings();
ArgumentParser argParser = new ArgumentParser(args);
inputFile = CityDoctorValidation.getInputFile(argParser, true);
if (inputFile != null) {
loadFileAtStartup = true;
xmlOutput = CityDoctorValidation.getXmlOutput(argParser);
pdfOutput = CityDoctorValidation.getPdfOutput(argParser);
try {
config = CityDoctorValidation.getValidationConfig(argParser, true);
} catch (FileNotFoundException e) {
Platform.runLater(() -> {
List<String> configFiles = argParser.getValues("config");
Alert alert = new Alert(AlertType.ERROR);
alert.setContentText(Localization.getText("MainWindow.missingConfig") + configFiles);
alert.showAndWait();
});
System.exit(4);
}
}
Application.launch(args);
}
private static void setLocaleFromSettings() {
String localeString = Settings.get(Settings.LANGUAGE);
if (localeString != null) {
try {
Locale loc = new Locale(localeString);
Locale.setDefault(loc);
} catch (Exception e) {
logger.warn("Could not set language to {}, using system language", localeString);
}
}
}
@Override
public void start(Stage stage) throws IOException {
this.stage = stage;
stage.getIcons().add(new Image(MainWindow.class.getResourceAsStream("icons/citydoctor_logo.png")));
FXMLLoader loader = new FXMLLoader(MainWindow.class.getResource("MainWindow.fxml"));
loader.setController(this);
BorderPane bp = loader.load();
highlightController = new HighlightController(world);
renderer = new Renderer(this, highlightController);
clickHandler = new VertexClickHandler(errorView, renderer, stage);
controller = new CityDoctorController(this, highlightController, renderer);
mainToolBar = new MainToolBar(stage, controller, featurePane, renderer, this);
viewPane.getChildren().add(mainToolBar.getToolBar());
HBox.setHgrow(mainToolBar.getToolBar(), Priority.ALWAYS);
ValidationView valView = new ValidationView(this, controller);
ViewRegistration.registerView(valView);
setupViews(valView);
createLanguageSelector();
setLabelsInCorrectLanguage();
Scene scene = new Scene(bp, 1280, 800);
createDropTarget(scene, valView);
String version = Localization.getText(Localization.VERSION);
stage.setTitle("CityDoctor " + version);
stage.setScene(scene);
stage.show();
checkForStartupLoading();
memoryBar.setOnMouseClicked(me -> System.gc());
Timer timer = new Timer(true);
// check memory every second
TimerTask task = new TimerTask() {
Runtime runtime = Runtime.getRuntime();
@Override
public void run() {
long totalMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
long usedMemory = totalMemory - freeMemory;
double percentage = usedMemory / (double) totalMemory;
if (totalMemory / 1024 / 1024 >= 1024) {
// gb
double totalMemoryGb = totalMemory / (1024d * 1024d * 1024d);
double usedMemoryGb = usedMemory / (1024d * 1024d * 1024d);
String memoryString = String.format("%.1f GB / %.1f GB", usedMemoryGb, totalMemoryGb);
Platform.runLater(() -> {
memoryConsumptionLabel.setText(memoryString);
memoryBar.setProgress(percentage);
});
} else if (totalMemory / 1024 >= 1024) {
// mb
double totalMemoryMb = totalMemory / (1024d * 1024d);
double usedMemoryMb = usedMemory / (1024d * 1024d);
String memoryString = String.format("%.1f MB / %.1f MB", usedMemoryMb, totalMemoryMb);
Platform.runLater(() -> {
memoryConsumptionLabel.setText(memoryString);
memoryBar.setProgress(percentage);
});
}
}
};
timer.schedule(task, 0, 1000);
}
private void createDropTarget(Scene scene, ValidationView valView) {
setDragOverInteraction(scene, valView);
scene.setOnDragDropped(event -> {
if (ViewRegistration.getCurrentActiveView() != valView) {
return;
}
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasFiles() && db.getFiles().size() == 1) {
File f = db.getFiles().get(0);
Thread t = new Thread(() -> {
try {
controller.loadCityGml(f.getAbsolutePath(), 8, (ProgressListener) null, false);
} catch (Exception e) {
if (logger.isErrorEnabled()) {
logger.error(Localization.getText("OpenFileDialog.loadFailed"), e);
}
Platform.runLater(() -> {
ExceptionDialog exDialog = new ExceptionDialog();
exDialog.show(e);
});
}
});
t.start();
success = true;
}
// let the source know whether the string was successfully transferred and used
event.setDropCompleted(success);
event.consume();
});
}
private void setDragOverInteraction(Scene scene, ValidationView valView) {
scene.setOnDragOver(event -> {
if (ViewRegistration.getCurrentActiveView() != valView) {
return;
}
if (event.getGestureSource() != scene && event.getDragboard().hasFiles()
&& event.getDragboard().getFiles().size() == 1) {
// allow for both copying and moving, whatever user chooses
event.acceptTransferModes(TransferMode.LINK);
}
event.consume();
});
}
private void setLabelsInCorrectLanguage() {
buildingsTab.setText(Localization.getText("MainWindow.buildingsTab"));
vegetationTab.setText(Localization.getText("MainWindow.vegetationTab"));
transportationTab.setText(Localization.getText("MainWindow.transportationTab"));
bridgeTab.setText(Localization.getText("MainWindow.bridgeTab"));
waterTab.setText(Localization.getText("MainWindow.waterTab"));
terrainTab.setText(Localization.getText("MainWindow.terrainTab"));
cityFurnitureTab.setText(Localization.getText("MainWindow.cityfurnitureTab"));
otherObjectsTab.setText(Localization.getText("MainWindow.otherObjectsTab"));
viewLabel.setText(Localization.getText("MainWindow.viewLabel"));
showLabel.setText(Localization.getText("MainWindow.showLabel"));
searchLabel.setText(Localization.getText("MainWindow.searchLabel"));
searchBtn.setText(Localization.getText("MainWindow.searchBtn"));
clearBtn.setText(Localization.getText("MainWindow.clearBtn"));
errorsTab.setText(Localization.getText("MainWindow.errorsTab"));
polygonsTab.setText(Localization.getText("MainWindow.polygonsTab"));
edgesTab.setText(Localization.getText("MainWindow.edgesTab"));
verticesTab.setText(Localization.getText("MainWindow.verticesTab"));
logTab.setText(Localization.getText("MainWindow.logTab"));
globalErrorsTab.setText(Localization.getText("MainWindow.globalErrorsTab"));
memoryLabel.setText(Localization.getText("MainWindow.memoryLabel"));
availableLabel.setText(String.format("%s %.1f GB", Localization.getText("MainWindow.availableLabel"),
Runtime.getRuntime().maxMemory() / 1024d / 1024d / 1024d));
}
private void createLanguageSelector() {
languageSelector.setButtonCell(new LanguageSelectorCell());
languageSelector.setCellFactory(view -> new LanguageSelectorCell());
languageSelector.getItems().add(Locale.GERMAN);
languageSelector.getItems().add(Locale.ENGLISH);
if (Locale.getDefault().getLanguage().equals(Locale.GERMAN.getLanguage())) {
languageSelector.getSelectionModel().select(Locale.GERMAN);
} else if (Locale.getDefault().getLanguage().equals(Locale.ENGLISH.getLanguage())) {
languageSelector.getSelectionModel().select(Locale.ENGLISH);
} else {
languageSelector.getSelectionModel().select(Locale.ENGLISH);
Settings.set(Settings.LANGUAGE, Locale.ENGLISH.getLanguage());
}
languageSelector.getSelectionModel().selectedItemProperty().addListener((obs, oldV, newV) -> {
Alert alert = new Alert(AlertType.CONFIRMATION, Localization.getText("MainWindow.languageChange"),
ButtonType.OK);
alert.showAndWait();
Settings.set(Settings.LANGUAGE, newV.getLanguage());
});
}
private void checkForStartupLoading() {
if (loadFileAtStartup) {
logger.info(Localization.getText("MainWindow.loadGivenFile"));
Thread t = new Thread(() -> {
try {
// wait a bit for the gui to show
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
try {
controller.loadCityGml(inputFile, config.getNumberOfRoundingPlaces(), null,
config.isXmlValidation());
logger.info(Localization.getText("MainWindow.finishedLoading"));
logger.info(Localization.getText("MainWindow.checking"));
controller.startChecks(config, null);
if (xmlOutput != null) {
logger.info(Localization.getText("MainWindow.writeXml"));
controller.writeXmlReport(new File(xmlOutput));
logger.info(Localization.getText("MainWindow.finishedXml"));
}
if (pdfOutput != null) {
logger.info(Localization.getText("MainWindow.writePdf"));
controller.writePdfReport(new File(pdfOutput));
logger.info(Localization.getText("MainWindow.finishedPdf"));
}
} catch (CityGmlParseException | InvalidGmlFileException e) {
logger.error(Localization.getText("MainWindow.loadFailed"), e.getMessage());
}
});
t.start();
}
}
private void setupViews(ValidationView valView) {
ToggleGroup group = new ToggleGroup();
for (View v : ViewRegistration.getRegisteredViews()) {
RadioButton radioButton = new RadioButton();
radioButton.getStyleClass().remove("radio-button");
radioButton.getStyleClass().add("toggle-button");
ImageView view = new ImageView(v.getViewLogo());
view.setFitHeight(32);
view.setFitWidth(32);
radioButton.setGraphic(view);
group.getToggles().add(radioButton);
if (v == valView) {
ViewRegistration.setCurrentActiveView(valView);
group.selectToggle(radioButton);
}
viewButtonBox.getChildren().add(radioButton);
radioButton.selectedProperty().addListener((obs, oldV, newV) -> {
if (Boolean.TRUE.equals(newV)) {
showView(v);
ViewRegistration.setCurrentActiveView(v);
} else {
v.onHide();
}
});
}
}
private void showView(View v) {
viewPane.getChildren().clear();
controller.showView(v);
Optional<HBox> toolbar = v.getToolbar();
if (toolbar.isPresent()) {
viewPane.getChildren().add(toolbar.get());
HBox.setHgrow(toolbar.get(), Priority.ALWAYS);
} else {
HBox placeHolderToolbar = new HBox();
placeHolderToolbar.setMaxHeight(Double.MAX_VALUE);
viewPane.getChildren().add(placeHolderToolbar);
HBox.setHgrow(placeHolderToolbar, Priority.ALWAYS);
}
mainPane.setCenter(v.getMainScreen());
}
public void initialize() {
GuiLogger.setTextArea(logArea);
loadFrameConfig();
setup3dView();
setupTrees();
setupSearchField();
setupSearchButtons();
setupShowCityComboBox();
detailsTabPane.getSelectionModel().selectedIndexProperty()
.addListener((ov, oldI, newI) -> Platform.runLater(() -> {
if (newI.intValue() == 0 && errorView.getSelectionModel().getSelectedItem() != null) {
// the first tab is selected, meaning the error tab
// redisplay the selected error
errorView.getSelectionModel().getSelectedItem().getValue().visit(renderer);
}
}));
}
private void setupShowCityComboBox() {
showCityObjectsCombo.getItems().addAll(Localization.getText("MainWindow.all"),
Localization.getText("MainWindow.withErrors"));
showCityObjectsCombo.getSelectionModel().selectFirst();
showCityObjectsCombo.setCellFactory(param -> new ListCell<>() {
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item);
if (item.contentEquals(Localization.getText("MainWindow.withErrors"))) {
setTextFill(Color.RED);
}
} else {
setText(null);
}
}
});
filterChangeListener = setupFilterSelectionListener();
showCityObjectsCombo.getSelectionModel().selectedIndexProperty().addListener(filterChangeListener);
}
public void resetFilterComboBox() {
showCityObjectsCombo.getSelectionModel().selectedIndexProperty().removeListener(filterChangeListener);
showCityObjectsCombo.getSelectionModel().selectFirst();
showCityObjectsCombo.getSelectionModel().selectedIndexProperty().addListener(filterChangeListener);
}
private ChangeListener<Number> setupFilterSelectionListener() {
return (obs, oldV, newV) -> controller.errorFilterIndexChanged(newV);
}
private void setupSearchButtons() {
searchBtn.setOnAction(ae -> controller.searchFeature(searchField.getText(), selectedTab));
clearBtn.setOnAction(ae -> {
if (searchField.getText().isEmpty()) {
// do not reset search if nothing has bee searched
return;
}
controller.resetSearch(selectedTab);
searchField.setText("");
});
}
private void setupSearchField() {
searchField.textProperty().addListener((obs, oldV, newV) -> {
if (newV.isEmpty()) {
controller.resetSearch(selectedTab);
}
});
featurePane.getSelectionModel().selectedIndexProperty().addListener((obs, oldV, newV) -> {
if (!searchField.getText().isEmpty()) {
resetSearchBar();
controller.resetSearch(selectedTab);
}
int index = newV.intValue();
switch (index) {
case 0:
selectedTab = FeatureType.BUILDING;
break;
case 1:
selectedTab = FeatureType.VEGETATION;
break;
case 2:
selectedTab = FeatureType.TRANSPORTATION;
break;
case 3:
selectedTab = FeatureType.BRIDGE;
break;
case 4:
selectedTab = FeatureType.WATER;
break;
case 5:
selectedTab = FeatureType.LAND;
break;
case 6:
selectedTab = FeatureType.CITY_FURNITURE;
break;
case 7:
selectedTab = FeatureType.GENERIC_CITY_OBJECT;
break;
default:
throw new IllegalStateException("Unknown tab index: " + index);
}
});
}
public void resetSearchBar() {
searchField.setText("");
}
private void loadFrameConfig() {
stage.setMaximized(Boolean.valueOf(Settings.get(Settings.MAXIMIZED, Boolean.FALSE.toString())));
stage.maximizedProperty()
.addListener((obs, oldV, newV) -> Settings.set(Settings.MAXIMIZED, Boolean.toString(newV)));
String widthString = Settings.get(Settings.FRAME_WIDTH);
if (widthString != null) {
stage.setWidth(Double.parseDouble(widthString));
}
stage.widthProperty().addListener(
(obs, oldV, newV) -> Settings.set(Settings.FRAME_WIDTH, Double.toString(newV.doubleValue())));
String heightString = Settings.get(Settings.FRAME_HEIGHT);
if (heightString != null) {
stage.setHeight(Double.parseDouble(heightString));
}
stage.heightProperty().addListener(
(obs, oldV, newV) -> Settings.set(Settings.FRAME_HEIGHT, Double.toString(newV.doubleValue())));
}
public void showExceptionDialog(Throwable e) {
if (exceptionDialog == null) {
exceptionDialog = new ExceptionDialog();
}
exceptionDialog.show(e);
}
private void setupTrees() {
setupSelectListener(errorView);
errorView.setRoot(new TreeItem<>());
errorView.setCellFactory(param -> new RenderableTreeCell());
buildingsView.setShowRoot(true);
setupSelectListener(buildingsView);
buildingsView.setCellFactory(param -> new RenderableTreeCell());
ContextMenu cMenu = new ContextMenu();
MenuItem mi = new MenuItem(Localization.getText("MainWindow.export"));
mi.setOnAction(ea -> {
Renderable render = buildingsView.getSelectionModel().getSelectedItem().getValue();
if (render instanceof BuildingNode node) {
controller.export(node.getBuilding());
}
});
cMenu.getItems().add(mi);
MenuItem deleteMi = new MenuItem("Delete");
deleteMi.setOnAction(ae -> controller.delete(buildingsView.getSelectionModel().getSelectedItem()));
cMenu.getItems().add(deleteMi);
buildingsView.setContextMenu(cMenu);
vegetationView.setShowRoot(true);
setupSelectListener(vegetationView);
vegetationView.setCellFactory(param -> new RenderableTreeCell());
transView.setShowRoot(true);
setupSelectListener(transView);
transView.setCellFactory(param -> new RenderableTreeCell());
bridgeView.setShowRoot(true);
setupSelectListener(bridgeView);
bridgeView.setCellFactory(param -> new RenderableTreeCell());
waterView.setShowRoot(true);
setupSelectListener(waterView);
waterView.setCellFactory(param -> new RenderableTreeCell());
terrainView.setShowRoot(true);
setupSelectListener(terrainView);
terrainView.setCellFactory(param -> new RenderableTreeCell());
cityFurnitureView.setShowRoot(true);
setupSelectListener(cityFurnitureView);
cityFurnitureView.setCellFactory(param -> new RenderableTreeCell());
otherObjectsView.setShowRoot(true);
setupSelectListener(otherObjectsView);
otherObjectsView.setCellFactory(param -> new RenderableTreeCell());
setupSelectListener(vertexView);
vertexView.setRoot(new TreeItem<>());
vertexView.setCellFactory(param -> new RenderableTreeCell());
setupSelectListener(polygonView);
polygonView.setRoot(new TreeItem<>());
polygonView.setCellFactory(param -> new RenderableTreeCell());
setupSelectListener(edgeView);
edgeView.setRoot(new TreeItem<>());
edgeView.setCellFactory(param -> new RenderableTreeCell());
}
private void setupSelectListener(TreeView<Renderable> view) {
view.getSelectionModel().selectedItemProperty().addListener((obs, oldI, newI) -> {
if (newI != null) {
newI.getValue().visit(renderer);
}
});
}
private void setup3dView() {
Group root = new Group();
geomScene = new SubScene(root, 500, 300, true, SceneAntialiasing.BALANCED);
geomScene.heightProperty().bind(meshView.heightProperty());
geomScene.widthProperty().bind(meshView.widthProperty());
geomScene.setFill(Color.AZURE);
meshView.getChildren().add(geomScene);
geomScene.addEventFilter(MouseEvent.MOUSE_PRESSED, me -> {
clickStart[0] = me.getScreenX();
clickStart[1] = me.getScreenY();
});
geomScene.addEventFilter(MouseEvent.MOUSE_RELEASED, me -> {
if (Math.abs(clickStart[0] - me.getScreenX()) > 3 || Math.abs(clickStart[1] - me.getScreenY()) > 3) {
// skip when mouse moved too much
return;
}
Node node = me.getPickResult().getIntersectedNode();
if (node != null) {
Object o = node.getUserData();
if (o instanceof ClickDispatcher cd) {
cd.click(me, clickHandler);
}
}
});
world = new Group();
root.getChildren().add(world);
meshGroup = new Group();
world.getChildren().add(meshGroup);
AmbientLight al = new AmbientLight(Color.WHITE);
root.getChildren().add(al);
buildCamera();
cameraXRotation.setAxis(Rotate.X_AXIS);
cameraZRotation.setAxis(Rotate.Z_AXIS);
world.getTransforms().add(cameraXRotation);
world.getTransforms().add(cameraZRotation);
root.getChildren().add(camera);
geomScene.setCamera(camera);
setupMeshViewControls();
}
private void setupMeshViewControls() {
meshView.setOnMousePressed(me -> {
if (me.getButton() == MouseButton.PRIMARY) {
dragX = me.getScreenX();
dragY = me.getScreenY();
}
});
meshView.setOnScroll(se -> {
if (se.getDeltaY() < 0) {
translateZ += translateZ * 0.05;
} else {
translateZ -= translateZ * 0.05;
}
camera.setTranslateZ(translateZ);
highlightController.changeScaling(translateZ);
});
meshView.setOnMouseDragged(me -> {
if (me.getButton() == MouseButton.PRIMARY) {
double deltaX = me.getScreenX() - dragX;
double deltaY = me.getScreenY() - dragY;
dragX = me.getScreenX();
dragY = me.getScreenY();
cameraXRot += (deltaX / 3d) % 360;
cameraYRot += (deltaY / 3d) % 360;
cameraZRotation.setAngle(cameraXRot);
cameraXRotation.setAngle(cameraYRot);
}
});
}
private void buildCamera() {
camera = new PerspectiveCamera(true);
camera.setNearClip(0.1);
camera.setFarClip(10000d);
camera.setTranslateZ(translateZ);
cameraZRotation.setAngle(cameraXRot);
cameraXRotation.setAngle(cameraYRot);
}
public void addFileNameToTitle(String fileName) {
String version = Localization.getText(Localization.VERSION);
stage.setTitle("CityDoctor " + version + " - " + fileName);
}
public TreeView<Renderable> getBuildingsView() {
return buildingsView;
}
public TreeView<Renderable> getVegetationView() {
return vegetationView;
}
public TreeView<Renderable> getTransportationView() {
return transView;
}
public TreeView<Renderable> getBridgeView() {
return bridgeView;
}
public TreeView<Renderable> getWaterView() {
return waterView;
}
public TreeView<Renderable> getTerrainView() {
return terrainView;
}
public TreeView<Renderable> getCityFurnitureView() {
return cityFurnitureView;
}
public TreeView<Renderable> getOtherObjectsView() {
return otherObjectsView;
}
public TreeView<Renderable> getPolygonsView() {
return polygonView;
}
public TreeView<Renderable> getEdgeView() {
return edgeView;
}
public TreeView<Renderable> getVertexView() {
return vertexView;
}
public Button getCheckButton() {
return mainToolBar.getCheckButton();
}
public ToggleButton getGridButton() {
return mainToolBar.getGridButton();
}
public Group getMeshGroup() {
return meshGroup;
}
public ToggleButton getCullingButton() {
return mainToolBar.getCullingButton();
}
public Button getWriteReportButton() {
return mainToolBar.getWriteReportButton();
}
public TreeView<Renderable> getErrorTree() {
return errorView;
}
public Stage getMainStage() {
return stage;
}
public void unselectEverything() {
buildingsView.getSelectionModel().clearSelection();
vegetationView.getSelectionModel().clearSelection();
transView.getSelectionModel().clearSelection();
waterView.getSelectionModel().clearSelection();
terrainView.getSelectionModel().clearSelection();
cityFurnitureView.getSelectionModel().clearSelection();
otherObjectsView.getSelectionModel().clearSelection();
bridgeView.getSelectionModel().clearSelection();
polygonView.getSelectionModel().clearSelection();
edgeView.getSelectionModel().clearSelection();
vertexView.getSelectionModel().clearSelection();
errorView.getSelectionModel().clearSelection();
}
public ToggleButton getLod1Btn() {
return mainToolBar.getLod1Btn();
}
public ToggleButton getLod2Btn() {
return mainToolBar.getLod2Btn();
}
public ToggleButton getLod3Btn() {
return mainToolBar.getLod3Btn();
}
public ToggleButton getLod4Btn() {
return mainToolBar.getLod4Btn();
}
public Button getWorldBtn() {
return mainToolBar.getWorldBtn();
}
public Button getSaveBtn() {
return mainToolBar.getSaveBtn();
}
public SplitPane getMainContainer() {
return mainContainer;
}
public ListView<String> getGlobalErrorsView() {
return globalErrorsView;
}
public void takeViewScreenshot() throws IOException {
WritableImage snapshot = geomScene.snapshot(null, null);
File outputFile = new File("img.png");
BufferedImage bImage = SwingFXUtils.fromFXImage(snapshot, null);
ImageIO.write(bImage, "png", outputFile);
}
public void zoomOutForBoundingBox(BoundingBox b) {
double longestSide = b.getDiagonalLength() * 0.4;
double d = longestSide / Math.tan(Math.toRadians(30) / 2);
translateZ = -d;
camera.setTranslateZ(translateZ);
highlightController.changeScaling(-translateZ);
}
public MainToolBar getMainToolbar() {
return mainToolBar;
}
public void clearHighlights() {
highlightController.clearHighlights();
}
public CityDoctorController getController() {
return controller;
}
public Button getOpenBtn() {
return mainToolBar.getOpenBtn();
}
public VertexClickHandler getClickHandler() {
return clickHandler;
}
public FeatureType getSelectedTab() {
return selectedTab;
}
}
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