Skip to content

Commit 1209729

Browse files
authored
Merge pull request #24 from tudelft3d/gio-dev
Merge for version 0.8.3
2 parents 63dd0f9 + 59155a6 commit 1209729

12 files changed

+349
-133
lines changed

README.md

+14-8
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ The plugin allows to connect to local or remote instances of the free and open-s
99
As semantic 3D city models tend to be huge datasets and are generally best managed in spatial databases, the main idea behind the development of this plugin is to facilitate access and use of [CityGML](https://en.wikipedia.org/wiki/CityGML)/[CityJSON](https://www.cityjson.org/) data for those practitioners that lack a deep knowledge of the international standard [OCG CityGML data model](https://www.ogc.org/standards/citygml), and/or have limited experience with SQL/Spatial-RDBMSs in general.
1010
The plugin consists of a server-side part (written in PL/pgSQL) and a client-side part (written in Python).
1111

12-
The client-side part offers at the moment three GUI-based tools:
12+
The client-side part offers, at the moment, three GUI-based tools:
1313
- The **Layer Loader**, to load and interact with data in the 3D City Database directly from QGIS
1414
- The **Bulk Deleter**, to delete features from the database, either at all at once, or by means of spatial and feature-related filters.
15-
- The **QGIS Package Administration**, to install the server-side part of the plugin, as well as to set up database user access and user privileges.
15+
- The **QGIS Package Administrator**, to install the server-side part of the plugin, as well as to set up database user access and user privileges.
1616

1717
In particular, the **Layer Loader** offers following functionalities:
1818
- All CityGML modules are supported (Building, Bridge, Tunnel, Vegetation, Terrain, etc.)
@@ -27,7 +27,7 @@ In particular, the **Layer Loader** offers following functionalities:
2727
- Support for CityGML enumerations and codelists
2828
- All layer geometries are 3D: they can be visualised both in 2D and in 3D (Please be aware that 3D visualisation in QGIS 3D map is still a bit unstable...).
2929

30-
Further details, and a user guide, can be found in the \user_guide subfolder of the plugin installation directory (see file "[3DCityDB-Tools_UserGuide.pdf](user_guide/3DCityDB-Tools_UserGuide_0.8.2.pdf)").
30+
Further details, and a user guide, can be found in the \user_guide subfolder of the plugin installation directory (see file "[3DCityDB-Tools_UserGuide.pdf](user_guide/3DCityDB-Tools_UserGuide_0.8.3.pdf)").
3131

3232
Some datasets for testing purposes are available, too, and are contained in the \test_datasets subfolder.
3333

@@ -36,28 +36,34 @@ Some datasets for testing purposes are available, too, and are contained in the
3636

3737
# Requirements
3838

39-
The plugin has been developed using [QGIS](https://www.qgis.org/nl/site/forusers/download.html) 3.22 LTR and works best with it. Our tests so far show that it works with any QGIS version >= 3.22, including the recently released version 3.28 LTR. Please note that support and further development will focus only on LTR versions.
39+
The plugin has been developed using [**QGIS**](https://www.qgis.org/en/site/forusers/download.html) **3.22 LTR** and **3.28 LTR**. Please note that support and further development will focus only on LTR versions.
4040

41-
The server-side part of the plugin requires PostgreSQL version >= 10.
41+
The server-side part of the plugin requires PostgreSQL version >= 10 and PostGIS version >= 2.
4242

4343
Otherwise, only a working instance of the 3D City Database is required. The currently supported version of the [3DCityDB](https://github.com/3dcitydb) is the 4.x. To set up the 3D City Database and import (or export) CityGML/CityJSON data from/to it, we heartily recommend to use the free and open-source, Java-based [Importer-Exporter](https://github.com/3dcitydb/importer-exporter). Alternatively, the [3D City Database Suite](https://github.com/3dcitydb/3dcitydb-suite/releases) already ships with all necessary software tools. Further information can be found [here](https://3dcitydb-docs.readthedocs.io/en/latest/).
4444

45+
# Installation
46+
47+
The easiest way to install the plug-in is via the [QGIS Plugins repository](https://plugins.qgis.org/plugins/citydb-tools/), or directly from QGIS. Just look for the 3DCityDB-Tools plug-in!
48+
49+
Alternatively, you can download the plug-in zip file from here and install it manually. Please refer to the installation steps explained in the documentation, which also contains details on how to set up the server-side part of the plug-in.
50+
4551
# Developers
4652

4753
The plugin is currently developed by:
4854
- [Giorgio Agugiaro](mailto:g.agugiaro@tudelft.nl)
4955
- [Konstantinos Pantelios](mailto:konstantinospantelios@yahoo.com)
5056

5157
with contributions by:
52-
- [Tendai Mbwanda](tendai.mbwanda@student.tudelft.nl)
58+
- [Tendai Mbwanda](mailto:tmbwanda52@gmail.com)
5359

54-
and with additional suggestions and feedback by Camilo León-Sánchez (TU Delft), Claus Nagel and Zhihang Yao (Virtual City Systems GmbH).
60+
and with additional suggestions and feedback by Camilo León-Sánchez (TU Delft), Claus Nagel and Zhihang Yao (VirtualCitySystems GmbH).
5561

5662
# Future
5763

5864
Besides further testing and debugging, there are a number of improvements that we are thinking of, such as:
5965
- Support for appearances (at least for X3D Materials, if possible)
60-
- Support of ADEs (e.g. the Energy ADE, to start with) (Currently being investigated in an on-going [MSc Geomatics thesis at TU Delft](https://3d.bk.tudelft.nl/education/#theses))
66+
- Support for ADEs (Preliminary work on the Energy ADE has been already carried out in a [MSc Geomatics thesis at TU Delft](https://repository.tudelft.nl/islandora/object/uuid%3A6786ac5c-b61d-4e17-8501-e3cf2c7a9577))
6167
- Testing and initial support for the 3DCityDB v. 5.0 (and therefore CityGML 3.0)
6268
- ...the sky is the limit...
6369

cdb4/gui_admin/ui/cdb4_admin_dialog.ui

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
</size>
3333
</property>
3434
<property name="windowTitle">
35-
<string>QGIS Package Administration</string>
35+
<string>QGIS Package Administrator</string>
3636
</property>
3737
<property name="sizeGripEnabled">
3838
<bool>true</bool>

cdb4/gui_loader/functions/sql.py

+40-3
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ def fetch_codelist_lookup_config(dlg: CDB4LoaderDialog, codelist_set_name: str)
585585
dlg.conn.rollback()
586586

587587

588-
def fetch_codelist_set_names(dlg: CDB4LoaderDialog) -> list:
588+
def fetch_CityGML_codelist_set_names(dlg: CDB4LoaderDialog) -> list:
589589
"""SQL query that retrieves the codelist set names to fill the codelist selection box
590590
591591
* :returns: the unique names in table codelist_lookup_config
@@ -615,8 +615,45 @@ def fetch_codelist_set_names(dlg: CDB4LoaderDialog) -> list:
615615

616616
except (Exception, psycopg2.Error) as error:
617617
gen_f.critical_log(
618-
func=fetch_codelist_set_names,
618+
func=fetch_CityGML_codelist_set_names,
619619
location=FILE_LOCATION,
620-
header=f"Retrieving codelist set names from table '{dlg.USR_SCHEMA}.codelist_lookup_config'",
620+
header=f"Retrieving CityGML codelist set from table '{dlg.USR_SCHEMA}.codelist_lookup_config'",
621621
error=error)
622622
dlg.conn.rollback()
623+
624+
# def fetch_ADE_codelist_set_names(dlg: CDB4LoaderDialog) -> list:
625+
# """SQL query that retrieves the codelist set names to fill the codelist selection box
626+
627+
# * :returns: the unique names in table codelist_lookup_config
628+
# :rtype: list of tuples
629+
# """
630+
# query = pysql.SQL("""
631+
# SELECT DISTINCT name FROM {_usr_schema}.codelist_lookup_config
632+
# WHERE ade_prefix = {_ade_prefix}
633+
# ORDER BY name;
634+
# """).format(
635+
# _usr_schema = pysql.Identifier(dlg.USR_SCHEMA),
636+
# _ade_prefix = pysql.Identifier(dlg.ADE_PREFIX)
637+
# )
638+
639+
# try:
640+
# # with dlg.conn.cursor(cursor_factory=NamedTupleCursor) as cur:
641+
# with dlg.conn.cursor() as cur:
642+
# cur.execute(query)
643+
# res = cur.fetchall()
644+
# dlg.conn.commit()
645+
646+
# codelist_set_names = [elem[0] for elem in res]
647+
648+
# if not codelist_set_names:
649+
# codelist_set_names = []
650+
651+
# return codelist_set_names
652+
653+
# except (Exception, psycopg2.Error) as error:
654+
# gen_f.critical_log(
655+
# func=fetch_ADE_codelist_set_names,
656+
# location=FILE_LOCATION,
657+
# header=f"Retrieving ADE codelist set from table '{dlg.USR_SCHEMA}.codelist_lookup_config'",
658+
# error=error)
659+
# dlg.conn.rollback()

cdb4/gui_loader/functions/tab_conn_functions.py

+14-3
Original file line numberDiff line numberDiff line change
@@ -298,10 +298,21 @@ def check_layers_status(dlg: CDB4LoaderDialog) -> bool:
298298
# Also enables the settings tab
299299
# Fill the combo box with the codelist selection
300300

301-
codelist_set_names: list = sql.fetch_codelist_set_names(dlg)
301+
CityGML_codelist_set_names: list = sql.fetch_CityGML_codelist_set_names(dlg)
302302
# print("Initializing combo box with:", codelist_set_names)
303-
if codelist_set_names:
304-
ts_wf.fill_codelist_selection_box(dlg, codelist_set_names)
303+
if CityGML_codelist_set_names:
304+
tl_wf.fill_CityGML_codelist_selection_box(dlg, CityGML_codelist_set_names)
305+
306+
# TO DO
307+
#
308+
# Add similar step to retrieve ADE codelist set
309+
#
310+
# if dlg.ADE_PREFIX:
311+
# ADE_codelist_set_names: list = sql.fetch_ADE_codelist_set_names(dlg)
312+
# # print("Initializing combo box with:", codelist_set_names)
313+
# if ADE_codelist_set_names:
314+
# tl_wf.fill_ADE_codelist_selection_box(dlg, ADE_codelist_set_names)
315+
#
305316

306317
dlg.tabSettings.setDisabled(False)
307318

cdb4/gui_loader/functions/tab_layers_widget_functions.py

+58
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,55 @@
2121
## Setup widget functions for 'Layer' tab
2222
####################################################
2323

24+
def fill_CityGML_codelist_selection_box(dlg: CDB4LoaderDialog, CityGML_codelist_set_names: list = None) -> None:
25+
"""Function that fills the 'Select CodeLists group' combo box.
26+
"""
27+
# Clean combo box from previous leftovers.
28+
dlg.cbxCodeListSelCityGML.clear()
29+
30+
if not CityGML_codelist_set_names:
31+
# Disable the combobox
32+
dlg.cbxCodeListSelCityGML.setDisabled(True)
33+
dlg.cbxCodeListSelCityGML.setDisabled(True)
34+
else:
35+
label: str = f"None"
36+
dlg.cbxCodeListSelCityGML.addItem(label, userData=label)
37+
for codelist_set_name in CityGML_codelist_set_names:
38+
label: str = f"{codelist_set_name}"
39+
dlg.cbxCodeListSelCityGML.addItem(label, userData=label)
40+
if not dlg.cbxCodeListSelCityGML.isEnabled():
41+
# Enable the combobox
42+
dlg.cbxCodeListSelCityGML.setDisabled(False)
43+
dlg.lblCodeListSelCityGML.setDisabled(False)
44+
45+
# REMEMBER: don't use method 'setSeparator', it adds a custom separator to join string of selected items
46+
return None
47+
48+
# def fill_ADE_codelist_selection_box(dlg: CDB4LoaderDialog, ADE_codelist_set_names: list = None) -> None:
49+
# """Function that fills the 'Select CodeLists group' combo box.
50+
# """
51+
# # Clean combo box from previous leftovers.
52+
# dlg.cbxCodeListSelADE.clear()
53+
54+
# if not ADE_codelist_set_names:
55+
# # Disable the combobox
56+
# dlg.cbxCodeListSelADE.setDisabled(True)
57+
# dlg.cbxCodeListSelADE.setDisabled(True)
58+
# else:
59+
# label: str = f"None"
60+
# dlg.cbxCodeListSelADE.addItem(label, userData=label)
61+
# for codelist_set_name in ADE_codelist_set_names:
62+
# label: str = f"{codelist_set_name}"
63+
# dlg.cbxCodeListSelADE.addItem(label, userData=label)
64+
# if not dlg.cbxCodeListSelADE.isEnabled():
65+
# # Enable the combobox
66+
# dlg.cbxCodeListSelADE.setDisabled(False)
67+
# dlg.lblCodeListSelADE.setDisabled(False)
68+
69+
# REMEMBER: don't use method 'setSeparator', it adds a custom separator to join string of selected items
70+
return None
71+
72+
2473
# In 'Basemap (OMS)' groupBox.
2574
def gbxBasemapL_setup(dlg: CDB4LoaderDialog) -> None:
2675
"""Function to setup the 'Basemap' groupbox. It uses an additional canvas instance to store an OSM map
@@ -59,6 +108,7 @@ def tabLayers_reset(dlg: CDB4LoaderDialog) -> None:
59108
lblInfoText_reset(dlg)
60109
gbxBasemapL_reset(dlg)
61110
gbxLayerSelection_reset(dlg)
111+
gbxCodeListSelection_reset(dlg)
62112
gbxAvailableL_reset(dlg)
63113

64114
return None
@@ -94,6 +144,14 @@ def gbxLayerSelection_reset(dlg: CDB4LoaderDialog) -> None:
94144

95145
return None
96146

147+
def gbxCodeListSelection_reset(dlg: CDB4LoaderDialog) -> None:
148+
"""Function to reset the 'Miscellaneous option' groupbox to the DEFAULT values
149+
"""
150+
dlg.cbxCodeListSelCityGML.clear()
151+
# dlg.cbxCodeListSelADE.clear()
152+
return None
153+
154+
97155

98156
def gbxAvailableL_reset(dlg: CDB4LoaderDialog) -> None:
99157
"""Function to reset the 'Features to Import' group box (in Layers tab).

cdb4/gui_loader/functions/tab_settings_widget_functions.py

+28-28
Original file line numberDiff line numberDiff line change
@@ -16,29 +16,29 @@
1616
FILE_LOCATION = gen_f.get_file_relative_path(file=__file__)
1717

1818

19-
def fill_codelist_selection_box(dlg: CDB4LoaderDialog, codelist_set_names: list = None) -> None:
20-
"""Function that fills the 'Select CodeLists group' combo box.
21-
"""
22-
# Clean combo box from previous leftovers.
23-
dlg.cbxCodeListSelection.clear()
24-
25-
if not codelist_set_names:
26-
# Disable the combobox
27-
dlg.cbxCodeListSelection.setDisabled(True)
28-
dlg.cbxCodeListSelection.setDisabled(True)
29-
else:
30-
label: str = f"None"
31-
dlg.cbxCodeListSelection.addItem(label, userData=label)
32-
for codelist_set_name in codelist_set_names:
33-
label: str = f"{codelist_set_name}"
34-
dlg.cbxCodeListSelection.addItem(label, userData=label)
35-
if not dlg.cbxCodeListSelection.isEnabled():
36-
# Enable the combobox
37-
dlg.cbxCodeListSelection.setDisabled(False)
38-
dlg.lblCodeListSelection.setDisabled(False)
19+
# def fill_CityGML_codelist_selection_box(dlg: CDB4LoaderDialog, CityGML_codelist_set_names: list = None) -> None:
20+
# """Function that fills the 'Select CodeLists group' combo box.
21+
# """
22+
# # Clean combo box from previous leftovers.
23+
# dlg.cbxCodeListSelCityGML.clear()
24+
25+
# if not CityGML_codelist_set_names:
26+
# # Disable the combobox
27+
# dlg.cbxCodeListSelCityGML.setDisabled(True)
28+
# dlg.cbxCodeListSelCityGML.setDisabled(True)
29+
# else:
30+
# label: str = f"None"
31+
# dlg.cbxCodeListSelCityGML.addItem(label, userData=label)
32+
# for codelist_set_name in CityGML_codelist_set_names:
33+
# label: str = f"{codelist_set_name}"
34+
# dlg.cbxCodeListSelCityGML.addItem(label, userData=label)
35+
# if not dlg.cbxCodeListSelCityGML.isEnabled():
36+
# # Enable the combobox
37+
# dlg.cbxCodeListSelCityGML.setDisabled(False)
38+
# dlg.lblCodeListSelCityGML.setDisabled(False)
3939

40-
# REMEMBER: don't use method 'setSeparator', it adds a custom separator to join string of selected items
41-
return None
40+
# # REMEMBER: don't use method 'setSeparator', it adds a custom separator to join string of selected items
41+
# return None
4242

4343

4444
####################################################
@@ -51,7 +51,7 @@ def tabSettings_reset(dlg: CDB4LoaderDialog) -> None:
5151
dlg.tabSettings.setDisabled(True)
5252
gbxGeomSimp_reset(dlg)
5353
gbxLayerOptions_reset(dlg)
54-
gbxCodeListSelection_reset(dlg)
54+
# gbxCodeListSelection_reset(dlg)
5555
gbxMisc_reset(dlg)
5656

5757
return None
@@ -77,12 +77,12 @@ def gbxLayerOptions_reset(dlg: CDB4LoaderDialog) -> None:
7777
return None
7878

7979

80-
def gbxCodeListSelection_reset(dlg: CDB4LoaderDialog) -> None:
81-
"""Function to reset the 'Miscellaneous option' groupbox to the DEFAULT values
82-
"""
83-
dlg.cbxCodeListSelection.clear()
80+
# def gbxCodeListSelection_reset(dlg: CDB4LoaderDialog) -> None:
81+
# """Function to reset the 'Miscellaneous option' groupbox to the DEFAULT values
82+
# """
83+
# dlg.cbxCodeListSelCityGML.clear()
8484

85-
return None
85+
# return None
8686

8787

8888
def gbxMisc_reset(dlg: CDB4LoaderDialog) -> None:

cdb4/gui_loader/loader_dialog.py

+13-7
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ def __init__(self, cdbMain: CDBToolsMain, parent=None):
123123
# Dictionary containing config data to set up codelist combo boxes in the attribute forms
124124
self.CodeListConfigRegistry: dict = {}
125125
# Variable to store the selected CodeListSet
126-
self.selectedCodeListSet: str = None
126+
self.selectedCityGMLCodeListSet: str = None
127127

128128
self.CDBSchemaPrivileges: str = None
129129
# self.n_cityobjects: int = 0 # Number of cityobjects in the current cdb_schema
@@ -1180,24 +1180,30 @@ def evt_btnImport_clicked(self) -> None:
11801180
if res == 16384: # YES, proceed with importing layers
11811181
success = tl_f.add_selected_layers_to_ToC(self, layers=selected_layers)
11821182
else:
1183-
return None #Import Cancelled
1183+
return None # Import Cancelled
11841184
else:
11851185

11861186
# Codelist settings and loading of the configs
1187-
sel_codelist_set: str = self.cbxCodeListSelection.currentData()
1188-
if sel_codelist_set == "None":
1187+
sel_CityGML_codelist_set: str = self.cbxCodeListSelCityGML.currentData()
1188+
if sel_CityGML_codelist_set == "None":
11891189
# do nothing
11901190
pass
11911191
else:
1192-
if any([not self.selectedCodeListSet, self.selectedCodeListSet != sel_codelist_set]):
1192+
if any([not self.selectedCityGMLCodeListSet, self.selectedCityGMLCodeListSet != sel_CityGML_codelist_set]):
11931193
# Set the new value
1194-
self.selectedCodeListSet = sel_codelist_set
1194+
self.selectedCityGMLCodeListSet = sel_CityGML_codelist_set
11951195
# print("Working with Codelist set:", self.selectedCodeListSet)
11961196
# Initialize the enum_lookup_config_registry
1197-
tl_f.populate_codelist_config_registry(self, codelist_set_name=sel_codelist_set)
1197+
tl_f.populate_codelist_config_registry(self, codelist_set_name=sel_CityGML_codelist_set)
11981198

11991199
success = tl_f.add_selected_layers_to_ToC(self, layers=selected_layers)
12001200

1201+
# TO DO
1202+
#
1203+
# Add similar process for selected ADE codelist set
1204+
#
1205+
#
1206+
12011207
if not success:
12021208
QgsMessageLog.logMessage(
12031209
message="Something went wrong while importing the layer(s)",

0 commit comments

Comments
 (0)