From 622816db41e99d1ca29042225d74865dbe6c61e0 Mon Sep 17 00:00:00 2001 From: signedav Date: Tue, 11 Nov 2025 16:22:38 +0100 Subject: [PATCH 1/6] Provide file url in IliCache istead of just model and version --- modelbaker/iliwrapper/ilicache.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modelbaker/iliwrapper/ilicache.py b/modelbaker/iliwrapper/ilicache.py index 37856a15..028f803b 100644 --- a/modelbaker/iliwrapper/ilicache.py +++ b/modelbaker/iliwrapper/ilicache.py @@ -245,6 +245,9 @@ def _process_informationfile(self, file, netloc, url): model["version"] = self.get_element_text( model_metadata.find("ili23:Version", self.ns) ) + model["file"] = self.get_element_text( + model_metadata.find("ili23:File", self.ns) + ) model["repository"] = netloc repo_models.append(model) @@ -262,6 +265,9 @@ def _process_informationfile(self, file, netloc, url): model["version"] = self.get_element_text( model_metadata.find("ili23:Version", self.ns) ) + model["file"] = self.get_element_text( + model_metadata.find("ili23:File", self.ns) + ) model["repository"] = netloc repo_models.append(model) @@ -375,6 +381,7 @@ class IliModelItemModel(QStandardItemModel): class Roles(Enum): ILIREPO = Qt.ItemDataRole.UserRole + 1 VERSION = Qt.ItemDataRole.UserRole + 2 + FILE = Qt.ItemDataRole.UserRole + 3 def __int__(self): return self.value @@ -400,6 +407,7 @@ def set_repositories(self, repositories): ) # considered in completer item.setData(model["repository"], int(IliModelItemModel.Roles.ILIREPO)) item.setData(model["version"], int(IliModelItemModel.Roles.VERSION)) + item.setData(model["file"], int(IliModelItemModel.Roles.FILE)) names.append(model["name"]) self.appendRow(item) From 1135472c2ee430cf5988dd6ddfb0dc585a4b9337 Mon Sep 17 00:00:00 2001 From: signedav Date: Tue, 11 Nov 2025 16:42:40 +0100 Subject: [PATCH 2/6] Ili2c integration --- modelbaker/iliwrapper/ili2dbconfig.py | 30 ++++++++++ modelbaker/iliwrapper/ili2dbtools.py | 10 ++++ modelbaker/iliwrapper/ili2dbutils.py | 76 +++++++++++++++++++++++++- modelbaker/iliwrapper/ilicompiler.py | 44 +++++++++++++++ modelbaker/iliwrapper/iliexecutable.py | 6 +- 5 files changed, 162 insertions(+), 4 deletions(-) create mode 100644 modelbaker/iliwrapper/ilicompiler.py diff --git a/modelbaker/iliwrapper/ili2dbconfig.py b/modelbaker/iliwrapper/ili2dbconfig.py index 487d832f..e03b18d4 100644 --- a/modelbaker/iliwrapper/ili2dbconfig.py +++ b/modelbaker/iliwrapper/ili2dbconfig.py @@ -587,3 +587,33 @@ def to_ili2db_args(self, extra_args=[], with_action=True): self.append_args(args, Ili2DbCommandConfiguration.to_ili2db_args(self)) return args + + +class Ili2CCommandConfiguration: + def __init__(self, other=None): + if not isinstance(other, Ili2CCommandConfiguration): + self.base_configuration = BaseConfiguration() + + self.oIMD16 = True + self.imdfile = "" + self.ilifile = "" + else: + # We got an 'other' object from which we'll get parameters + self.__dict__ = other.__dict__.copy() + + def append_args(self, args, values): + args += values + + def to_ili2c_args(self): + + args = self.base_configuration.to_ili2db_args(False, False) + + if self.oIMD16: + self.append_args(args, ["-oIMD16"]) + + if self.imdfile: + self.append_args(args, ["--out", self.imdfile]) + + if self.ilifile: + self.append_args(args, [self.ilifile]) + return args diff --git a/modelbaker/iliwrapper/ili2dbtools.py b/modelbaker/iliwrapper/ili2dbtools.py index 2d247545..22b52156 100644 --- a/modelbaker/iliwrapper/ili2dbtools.py +++ b/modelbaker/iliwrapper/ili2dbtools.py @@ -55,3 +55,13 @@ def get_tool_url(tool, db_ili_version): ) return "" + + +def get_ili2c_tool_version(): + return "5.6.6" + + +def get_ili2c_tool_url(): + return "https://downloads.interlis.ch/ili2c/ili2c-{version}.zip".format( + version=get_ili2c_tool_version() + ) diff --git a/modelbaker/iliwrapper/ili2dbutils.py b/modelbaker/iliwrapper/ili2dbutils.py index de58d724..1900882e 100644 --- a/modelbaker/iliwrapper/ili2dbutils.py +++ b/modelbaker/iliwrapper/ili2dbutils.py @@ -28,7 +28,12 @@ from ..utils.qt_utils import NetworkError, download_file from .globals import DbIliMode -from .ili2dbtools import get_tool_url, get_tool_version +from .ili2dbtools import ( + get_ili2c_tool_url, + get_ili2c_tool_version, + get_tool_url, + get_tool_version, +) def get_ili2db_bin(tool, db_ili_version, stdout, stderr): @@ -125,6 +130,75 @@ def get_ili2db_bin(tool, db_ili_version, stdout, stderr): return ili2db_file +def get_ili2c_bin(stdout, stderr): + ili_tool_version = get_ili2c_tool_version() + ili_tool_url = get_ili2c_tool_url() + + dir_path = os.path.dirname(os.path.realpath(__file__)) + ili2c_dir = "ili2c-{}".format(ili_tool_version) + + ili2c_file = os.path.join( + dir_path, + "bin", + ili2c_dir, + "ili2c.jar".format(version=ili_tool_version), + ) + + if not os.path.isfile(ili2c_file): + try: + os.makedirs(os.path.join(dir_path, "bin", ili2c_dir), exist_ok=True) + except FileExistsError: + pass + + tmpfile = tempfile.NamedTemporaryFile(suffix=".zip", delete=False) + + stdout.emit( + QCoreApplication.translate( + "ili2dbutils", + "Downloading ili2c version {}…".format(ili_tool_version), + ) + ) + + try: + download_file( + ili_tool_url, + tmpfile.name, + on_progress=lambda received, total: stdout.emit("."), + ) + except NetworkError as e: + stderr.emit( + QCoreApplication.translate( + "ili2dbutils", + 'Could not download ili2c\n\n Error: {error}\n\nFile "{file}" not found. Please download and extract ili2c'.format( + ili2db_url=ili_tool_url, + error=e.msg, + file=ili2c_file, + ), + ) + ) + return None + + try: + with zipfile.ZipFile(tmpfile.name, "r") as z: + z.extractall(os.path.join(dir_path, "bin", ili2c_dir)) + except zipfile.BadZipFile: + # We will realize soon enough that the files were not extracted + pass + + if not os.path.isfile(ili2c_file): + stderr.emit( + QCoreApplication.translate( + "ili2dbutils", + 'File "{file}" not found. Please download and extract ili2c.'.format( + file=ili2c_file, ili2c_url=ili_tool_url + ), + ) + ) + return None + + return ili2c_file + + def get_all_modeldir_in_path(path, lambdafunction=None): all_subdirs = [path[0] for path in os.walk(path)] # include path # Make sure path is included, it can be a special string like `%XTF_DIR` diff --git a/modelbaker/iliwrapper/ilicompiler.py b/modelbaker/iliwrapper/ilicompiler.py new file mode 100644 index 00000000..2a5ac72f --- /dev/null +++ b/modelbaker/iliwrapper/ilicompiler.py @@ -0,0 +1,44 @@ +""" +/*************************************************************************** + ------------------- + begin : 25/11/11 + git sha : :%H$ + copyright : (C) 2017 by OPENGIS.ch + email : info@opengis.ch + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +""" +from .ili2dbconfig import Ili2CCommandConfiguration +from .ili2dbutils import get_ili2c_bin +from .iliexecutable import IliExecutable + + +class IliCompiler(IliExecutable): + def __init__(self, parent=None): + super().__init__(parent) + + def _create_config(self) -> Ili2CCommandConfiguration: + """Creates the configuration that will be used by *run* method. + :return: ili2c configuration""" + return Ili2CCommandConfiguration() + + def _args(self): + """Gets the list of ili2c arguments from configuration. + :return: ili2c arguments list. + :rtype: list + """ + return self.configuration.to_ili2c_args() + + def _ili2_jar_arg(self): + ili2c_bin = get_ili2c_bin(self.stdout, self.stderr) + if not ili2c_bin: + return self.ILI2C_NOT_FOUND + return ["-jar", ili2c_bin] diff --git a/modelbaker/iliwrapper/iliexecutable.py b/modelbaker/iliwrapper/iliexecutable.py index 34f61804..a6514c65 100644 --- a/modelbaker/iliwrapper/iliexecutable.py +++ b/modelbaker/iliwrapper/iliexecutable.py @@ -79,7 +79,7 @@ def _args(self, hide_password): return get_ili2db_args(self.configuration, hide_password) - def _ili2db_jar_arg(self): + def _ili2_jar_arg(self): ili2db_bin = get_ili2db_bin( self.tool, self._get_ili2db_version(), self.stdout, self.stderr ) @@ -95,7 +95,7 @@ def _escaped_arg(self, argument): return argument def command(self, hide_password): - ili2db_jar_arg = self._ili2db_jar_arg() + ili2db_jar_arg = self._ili2_jar_arg() if ili2db_jar_arg == self.ILI2DB_NOT_FOUND: return "ili2db tool not found!" @@ -142,7 +142,7 @@ def run(self, edited_command=None): ) if not edited_command: - ili2db_jar_arg = self._ili2db_jar_arg() + ili2db_jar_arg = self._ili2_jar_arg() if ili2db_jar_arg == self.ILI2DB_NOT_FOUND: return self.ILI2DB_NOT_FOUND args = self._args(False) From 6786cb2b4df26ad8d12ae748b2c422f850fa6e84 Mon Sep 17 00:00:00 2001 From: signedav Date: Thu, 13 Nov 2025 09:52:28 +0100 Subject: [PATCH 3/6] Pythonizer light --- modelbaker/pythonizer/pythonizer.py | 135 ++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 modelbaker/pythonizer/pythonizer.py diff --git a/modelbaker/pythonizer/pythonizer.py b/modelbaker/pythonizer/pythonizer.py new file mode 100644 index 00000000..475a03f1 --- /dev/null +++ b/modelbaker/pythonizer/pythonizer.py @@ -0,0 +1,135 @@ +import datetime +import os + +from qgis.core import Qgis +from qgis.PyQt.QtCore import QFile, QObject, QStandardPaths + +from ..iliwrapper import iliexecutable +from ..iliwrapper.ili2dbconfig import Ili2CCommandConfiguration +from ..iliwrapper.ili2dbutils import JavaNotFoundError +from ..libs.ili2py.mappers.helpers import Index +from ..libs.ili2py.readers.interlis_24.ilismeta16.xsdata import Imd16Reader +from ..libs.ili2py.writers.py.python_structure import Library +from ..utils import db_utils +from ..utils.globals import default_log_function +from ..utils.qt_utils import NetworkError, download_file + + +class Pythonizer(QObject): + """ + This is pure Tinkerlis. pythonizer function does the ili2py stuff. The rest is kind of a utils api. + """ + + def __init__(self, log_function=None) -> None: + QObject.__init__(self) + + self.log_function = log_function if log_function else default_log_function + + if not log_function: + self.log_function = default_log_function + + def pythonize(self, imd_file): + reader = Imd16Reader() + metamodel = reader.read(imd_file) + index = Index(metamodel.datasection) + library_name = index.types_bucket["Model"][-1].name + library = Library.from_imd(metamodel.datasection.ModelData, index, library_name) + return index, library + + def compile(self, base_configuration, ili_file): + compiler = iliexecutable.IliCompiler() + + configuration = Ili2CCommandConfiguration() + configuration.base_configuration = base_configuration + configuration.ilifile = ili_file + configuration.imdfile = os.path.join( + QStandardPaths.writableLocation( + QStandardPaths.StandardLocation.TempLocation + ), + "temp_imd_{:%Y%m%d%H%M%S%f}.imd".format(datetime.datetime.now()), + ) + + compiler.configuration = configuration + + compiler.stdout.connect(self.on_ili_stdout) + compiler.stderr.connect(self.on_ili_stderr) + compiler.process_started.connect(self.on_ili_process_started) + compiler.process_finished.connect(self.on_ili_process_finished) + result = True + + try: + compiler_result = compiler.run() + if compiler_result != compiler.SUCCESS: + result = False + except JavaNotFoundError as e: + self.log_function( + self.tr("Java not found error: {}").format(e.error_string), + Qgis.MessageLevel.Warning, + ) + result = False + + return result, compiler.configuration.imdfile + + def model_files_generated_from_db(self, configuration, model_list=[]): + model_files = [] + # this could be improved i guess, we already have the models read from the same function. but yes. poc etc. + db_connector = db_utils.get_db_connector(configuration) + db_connector.get_models() + model_records = db_connector.get_models() + for record in model_records: + name = record["modelname"].split("{")[0] + # on an empty model_list we create a file for every found model + if len(model_list) == 0 or name in model_list: + modelfilepath = self._temp_ilifile(name) + file = QFile(modelfilepath) + if file.open(QFile.OpenModeFlag.WriteOnly): + file.write(record["content"].encode("utf-8")) + file.close() + model_files.append(modelfilepath) + print(modelfilepath) + return model_files + + def download_file(self, modelname, url): + modelfilepath = self._temp_ilifile(modelname) + try: + download_file( + url, + modelfilepath, + on_progress=lambda received, total: self.on_ili_stdout("."), + ) + except NetworkError: + self.on_ili_stderr(f"Could not download model {modelname} from {url}") + return None + return modelfilepath + + def _temp_ilifile(self, name): + return os.path.join( + QStandardPaths.writableLocation( + QStandardPaths.StandardLocation.TempLocation + ), + "temp_{}_{:%Y%m%d%H%M%S%f}.ili".format(name, datetime.datetime.now()), + ) + + def on_ili_stdout(self, message): + lines = message.strip().split("\n") + for line in lines: + text = f"pythonizer: {line}" + self.log_function(text, Qgis.MessageLevel.Info) + + def on_ili_stderr(self, message): + lines = message.strip().split("\n") + for line in lines: + text = f"pythonizer: {line}" + self.log_function(text, Qgis.MessageLevel.Critical) + + def on_ili_process_started(self, command): + text = f"pythonizer: {command}" + self.log_function(text, Qgis.MessageLevel.Info) + + def on_ili_process_finished(self, exit_code, result): + if exit_code == 0: + text = f"pythonizer: Successfully performed command." + self.log_function(text, Qgis.MessageLevel.Info) + else: + text = f"pythonizer: Finished with errors: {result}" + self.log_function(text, Qgis.MessageLevel.Critical) From 4012d40d1c52b9bb9df8d65abb32b8eabd43d12b Mon Sep 17 00:00:00 2001 From: signedav Date: Mon, 17 Nov 2025 17:48:56 +0100 Subject: [PATCH 4/6] Settings Prophet --- modelbaker/iliwrapper/ilicompiler.py | 3 +- modelbaker/pythonizer/pythonizer.py | 10 +-- modelbaker/pythonizer/settings_prophet.py | 89 +++++++++++++++++++ tests/test_pythonizer.py | 84 +++++++++++++++++ .../ilimodels/ArcInfrastruktur_V1.ili | 36 ++++++++ .../ilimodels/KT_ArcInfrastruktur_V1.ili | 19 ++++ 6 files changed, 235 insertions(+), 6 deletions(-) create mode 100644 modelbaker/pythonizer/settings_prophet.py create mode 100644 tests/test_pythonizer.py create mode 100644 tests/testdata/ilimodels/ArcInfrastruktur_V1.ili create mode 100644 tests/testdata/ilimodels/KT_ArcInfrastruktur_V1.ili diff --git a/modelbaker/iliwrapper/ilicompiler.py b/modelbaker/iliwrapper/ilicompiler.py index 2a5ac72f..0a11bbd7 100644 --- a/modelbaker/iliwrapper/ilicompiler.py +++ b/modelbaker/iliwrapper/ilicompiler.py @@ -30,11 +30,12 @@ def _create_config(self) -> Ili2CCommandConfiguration: :return: ili2c configuration""" return Ili2CCommandConfiguration() - def _args(self): + def _args(self, param): """Gets the list of ili2c arguments from configuration. :return: ili2c arguments list. :rtype: list """ + # todo care about param (it should not be considered) return self.configuration.to_ili2c_args() def _ili2_jar_arg(self): diff --git a/modelbaker/pythonizer/pythonizer.py b/modelbaker/pythonizer/pythonizer.py index 475a03f1..3482ff23 100644 --- a/modelbaker/pythonizer/pythonizer.py +++ b/modelbaker/pythonizer/pythonizer.py @@ -1,15 +1,15 @@ import datetime import os +from ili2py.mappers.helpers import Index +from ili2py.readers.interlis_24.ilismeta16.xsdata import Imd16Reader +from ili2py.writers.py.python_structure import Library from qgis.core import Qgis from qgis.PyQt.QtCore import QFile, QObject, QStandardPaths -from ..iliwrapper import iliexecutable from ..iliwrapper.ili2dbconfig import Ili2CCommandConfiguration from ..iliwrapper.ili2dbutils import JavaNotFoundError -from ..libs.ili2py.mappers.helpers import Index -from ..libs.ili2py.readers.interlis_24.ilismeta16.xsdata import Imd16Reader -from ..libs.ili2py.writers.py.python_structure import Library +from ..iliwrapper.ilicompiler import IliCompiler from ..utils import db_utils from ..utils.globals import default_log_function from ..utils.qt_utils import NetworkError, download_file @@ -37,7 +37,7 @@ def pythonize(self, imd_file): return index, library def compile(self, base_configuration, ili_file): - compiler = iliexecutable.IliCompiler() + compiler = IliCompiler() configuration = Ili2CCommandConfiguration() configuration.base_configuration = base_configuration diff --git a/modelbaker/pythonizer/settings_prophet.py b/modelbaker/pythonizer/settings_prophet.py new file mode 100644 index 00000000..ab8ce431 --- /dev/null +++ b/modelbaker/pythonizer/settings_prophet.py @@ -0,0 +1,89 @@ +from ili2py.mappers.helpers import Index +from qgis.PyQt.QtCore import QFile, QObject + +from ..utils.globals import default_log_function + + +class SettingsProphet(QObject): + def __init__(self, index: Index, log_function=None) -> None: + QObject.__init__(self) + + self.log_function = log_function if log_function else default_log_function + + if not log_function: + self.log_function = default_log_function + + self.index = index + + def smart_inheritance(self): + """ + Does it make sense to make any suggestions here? I don't know. + """ + return True + + def enum_info(self): + + return True + + def has_basket_oids(self): + """ + Is there any BASKET OID definition in the model. + """ + bid_in_model = self.index.basket_oid_in_model + bid_in_topics = self.index.basket_oid_in_submodel + if len(bid_in_model.keys()) + len(bid_in_topics.keys()): + return True + return False + + def has_arcs(self): + """ + Arcs in any classes of the model or imported models. + """ + line_forms = self.index.geometric_attributes_line_form + if any( + "INTERLIS.ARCS" in forms or "ARCS" in forms for forms in line_forms.values() + ): + return True + return False + + def has_multiple_geometrie_columms(self): + """ + Multiple geometry columns in any classes of the model or imported models. + """ + classes_geometries = self.index.geometric_classes + if any(len(columns) > 1 for columns in classes_geometries.values()): + return True + return False + + def multi_geometry_structures_on_23(self): + """ + Multi geometry structures in INTERLIS 23.. + """ + return False + + def is_translation(self): + return False + + +class ProphetTools(QObject): + def __init__(self, log_function=None) -> None: + QObject.__init__(self) + + self.log_function = log_function if log_function else default_log_function + + def _multi_geometry_mappings(self, multigeometry_structs): + entries = [] + for element in multigeometry_structs: + entries.append("[{}]\n{}".format(element, "test")) + return "\n".join(entries) + + def temp_meta_attr_file(self, multigeometry_structs): + temporary_filename = "{}/modelbaker-metaattrs.conf".format(QDir.tempPath()) + temporary_file = QFile(temporary_filename) + if temporary_file.open(QFile.OpenModeFlag.WriteOnly): + content = self._multi_geometry_mappings(multigeometry_structs) + if content: + temporary_file.write(content).encode("utf-8") + temporary_file.close() + return temporary_filename + return None diff --git a/tests/test_pythonizer.py b/tests/test_pythonizer.py new file mode 100644 index 00000000..ac488d48 --- /dev/null +++ b/tests/test_pythonizer.py @@ -0,0 +1,84 @@ +""" +/*************************************************************************** + begin : 08.12.2025 + git sha : :%H$ + copyright : (C) 2025 by Dave Signer + email : david at opengis ch + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +""" + +import tempfile + +from qgis.testing import start_app, unittest + +from modelbaker.iliwrapper.ili2dbconfig import BaseConfiguration +from modelbaker.pythonizer.pythonizer import Pythonizer +from modelbaker.pythonizer.settings_prophet import ProphetTools, SettingsProphet +from tests.utils import testdata_path + +start_app() + + +class TestPythonizer(unittest.TestCase): + @classmethod + def setUpClass(cls): + """Run before all tests.""" + cls.basetestpath = tempfile.mkdtemp() + cls.base_config = BaseConfiguration() + + def test_settings_prophet_nupla(self): + pythonizer = Pythonizer() + base_configuration = BaseConfiguration() + ili_file = testdata_path("ilimodels/Nutzungsplanung_V1_2.ili") + john_wayne, imd_file = pythonizer.compile(base_configuration, ili_file) + index, lib = pythonizer.pythonize(imd_file) + prophet = SettingsProphet(index) + + assert prophet.has_basket_oids() + assert prophet.has_arcs() + assert not prophet.has_multiple_geometrie_columms() + + expected_multigeometries = {} + assert prophet.multi_geometry_structures_on_23() == expected_multigeometries + + def test_settings_prophet_arcmodel(self): + pythonizer = Pythonizer() + base_configuration = BaseConfiguration() + ili_file = testdata_path("ilimodels/KT_ArcInfrastruktur.ili") + john_wayne, imd_file = pythonizer.compile(base_configuration, ili_file) + index, lib = pythonizer.pythonize(imd_file) + prophet = SettingsProphet(index) + + assert not prophet.has_basket_oids() + assert prophet.has_arcs() + assert prophet.has_multiple_geometrie_columms() + + expected_multigeometries = {} + assert prophet.multi_geometry_structures_on_23() == expected_multigeometries + + def test_settings_prophet_arcs(self): + pythonizer = Pythonizer() + base_configuration = BaseConfiguration() + ili_file = testdata_path("ilimodels/KbS_V1_5.ili") + john_wayne, imd_file = pythonizer.compile(base_configuration, ili_file) + index, lib = pythonizer.pythonize(imd_file) + prophet = SettingsProphet(index) + + assert not prophet.has_basket_oids() + assert not prophet.has_arcs() + assert prophet.has_multiple_geometrie_columms() + + expected_multigeometries = {} + assert prophet.multi_geometry_structures_on_23() == expected_multigeometries + + prophet_tools = ProphetTools() + prophet_tools.temp_meta_attr_file(expected_multigeometries) diff --git a/tests/testdata/ilimodels/ArcInfrastruktur_V1.ili b/tests/testdata/ilimodels/ArcInfrastruktur_V1.ili new file mode 100644 index 00000000..1150b2c3 --- /dev/null +++ b/tests/testdata/ilimodels/ArcInfrastruktur_V1.ili @@ -0,0 +1,36 @@ +INTERLIS 2.3; + +MODEL ArcInfrastruktur_V1 (en) AT "https://modelbaker.ch" VERSION "2023-03-29" = + IMPORTS GeometryCHLV95_V1; + + DOMAIN + Line = POLYLINE WITH (STRAIGHTS) VERTEX GeometryCHLV95_V1.Coord2; + Surface = SURFACE WITH (STRAIGHTS) VERTEX GeometryCHLV95_V1.Coord2 WITHOUT OVERLAPS > 0.001; + LineArcs = POLYLINE WITH (STRAIGHTS, ARCS) VERTEX GeometryCHLV95_V1.Coord2; + SurfaceArcs = SURFACE WITH (STRAIGHTS, ARCS) VERTEX GeometryCHLV95_V1.Coord2 WITHOUT OVERLAPS > 0.001; + + TOPIC StrassenEtc = + CLASS Strasse = + Name : MANDATORY TEXT*99; + Geometrie : MANDATORY ArcInfrastruktur_V1.Line; + END Strasse; + + CLASS Haus = + Name : MANDATORY TEXT*99; + Geometrie : MANDATORY ArcInfrastruktur_V1.SurfaceArcs; + END Haus; + END StrassenEtc; + + TOPIC Natur = + CLASS Pfad = + Name : MANDATORY TEXT*99; + Geometrie : MANDATORY ArcInfrastruktur_V1.LineArcs; + END Pfad; + + CLASS Park = + Name : MANDATORY TEXT*99; + Geometrie : MANDATORY ArcInfrastruktur_V1.Surface; + END Park; + END Natur; + +END ArcInfrastruktur_V1. diff --git a/tests/testdata/ilimodels/KT_ArcInfrastruktur_V1.ili b/tests/testdata/ilimodels/KT_ArcInfrastruktur_V1.ili new file mode 100644 index 00000000..a5bde115 --- /dev/null +++ b/tests/testdata/ilimodels/KT_ArcInfrastruktur_V1.ili @@ -0,0 +1,19 @@ + +INTERLIS 2.3; + +/* Ortsplanung as national model */ +MODEL KT_ArcInfrastruktur_V1 (en) AT "https://modelbaker.ch" VERSION "2023-03-29" = + IMPORTS ArcInfrastruktur_V1; + + TOPIC StrassenEtc EXTENDS ArcInfrastruktur_V1.StrassenEtc = + CLASS Strasse (EXTENDED) = + Beschreibung : MANDATORY TEXT*99; + END Strasse; + + CLASS Konstruktion = + Name : TEXT; + Linie : MANDATORY ArcInfrastruktur_V1.Line; + Polygon : MANDATORY ArcInfrastruktur_V1.Surface; + END Konstruktion; + END StrassenEtc; +END KT_ArcInfrastruktur_V1. From 56ee093c0a1f9d0c1c1ffe20393d58f1c909aab4 Mon Sep 17 00:00:00 2001 From: signedav Date: Mon, 24 Nov 2025 22:00:41 +0100 Subject: [PATCH 5/6] fix has arcs and has multiple geometry columns --- modelbaker/pythonizer/settings_prophet.py | 66 ++++++++++++++++++++--- 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/modelbaker/pythonizer/settings_prophet.py b/modelbaker/pythonizer/settings_prophet.py index ab8ce431..6ff9105c 100644 --- a/modelbaker/pythonizer/settings_prophet.py +++ b/modelbaker/pythonizer/settings_prophet.py @@ -5,7 +5,7 @@ class SettingsProphet(QObject): - def __init__(self, index: Index, log_function=None) -> None: + def __init__(self, index: Index, models: list, log_function=None) -> None: QObject.__init__(self) self.log_function = log_function if log_function else default_log_function @@ -14,6 +14,7 @@ def __init__(self, index: Index, log_function=None) -> None: self.log_function = default_log_function self.index = index + self.models = models def smart_inheritance(self): """ @@ -39,19 +40,39 @@ def has_arcs(self): """ Arcs in any classes of the model or imported models. """ + # get all the geometric attributes of the relevant classes + relevant_geometric_attributes = [] + relevant_geometric_attributes_per_class = ( + self._relevant_geometric_attributes_per_class() + ) + for relevant_classname in relevant_geometric_attributes_per_class.keys(): + relevant_geometric_attributes += relevant_geometric_attributes_per_class[ + relevant_classname + ] + + # get the line form of the relevant geometry attributes line_forms = self.index.geometric_attributes_line_form - if any( - "INTERLIS.ARCS" in forms or "ARCS" in forms for forms in line_forms.values() - ): - return True - return False + line_forms_of_interest = [] + for attribute in line_forms.keys(): + if attribute in relevant_geometric_attributes: + line_forms_of_interest += line_forms[attribute] + + return bool( + "INTERLIS.ARCS" in line_forms_of_interest + or "ARCS" in line_forms_of_interest + ) def has_multiple_geometrie_columms(self): """ Multiple geometry columns in any classes of the model or imported models. """ - classes_geometries = self.index.geometric_classes - if any(len(columns) > 1 for columns in classes_geometries.values()): + relevant_geometric_attributes_per_class = ( + self._relevant_geometric_attributes_per_class() + ) + if any( + len(columns) > 1 + for columns in relevant_geometric_attributes_per_class.values() + ): return True return False @@ -61,6 +82,35 @@ def multi_geometry_structures_on_23(self): """ return False + def _relevant_classes(self): + # get the relevant baskets of the models + relevant_topics = [] + all_topics = self.index.submodel_in_package + for model in self.models: + if model in all_topics.keys(): + relevant_topics += all_topics[model] + topic_baskets_map = self.index.topic_basket + relevant_baskets = [topic_baskets_map.get(topic) for topic in relevant_topics] + + # get all the relevant classes by checking if they are allowed in the data unit + relevant_classes = [] + all_elements = self.index.allowed_in_basket_of_data_unit + for element_basket in all_elements.keys(): + if element_basket in relevant_baskets: + relevant_classes += all_elements[element_basket] + return relevant_classes + + def _relevant_geometric_attributes_per_class(self): + relevant_classes = self._relevant_classes() + geometric_classes = self.index.geometric_classes + relevant_geometric_attributes_per_class = {} + for geometric_classname in geometric_classes.keys(): + if geometric_classname in relevant_classes: + relevant_geometric_attributes_per_class[ + geometric_classname + ] = geometric_classes[geometric_classname] + return relevant_geometric_attributes_per_class + def is_translation(self): return False From 722ad84961ccb8ba3403f54d53ae1299a10124a8 Mon Sep 17 00:00:00 2001 From: signedav Date: Tue, 25 Nov 2025 14:43:28 +0100 Subject: [PATCH 6/6] setting prophet updates --- modelbaker/pythonizer/settings_prophet.py | 39 ++++++++++++++++------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/modelbaker/pythonizer/settings_prophet.py b/modelbaker/pythonizer/settings_prophet.py index 6ff9105c..3250d614 100644 --- a/modelbaker/pythonizer/settings_prophet.py +++ b/modelbaker/pythonizer/settings_prophet.py @@ -41,14 +41,7 @@ def has_arcs(self): Arcs in any classes of the model or imported models. """ # get all the geometric attributes of the relevant classes - relevant_geometric_attributes = [] - relevant_geometric_attributes_per_class = ( - self._relevant_geometric_attributes_per_class() - ) - for relevant_classname in relevant_geometric_attributes_per_class.keys(): - relevant_geometric_attributes += relevant_geometric_attributes_per_class[ - relevant_classname - ] + relevant_geometric_attributes = self._relevant_geometric_attributes() # get the line form of the relevant geometry attributes line_forms = self.index.geometric_attributes_line_form @@ -62,7 +55,7 @@ def has_arcs(self): or "ARCS" in line_forms_of_interest ) - def has_multiple_geometrie_columms(self): + def has_multiple_geometry_columns(self): """ Multiple geometry columns in any classes of the model or imported models. """ @@ -76,11 +69,22 @@ def has_multiple_geometrie_columms(self): return True return False - def multi_geometry_structures_on_23(self): + def multi_geometry_structures_on_23(self) -> dict: """ Multi geometry structures in INTERLIS 23.. """ - return False + set_of_relevant_geometric_attributes = set( + self._relevant_geometric_attributes() + ) + set_of_multi_geometry_attributes = set(self.index.geometric_attributes_multi) + + relevant_multi_geometry_attributes = list( + set_of_relevant_geometric_attributes & set_of_multi_geometry_attributes + ) + + # then it should get the type of the attributes (like KbS_V1_5.Belastete_Standorte.MultiPolygon) + # and then it should return KbS_V1_5.Belastete_Standorte.MultiPolygon : 'polygon' or 'surface' or something... + return {} def _relevant_classes(self): # get the relevant baskets of the models @@ -100,7 +104,7 @@ def _relevant_classes(self): relevant_classes += all_elements[element_basket] return relevant_classes - def _relevant_geometric_attributes_per_class(self): + def _relevant_geometric_attributes_per_class(self) -> dict: relevant_classes = self._relevant_classes() geometric_classes = self.index.geometric_classes relevant_geometric_attributes_per_class = {} @@ -111,6 +115,17 @@ def _relevant_geometric_attributes_per_class(self): ] = geometric_classes[geometric_classname] return relevant_geometric_attributes_per_class + def _relevant_geometric_attributes(self) -> list: + relevant_geometric_attributes = [] + relevant_geometric_attributes_per_class = ( + self._relevant_geometric_attributes_per_class() + ) + for relevant_classname in relevant_geometric_attributes_per_class.keys(): + relevant_geometric_attributes += relevant_geometric_attributes_per_class[ + relevant_classname + ] + return relevant_geometric_attributes + def is_translation(self): return False