dicogis 4.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dicogis/__about__.py +55 -0
- dicogis/__init__.py +0 -0
- dicogis/bin/audio/notification_sound_microwave_bell.wav +0 -0
- dicogis/bin/img/DicoGIS.ico +0 -0
- dicogis/bin/img/DicoGIS_logo.gif +0 -0
- dicogis/bin/img/DicoGIS_logo_200px.png +0 -0
- dicogis/cli/__init__.py +0 -0
- dicogis/cli/cmd_inventory.py +500 -0
- dicogis/cli/cmd_publish.py +170 -0
- dicogis/cli/main.py +94 -0
- dicogis/constants.py +113 -0
- dicogis/export/__init__.py +0 -0
- dicogis/export/base_serializer.py +128 -0
- dicogis/export/output_path.py +100 -0
- dicogis/export/to_json.py +165 -0
- dicogis/export/to_xlsx.py +1046 -0
- dicogis/georeaders/__init__.py +0 -0
- dicogis/georeaders/base_georeader.py +433 -0
- dicogis/georeaders/gdal_exceptions_handler.py +68 -0
- dicogis/georeaders/process_files.py +529 -0
- dicogis/georeaders/read_dxf.py +44 -0
- dicogis/georeaders/read_postgis.py +316 -0
- dicogis/georeaders/read_raster.py +152 -0
- dicogis/georeaders/read_vector_flat_dataset.py +238 -0
- dicogis/georeaders/read_vector_flat_geodatabase.py +67 -0
- dicogis/listing/__init__.py +0 -0
- dicogis/listing/geodata_listing.py +412 -0
- dicogis/locale/lang_EN.xml +75 -0
- dicogis/locale/lang_ES.xml +75 -0
- dicogis/locale/lang_FR.xml +75 -0
- dicogis/models/__init__.py +0 -0
- dicogis/models/data_format.py +45 -0
- dicogis/models/database_connection.py +162 -0
- dicogis/models/feature_attributes.py +72 -0
- dicogis/models/metadataset.py +377 -0
- dicogis/publishing/__init__.py +0 -0
- dicogis/publishing/udata.py +284 -0
- dicogis/ui/__init__.py +7 -0
- dicogis/ui/dialogs/__init__.py +1 -0
- dicogis/ui/dialogs/dlg_database_connection.py +102 -0
- dicogis/ui/dialogs/dlg_database_connection.ui +97 -0
- dicogis/ui/i18n/dicogis_es.qm +0 -0
- dicogis/ui/i18n/dicogis_fr.qm +0 -0
- dicogis/ui/main.py +154 -0
- dicogis/ui/mw_dicogis.py +1079 -0
- dicogis/ui/mw_dicogis.ui +209 -0
- dicogis/ui/qt_compat/QtCore.py +13 -0
- dicogis/ui/qt_compat/QtGui.py +13 -0
- dicogis/ui/qt_compat/QtWidgets.py +13 -0
- dicogis/ui/qt_compat/__init__.py +95 -0
- dicogis/ui/qt_compat/uic.py +12 -0
- dicogis/ui/wdg_misc_buttons.py +124 -0
- dicogis/ui/wdg_misc_buttons.ui +69 -0
- dicogis/ui/wdg_scrollable_table.py +52 -0
- dicogis/ui/wdg_scrollable_table.ui +59 -0
- dicogis/ui/wdg_tab_credits.py +87 -0
- dicogis/ui/wdg_tab_credits.ui +132 -0
- dicogis/ui/wdg_tab_database.py +147 -0
- dicogis/ui/wdg_tab_database.ui +73 -0
- dicogis/ui/wdg_tab_files.py +242 -0
- dicogis/ui/wdg_tab_files.ui +212 -0
- dicogis/ui/wdg_tab_publish.py +212 -0
- dicogis/ui/wdg_tab_publish.ui +126 -0
- dicogis/ui/wdg_tab_settings.py +295 -0
- dicogis/ui/wdg_tab_settings.ui +303 -0
- dicogis/ui/workers.py +295 -0
- dicogis/utils/__init__.py +0 -0
- dicogis/utils/app_dir.py +45 -0
- dicogis/utils/check_path.py +324 -0
- dicogis/utils/checknorris.py +97 -0
- dicogis/utils/db_conf_reader.py +96 -0
- dicogis/utils/environment.py +222 -0
- dicogis/utils/formatters.py +75 -0
- dicogis/utils/journalizer.py +146 -0
- dicogis/utils/notifier.py +127 -0
- dicogis/utils/options.py +256 -0
- dicogis/utils/paths.py +33 -0
- dicogis/utils/progress.py +83 -0
- dicogis/utils/slugger.py +71 -0
- dicogis/utils/str2bool.py +65 -0
- dicogis/utils/texts.py +142 -0
- dicogis/utils/utils.py +66 -0
- dicogis-4.0.1.dist-info/METADATA +95 -0
- dicogis-4.0.1.dist-info/RECORD +88 -0
- dicogis-4.0.1.dist-info/WHEEL +5 -0
- dicogis-4.0.1.dist-info/entry_points.txt +5 -0
- dicogis-4.0.1.dist-info/licenses/LICENSE +201 -0
- dicogis-4.0.1.dist-info/top_level.txt +1 -0
dicogis/__about__.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#! python3 # noqa: E265
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Metadata about the package to easily retrieve informations about it.
|
|
5
|
+
|
|
6
|
+
See: https://packaging.python.org/guides/single-sourcing-package-version/
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# standard lib
|
|
10
|
+
from datetime import date
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"__author__",
|
|
16
|
+
"__copyright__",
|
|
17
|
+
"__email__",
|
|
18
|
+
"__executable_name__",
|
|
19
|
+
"__license__",
|
|
20
|
+
"__summary__",
|
|
21
|
+
"__title__",
|
|
22
|
+
"__title_clean__",
|
|
23
|
+
"__uri__",
|
|
24
|
+
"__version__",
|
|
25
|
+
"__version_info__",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
__author__ = "Julien Moura"
|
|
29
|
+
__copyright__ = f"2014 - {date.today().year}, {__author__}"
|
|
30
|
+
__email__ = "dev@ingeoveritas.com"
|
|
31
|
+
__executable_name__ = "DicoGIS.exe"
|
|
32
|
+
__package_name__ = "dicogis"
|
|
33
|
+
__icon_path__ = Path("bin/img/DicoGIS_logo_200px.png")
|
|
34
|
+
__keywords__ = ["GIS", "metadata", "INSPIRE", "GDAL", "OGR", "data management"]
|
|
35
|
+
__license__ = "Apache-2.0"
|
|
36
|
+
__notification_sound_path__ = Path("bin/audio/notification_sound_microwave_bell.wav")
|
|
37
|
+
__summary__ = (
|
|
38
|
+
"Create Excel spreadsheet describing geographical data from a PostGIS "
|
|
39
|
+
"Database or a file tree structure."
|
|
40
|
+
)
|
|
41
|
+
__title__ = "DicoGIS"
|
|
42
|
+
__title_clean__ = "".join(e for e in __title__ if e.isalnum())
|
|
43
|
+
__uri_homepage__ = "https://guts.github.io/DicoGIS/"
|
|
44
|
+
__uri_repository__ = "https://github.com/Guts/DicoGIS/"
|
|
45
|
+
__uri_tracker__ = f"{__uri_repository__}issues/"
|
|
46
|
+
__uri__ = __uri_repository__
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
__version__ = "4.0.1"
|
|
50
|
+
__version_info__ = tuple(
|
|
51
|
+
[
|
|
52
|
+
int(num) if num.isdigit() else num
|
|
53
|
+
for num in __version__.replace("-", ".", 1).split(".")
|
|
54
|
+
]
|
|
55
|
+
)
|
dicogis/__init__.py
ADDED
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
dicogis/cli/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
#! python3 # noqa: E265
|
|
2
|
+
|
|
3
|
+
# ############################################################################
|
|
4
|
+
# ########## Libraries #############
|
|
5
|
+
# ##################################
|
|
6
|
+
|
|
7
|
+
# standard lib
|
|
8
|
+
import logging
|
|
9
|
+
from locale import getlocale
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import TYPE_CHECKING, Annotated
|
|
12
|
+
|
|
13
|
+
# 3rd party
|
|
14
|
+
import typer
|
|
15
|
+
from rich import print as rich_print
|
|
16
|
+
|
|
17
|
+
# project
|
|
18
|
+
from dicogis.__about__ import __package_name__
|
|
19
|
+
from dicogis.constants import SUPPORTED_FORMATS, AvailableLocales, OutputFormats
|
|
20
|
+
from dicogis.export.base_serializer import MetadatasetSerializerBase
|
|
21
|
+
from dicogis.export.output_path import determine_output_path
|
|
22
|
+
from dicogis.listing.geodata_listing import check_usable_pg_services, find_geodata_files
|
|
23
|
+
from dicogis.utils.app_dir import get_app_dir
|
|
24
|
+
from dicogis.utils.environment import (
|
|
25
|
+
GDAL_IS_AVAILABLE,
|
|
26
|
+
get_available_gdal_drivers,
|
|
27
|
+
is_format_supported_by_gdal,
|
|
28
|
+
)
|
|
29
|
+
from dicogis.utils.journalizer import LogManager
|
|
30
|
+
from dicogis.utils.notifier import send_system_notify
|
|
31
|
+
from dicogis.utils.texts import TextsManager
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# GDAL is an optional dependency (see pyproject.toml `gdal` extra): importing the
|
|
35
|
+
# georeaders modules here at module level would break `dicogis-cli` entirely
|
|
36
|
+
# (even --help/--version) when GDAL isn't installed, e.g. under pipx. So the
|
|
37
|
+
# imports below are deferred into `inventory()`, guarded by GDAL_IS_AVAILABLE.
|
|
38
|
+
# Type-checking-only imports are not executed at runtime, so they are safe here.
|
|
39
|
+
if TYPE_CHECKING:
|
|
40
|
+
from dicogis.georeaders.read_postgis import ReadPostGIS
|
|
41
|
+
|
|
42
|
+
# ############################################################################
|
|
43
|
+
# ########## Globals ###############
|
|
44
|
+
# ##################################
|
|
45
|
+
|
|
46
|
+
state = {"verbose": False}
|
|
47
|
+
logger = logging.getLogger(__name__)
|
|
48
|
+
default_formats = ",".join([f.name for f in SUPPORTED_FORMATS])
|
|
49
|
+
# ############################################################################
|
|
50
|
+
# ########## Functions #############
|
|
51
|
+
# ##################################
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def serialize_pg_service_layers(
|
|
55
|
+
sgbd_reader: "ReadPostGIS",
|
|
56
|
+
output_serializer: MetadatasetSerializerBase,
|
|
57
|
+
) -> int:
|
|
58
|
+
"""Serialize the metadata of every layer exposed by a connected PostGIS service.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
sgbd_reader: PostGIS reader with an already opened connection
|
|
62
|
+
output_serializer: serializer to write each layer's metadata into
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
number of layers read from this service
|
|
66
|
+
"""
|
|
67
|
+
layers_count: int = sgbd_reader.conn.GetLayerCount()
|
|
68
|
+
logger.info(f"{layers_count} tables found in PostGIS database.")
|
|
69
|
+
|
|
70
|
+
for idx_layer in range(layers_count):
|
|
71
|
+
layer = sgbd_reader.conn.GetLayerByIndex(idx_layer)
|
|
72
|
+
metadataset = sgbd_reader.infos_dataset(layer=layer)
|
|
73
|
+
logger.info(f"Table examined: {metadataset.name}")
|
|
74
|
+
output_serializer.serialize_metadaset(metadataset=metadataset)
|
|
75
|
+
logger.debug("Layer metadata stored into workbook.")
|
|
76
|
+
|
|
77
|
+
return layers_count
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def inventory(
|
|
81
|
+
input_folder: Annotated[
|
|
82
|
+
Path | None,
|
|
83
|
+
typer.Option(
|
|
84
|
+
dir_okay=True,
|
|
85
|
+
envvar="DICOGIS_START_FOLDER",
|
|
86
|
+
file_okay=False,
|
|
87
|
+
help="Starting folder for listing files. "
|
|
88
|
+
"The folder must exist and be readable. "
|
|
89
|
+
"If None, files listing is ignored. Defaults to None.",
|
|
90
|
+
readable=True,
|
|
91
|
+
resolve_path=True,
|
|
92
|
+
),
|
|
93
|
+
] = None,
|
|
94
|
+
formats: Annotated[
|
|
95
|
+
str,
|
|
96
|
+
typer.Option(
|
|
97
|
+
envvar="DICOGIS_FORMATS_LIST",
|
|
98
|
+
help="List of files extensions to include into listing. "
|
|
99
|
+
"Must be part of supported formats. "
|
|
100
|
+
"Defaults to every supported formats.",
|
|
101
|
+
),
|
|
102
|
+
] = default_formats,
|
|
103
|
+
pg_services: Annotated[
|
|
104
|
+
list[str] | None,
|
|
105
|
+
typer.Option(
|
|
106
|
+
envvar="DICOGIS_POSTGRES_SERVICES",
|
|
107
|
+
help="name(s) of PostgreSQL services to use. It's a repeatable option. "
|
|
108
|
+
"If None, database listing is ignored. Defaults to None.",
|
|
109
|
+
),
|
|
110
|
+
] = None,
|
|
111
|
+
output_path: Annotated[
|
|
112
|
+
Path | None,
|
|
113
|
+
typer.Option(
|
|
114
|
+
dir_okay=True,
|
|
115
|
+
envvar="DICOGIS_OUTPUT_FILEPATH",
|
|
116
|
+
file_okay=True,
|
|
117
|
+
help="Output file (excel) or folder (json, udata). If not set, it's "
|
|
118
|
+
"created in the current working directory (cwd), named after the input "
|
|
119
|
+
"folder or PostgreSQL services and the date.",
|
|
120
|
+
resolve_path=True,
|
|
121
|
+
writable=True,
|
|
122
|
+
),
|
|
123
|
+
] = None,
|
|
124
|
+
output_format: Annotated[
|
|
125
|
+
OutputFormats,
|
|
126
|
+
typer.Option(
|
|
127
|
+
case_sensitive=False,
|
|
128
|
+
envvar="DICOGIS_OUTPUT_FORMAT",
|
|
129
|
+
help="Output format: excel for a Microsoft Excel workbook (.xlsx), json "
|
|
130
|
+
"for one JSON file per dataset, udata for JSON files shaped for a uData "
|
|
131
|
+
"catalog (see the publish command).",
|
|
132
|
+
),
|
|
133
|
+
] = "excel",
|
|
134
|
+
opt_notify_sound: Annotated[
|
|
135
|
+
bool,
|
|
136
|
+
typer.Option(
|
|
137
|
+
envvar="DICOGIS_ENABLE_NOTIFICATION_SOUND",
|
|
138
|
+
help="Enable/disable notification's sound at the end of processing.",
|
|
139
|
+
),
|
|
140
|
+
] = True,
|
|
141
|
+
opt_open_output: Annotated[
|
|
142
|
+
bool,
|
|
143
|
+
typer.Option(
|
|
144
|
+
envvar="DICOGIS_OPEN_OUTPUT",
|
|
145
|
+
help="Enable/disable auto opening output file when processing has finished.",
|
|
146
|
+
),
|
|
147
|
+
] = True,
|
|
148
|
+
opt_prettify_size: Annotated[
|
|
149
|
+
bool,
|
|
150
|
+
typer.Option(
|
|
151
|
+
envvar="DICOGIS_EXPORT_SIZE_PRETTIFY",
|
|
152
|
+
help="Enable prettified files size in output. "
|
|
153
|
+
"Example: 1 ko instead of 1024.",
|
|
154
|
+
),
|
|
155
|
+
] = False,
|
|
156
|
+
opt_quick_fail: Annotated[
|
|
157
|
+
bool,
|
|
158
|
+
typer.Option(
|
|
159
|
+
envvar="DICOGIS_QUICK_FAIL",
|
|
160
|
+
help="Enable quick fail instead of passing errors. Useful for debug.",
|
|
161
|
+
),
|
|
162
|
+
] = False,
|
|
163
|
+
opt_raw_path: Annotated[
|
|
164
|
+
bool,
|
|
165
|
+
typer.Option(
|
|
166
|
+
envvar="DICOGIS_EXPORT_RAW_PATH",
|
|
167
|
+
help="Enable raw path instead of hyperlink in formats which support it.",
|
|
168
|
+
),
|
|
169
|
+
] = False,
|
|
170
|
+
opt_parallel_scan: Annotated[
|
|
171
|
+
bool,
|
|
172
|
+
typer.Option(
|
|
173
|
+
envvar="DICOGIS_LISTING_PARALLEL_SCAN",
|
|
174
|
+
help="Scan top-level subfolders in parallel worker threads instead of "
|
|
175
|
+
"a single sequential walk. Only beneficial on high-latency storage "
|
|
176
|
+
"(e.g. network-mounted shares) with a deep folder tree - can be slower "
|
|
177
|
+
"on local/fast storage. Disabled by default.",
|
|
178
|
+
),
|
|
179
|
+
] = False,
|
|
180
|
+
listing_max_workers: Annotated[
|
|
181
|
+
int | None,
|
|
182
|
+
typer.Option(
|
|
183
|
+
envvar="DICOGIS_LISTING_MAX_WORKERS",
|
|
184
|
+
help="Maximum worker threads used when --opt-parallel-scan is enabled. "
|
|
185
|
+
"Ignored otherwise. Defaults to ThreadPoolExecutor's own default "
|
|
186
|
+
"(based on CPU count).",
|
|
187
|
+
),
|
|
188
|
+
] = None,
|
|
189
|
+
language: Annotated[
|
|
190
|
+
AvailableLocales | None,
|
|
191
|
+
typer.Option(
|
|
192
|
+
case_sensitive=False,
|
|
193
|
+
envvar="DICOGIS_DEFAULT_LANGUAGE",
|
|
194
|
+
help="Force language to use. If not set, the current default locale is used.",
|
|
195
|
+
),
|
|
196
|
+
] = None,
|
|
197
|
+
verbose: Annotated[
|
|
198
|
+
bool,
|
|
199
|
+
typer.Option(
|
|
200
|
+
envvar="DICOGIS_DEBUG",
|
|
201
|
+
help="Increase verbosity to show debug logs.",
|
|
202
|
+
),
|
|
203
|
+
] = False,
|
|
204
|
+
):
|
|
205
|
+
"""List, extract metadata from geospatial datasets (files or database) and store it
|
|
206
|
+
as files.
|
|
207
|
+
|
|
208
|
+
Make an inventory of geodata files starting from a folder and/or
|
|
209
|
+
databases using connection listed in pg_service.conf and store everything in an
|
|
210
|
+
output file.
|
|
211
|
+
\f
|
|
212
|
+
Args:
|
|
213
|
+
input_folder: starting folder for files. Defaults to None.
|
|
214
|
+
formats: List of files extensions to include into listing. Defaults to every
|
|
215
|
+
supported formats.
|
|
216
|
+
pg_services: name(s) of PostgreSQL services to use. Repeatable. If None,
|
|
217
|
+
database listing is ignored. Defaults to None.
|
|
218
|
+
language: language code to use. If not set, the current default locale is used.
|
|
219
|
+
Defaults to None.
|
|
220
|
+
verbose: enable verbose mode. Defaults to False.
|
|
221
|
+
"""
|
|
222
|
+
app_dir = get_app_dir()
|
|
223
|
+
# start logging
|
|
224
|
+
if verbose:
|
|
225
|
+
state["verbose"] = True
|
|
226
|
+
|
|
227
|
+
logmngr = LogManager(
|
|
228
|
+
console_level=logging.DEBUG if verbose else logging.WARNING,
|
|
229
|
+
file_level=logging.DEBUG if verbose else logging.INFO,
|
|
230
|
+
label=f"{__package_name__}-cli-inventory",
|
|
231
|
+
folder=Path(app_dir).joinpath("logs"),
|
|
232
|
+
)
|
|
233
|
+
# add headers
|
|
234
|
+
logmngr.headers()
|
|
235
|
+
logger.debug(f"DicoGIS working folder: {app_dir}")
|
|
236
|
+
logger.debug(
|
|
237
|
+
f"CLI passed parameters: {input_folder=} - {formats=} - {pg_services=} - "
|
|
238
|
+
f"{verbose=} -{language=}"
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
# check minimal parameters
|
|
242
|
+
# note: pg_services defaults to [] not to None
|
|
243
|
+
if input_folder is None and not pg_services:
|
|
244
|
+
rich_print(
|
|
245
|
+
"[bold red]Error: You must provide at least one of the options: "
|
|
246
|
+
"input_folder or pg_services[/bold red]"
|
|
247
|
+
)
|
|
248
|
+
raise typer.Exit(code=1)
|
|
249
|
+
|
|
250
|
+
if not GDAL_IS_AVAILABLE:
|
|
251
|
+
rich_print(
|
|
252
|
+
"[bold red]Error: GDAL (and its Python bindings) is required to run an "
|
|
253
|
+
"inventory but is not installed.[/bold red]\n"
|
|
254
|
+
"See https://guts.github.io/DicoGIS/usage/installation.html for how to "
|
|
255
|
+
"install it, including with pipx."
|
|
256
|
+
)
|
|
257
|
+
raise typer.Exit(code=1)
|
|
258
|
+
|
|
259
|
+
# imported here rather than at module level: GDAL is an optional dependency
|
|
260
|
+
# (see pyproject.toml `gdal` extra), so importing it eagerly would break every
|
|
261
|
+
# dicogis-cli invocation, even --help/--version, when GDAL isn't installed
|
|
262
|
+
from dicogis.georeaders.process_files import ProcessingFiles
|
|
263
|
+
from dicogis.georeaders.read_postgis import ReadPostGIS
|
|
264
|
+
|
|
265
|
+
# drop requested formats the installed GDAL build can't actually read
|
|
266
|
+
# (some drivers, e.g. Geoconcept or ECW, are optional/plugin drivers not
|
|
267
|
+
# included in every GDAL packaging)
|
|
268
|
+
requested_formats = [fmt.strip() for fmt in formats.split(",") if fmt.strip()]
|
|
269
|
+
available_gdal_drivers = get_available_gdal_drivers()
|
|
270
|
+
unsupported_formats = [
|
|
271
|
+
fmt
|
|
272
|
+
for fmt in requested_formats
|
|
273
|
+
if not is_format_supported_by_gdal(fmt, available_gdal_drivers)
|
|
274
|
+
]
|
|
275
|
+
if unsupported_formats:
|
|
276
|
+
rich_print(
|
|
277
|
+
"[bold yellow]Warning: the installed GDAL build does not support the "
|
|
278
|
+
f"following requested format(s), they will be ignored: "
|
|
279
|
+
f"{', '.join(unsupported_formats)}[/bold yellow]"
|
|
280
|
+
)
|
|
281
|
+
formats = ",".join(
|
|
282
|
+
fmt for fmt in requested_formats if fmt not in unsupported_formats
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
# i18n
|
|
286
|
+
if language is None:
|
|
287
|
+
language = getlocale()
|
|
288
|
+
localized_strings = TextsManager().load_texts(language_code=language)
|
|
289
|
+
|
|
290
|
+
# output format
|
|
291
|
+
|
|
292
|
+
# look for geographic data files
|
|
293
|
+
if input_folder is not None:
|
|
294
|
+
output_path = determine_output_path(
|
|
295
|
+
output_path=output_path,
|
|
296
|
+
output_format=output_format,
|
|
297
|
+
input_folder=input_folder,
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
output_serializer = MetadatasetSerializerBase.get_serializer_from_parameters(
|
|
301
|
+
format_or_serializer=output_format,
|
|
302
|
+
localized_strings=localized_strings,
|
|
303
|
+
output_path=output_path,
|
|
304
|
+
opt_prettify_size=opt_prettify_size,
|
|
305
|
+
opt_raw_path=opt_raw_path,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
li_vectors = []
|
|
309
|
+
(
|
|
310
|
+
num_folders,
|
|
311
|
+
li_shapefiles,
|
|
312
|
+
li_mapinfo_tab,
|
|
313
|
+
li_kml,
|
|
314
|
+
li_gml,
|
|
315
|
+
li_geojson,
|
|
316
|
+
li_geotiff,
|
|
317
|
+
li_gxt,
|
|
318
|
+
li_raster,
|
|
319
|
+
li_file_database_esri,
|
|
320
|
+
li_dxf,
|
|
321
|
+
li_dwg,
|
|
322
|
+
li_dgn,
|
|
323
|
+
li_cdao,
|
|
324
|
+
li_file_databases,
|
|
325
|
+
li_file_database_spatialite,
|
|
326
|
+
li_file_database_geopackage,
|
|
327
|
+
) = find_geodata_files(
|
|
328
|
+
start_folder=input_folder,
|
|
329
|
+
parallel_scan=opt_parallel_scan,
|
|
330
|
+
max_workers=listing_max_workers,
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
rich_print(
|
|
334
|
+
"Found: "
|
|
335
|
+
f"{len(li_shapefiles)} shapefiles - "
|
|
336
|
+
f"{len(li_mapinfo_tab)} tables (MapInfo) - "
|
|
337
|
+
f"{len(li_kml)} KML - "
|
|
338
|
+
f"{len(li_gml)} GML - "
|
|
339
|
+
f"{len(li_geojson)} GeoJSON - "
|
|
340
|
+
f"{len(li_gxt)} GXT - "
|
|
341
|
+
f"{len(li_raster)} rasters - "
|
|
342
|
+
f"{len(li_file_databases)} file databases - "
|
|
343
|
+
f"{len(li_cdao)} CAO/DAO - "
|
|
344
|
+
f"in {num_folders}{localized_strings.get('log_numfold')}"
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
# grouping vectors lists
|
|
348
|
+
li_vectors.extend(li_shapefiles)
|
|
349
|
+
li_vectors.extend(li_mapinfo_tab)
|
|
350
|
+
li_vectors.extend(li_kml)
|
|
351
|
+
li_vectors.extend(li_gml)
|
|
352
|
+
li_vectors.extend(li_geojson)
|
|
353
|
+
li_vectors.extend(li_gxt)
|
|
354
|
+
|
|
355
|
+
# check if there are some layers into the folder structure
|
|
356
|
+
if not (
|
|
357
|
+
len(li_vectors) + len(li_raster) + len(li_file_databases) + len(li_cdao)
|
|
358
|
+
):
|
|
359
|
+
logger.error(localized_strings.get("nodata"))
|
|
360
|
+
raise typer.Exit(1)
|
|
361
|
+
|
|
362
|
+
# instanciate geofiles processor
|
|
363
|
+
geofiles_processor = ProcessingFiles(
|
|
364
|
+
serializer=output_serializer,
|
|
365
|
+
localized_strings=localized_strings,
|
|
366
|
+
# list by tabs
|
|
367
|
+
li_vectors=li_vectors,
|
|
368
|
+
li_rasters=li_raster,
|
|
369
|
+
li_file_databases=li_file_databases,
|
|
370
|
+
li_cdao=li_cdao,
|
|
371
|
+
# list by formats
|
|
372
|
+
li_dxf=li_dxf,
|
|
373
|
+
li_flat_geodatabase_esri_filegdb=li_file_database_esri,
|
|
374
|
+
li_flat_geodatabase_spatialite=li_file_database_spatialite,
|
|
375
|
+
li_flat_geodatabase_geopackage=li_file_database_geopackage,
|
|
376
|
+
li_geojson=li_geojson,
|
|
377
|
+
li_gml=li_gml,
|
|
378
|
+
li_gxt=li_gxt,
|
|
379
|
+
li_kml=li_kml,
|
|
380
|
+
li_mapinfo_tab=li_mapinfo_tab,
|
|
381
|
+
li_shapefiles=li_shapefiles,
|
|
382
|
+
# options
|
|
383
|
+
opt_analyze_cdao="dgn" in formats,
|
|
384
|
+
opt_analyze_esri_filegdb="file_geodatabase_esri" in formats,
|
|
385
|
+
opt_analyze_geopackage="file_geodatabase_geopackage" in formats,
|
|
386
|
+
opt_analyze_geojson="geojson" in formats,
|
|
387
|
+
opt_analyze_gml="gml" in formats,
|
|
388
|
+
opt_analyze_gxt="gxt" in formats,
|
|
389
|
+
opt_analyze_kml="kml" in formats,
|
|
390
|
+
opt_analyze_mapinfo_tab="mapinfo_tab" in formats,
|
|
391
|
+
opt_analyze_raster=any(
|
|
392
|
+
[fmt in formats for fmt in ("ecw", "geotiff", "jpeg")]
|
|
393
|
+
),
|
|
394
|
+
opt_analyze_shapefiles="esri_shapefile" in formats,
|
|
395
|
+
opt_analyze_spatialite="file_geodatabase_spatialite" in formats,
|
|
396
|
+
# misc
|
|
397
|
+
opt_quick_fail=opt_quick_fail,
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
# sheets and progress bar
|
|
401
|
+
total_files = geofiles_processor.count_files_to_process()
|
|
402
|
+
rich_print(f"Start analyzing {total_files} files...")
|
|
403
|
+
|
|
404
|
+
geofiles_processor.process_datasets_in_queue()
|
|
405
|
+
|
|
406
|
+
send_system_notify(
|
|
407
|
+
notification_title="DicoGIS analysis ended",
|
|
408
|
+
notification_message=f"DicoGIS successfully processed {total_files} files.",
|
|
409
|
+
notification_sound=opt_notify_sound,
|
|
410
|
+
)
|
|
411
|
+
if opt_open_output:
|
|
412
|
+
typer.launch(url=f"{output_path.resolve()}")
|
|
413
|
+
|
|
414
|
+
# look for geographic database
|
|
415
|
+
if pg_services:
|
|
416
|
+
# check if at least one pg_service name is referenced
|
|
417
|
+
pg_services = check_usable_pg_services(requested_pg_services=pg_services)
|
|
418
|
+
if not len(pg_services):
|
|
419
|
+
logger.error("None of the specified pg_services is available.")
|
|
420
|
+
raise typer.Exit(1)
|
|
421
|
+
|
|
422
|
+
output_path = determine_output_path(
|
|
423
|
+
output_path=output_path,
|
|
424
|
+
output_format=output_format,
|
|
425
|
+
pg_services=pg_services,
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
output_serializer = MetadatasetSerializerBase.get_serializer_from_parameters(
|
|
429
|
+
format_or_serializer=output_format,
|
|
430
|
+
localized_strings=localized_strings,
|
|
431
|
+
output_path=output_path,
|
|
432
|
+
opt_prettify_size=opt_prettify_size,
|
|
433
|
+
opt_raw_path=opt_raw_path,
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
rich_print("Start looking for geographic table in PostGIS...")
|
|
437
|
+
# configure output workbook
|
|
438
|
+
output_serializer.pre_serializing(has_sgbd=True)
|
|
439
|
+
|
|
440
|
+
# counted across every service, not just the last one
|
|
441
|
+
total_layers: int = 0
|
|
442
|
+
failed_pg_services: list[str] = []
|
|
443
|
+
|
|
444
|
+
for pg_service in pg_services:
|
|
445
|
+
rich_print(f"Start processing using PostgreSQL service: {pg_service}")
|
|
446
|
+
|
|
447
|
+
# testing connection settings
|
|
448
|
+
sgbd_reader = ReadPostGIS(
|
|
449
|
+
service=pg_service, localized_strings=localized_strings
|
|
450
|
+
)
|
|
451
|
+
sgbd_reader.get_connection()
|
|
452
|
+
|
|
453
|
+
# check connection state
|
|
454
|
+
if sgbd_reader.conn is None:
|
|
455
|
+
fail_reason = sgbd_reader.db_connection.state_msg
|
|
456
|
+
logger.error(
|
|
457
|
+
f"Connection failed using pg_service {pg_service}. Trace: {fail_reason}."
|
|
458
|
+
)
|
|
459
|
+
# the CLI installs no console log handler, so a failed service
|
|
460
|
+
# would otherwise only show up in the log file
|
|
461
|
+
rich_print(
|
|
462
|
+
"[bold yellow]Warning: connection failed using PostgreSQL service "
|
|
463
|
+
f"{pg_service}, it is skipped. Trace: {fail_reason}[/bold yellow]"
|
|
464
|
+
)
|
|
465
|
+
failed_pg_services.append(pg_service)
|
|
466
|
+
continue
|
|
467
|
+
|
|
468
|
+
# show must go on: parse the layers of this service
|
|
469
|
+
total_layers += serialize_pg_service_layers(
|
|
470
|
+
sgbd_reader=sgbd_reader, output_serializer=output_serializer
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
# nothing could be read: fail instead of writing an empty inventory and
|
|
474
|
+
# reporting success, which a calling script would take at face value
|
|
475
|
+
if len(failed_pg_services) == len(pg_services):
|
|
476
|
+
logger.error(
|
|
477
|
+
"Connection failed for every requested PostgreSQL service: "
|
|
478
|
+
f"{', '.join(failed_pg_services)}."
|
|
479
|
+
)
|
|
480
|
+
rich_print(
|
|
481
|
+
"[bold red]Error: connection failed for every requested PostgreSQL "
|
|
482
|
+
f"service ({', '.join(failed_pg_services)}). No output written."
|
|
483
|
+
"[/bold red]"
|
|
484
|
+
)
|
|
485
|
+
raise typer.Exit(code=1)
|
|
486
|
+
|
|
487
|
+
output_serializer.post_serializing()
|
|
488
|
+
|
|
489
|
+
send_system_notify(
|
|
490
|
+
notification_title="DicoGIS analysis ended",
|
|
491
|
+
notification_message="DicoGIS successfully processed "
|
|
492
|
+
f"{total_layers} PostGIS tables. "
|
|
493
|
+
"\nOpen the application to save the workbook.",
|
|
494
|
+
notification_sound=opt_notify_sound,
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
if opt_open_output:
|
|
498
|
+
typer.launch(url=f"{output_path.resolve()}")
|
|
499
|
+
|
|
500
|
+
logger.info(f"Logs stored in {Path(app_dir).joinpath('logs')}")
|