Skip to content

Commit

Permalink
Merge pull request #108 from opengisch/multigeom
Browse files Browse the repository at this point in the history
Concern multiple geometries in same table for geopackage
  • Loading branch information
signedav authored Oct 31, 2024
2 parents 647761c + 978b1c9 commit 1029271
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 3 deletions.
5 changes: 5 additions & 0 deletions modelbaker/db_factory/gpkg_layer_uri.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,14 @@ class GpkgLayerUri(LayerUri):
def __init__(self, uri: str) -> None:
LayerUri.__init__(self, uri)
self.provider = "ogr"
self.gpkg_multigeom = False

def get_data_source_uri(self, record: dict) -> str:
data_source_uri = "{uri}|layername={table}".format(
uri=self.uri, table=record["tablename"]
)
if self.gpkg_multigeom:
data_source_uri = "{} ({})".format(
data_source_uri, record["geometry_column"]
)
return data_source_uri
7 changes: 7 additions & 0 deletions modelbaker/dbconnector/db_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,13 @@ def get_classes_relevance(self):
"""
return []

def multiple_geometry_tables(self):
"""
Returns a list of tables having multiple geometry columns.
It's only usefull on GeoPackage.
"""
return []

def create_basket(
self, dataset_tid, topic, tilitid_value=None, attachment_key="modelbaker"
):
Expand Down
19 changes: 19 additions & 0 deletions modelbaker/dbconnector/gpkg_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,25 @@ def get_classes_relevance(self):
return contents
return []

def multiple_geometry_tables(self):
tables = []
if self._table_exists("gpkg_geometry_columns"):
cursor = self.conn.cursor()
cursor.execute(
"""
SELECT table_name
FROM gpkg_geometry_columns
GROUP BY table_name
HAVING COUNT(table_name)>1
"""
)
records = cursor.fetchall()
cursor.close()
for record in records:
tables.append(record["table_name"])

return tables

def create_basket(
self, dataset_tid, topic, tilitid_value=None, attachment_key="modelbaker"
):
Expand Down
5 changes: 5 additions & 0 deletions modelbaker/generator/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,11 @@ def layers(self, filter_layer_list: list = []) -> list[Layer]:
if coord_decimals:
coordinate_precision = 1 / (10**coord_decimals)

layer_uri.gpkg_multigeom = bool(
table_appearance_count[record["tablename"]] > 1
and "geometry_column" in record
)

layer = Layer(
layer_uri.provider,
layer_uri.get_data_source_uri(record),
Expand Down
8 changes: 8 additions & 0 deletions modelbaker/iliwrapper/ili2dbconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from qgis.core import QgsNetworkAccessManager
from qgis.PyQt.QtNetwork import QNetworkProxy

from .globals import DbIliMode
from .ili2dbutils import get_all_modeldir_in_path


Expand Down Expand Up @@ -239,6 +240,7 @@ def __init__(self, other: Ili2DbCommandConfiguration = None):
self.create_import_tid = True
self.srs_auth = "EPSG" # Default SRS auth in ili2db
self.srs_code = 2056 # Default SRS code in ili2db
self.create_gpkg_multigeom = False
self.stroke_arcs = True
self.pre_script = ""
self.post_script = ""
Expand Down Expand Up @@ -297,6 +299,12 @@ def to_ili2db_args(self, extra_args=[], with_action=True):
elif self.db_ili_version is None or self.db_ili_version > 3:
self.append_args(args, ["--strokeArcs=False"])

if self.tool and (self.tool & DbIliMode.gpkg):
if self.create_gpkg_multigeom:
self.append_args(args, ["--gpkgMultiGeomPerTable"], True)
elif self.db_ili_version is None or self.db_ili_version > 3:
self.append_args(args, ["--gpkgMultiGeomPerTable=False"])

if self.create_basket_col:
self.append_args(args, ["--createBasketCol"])
elif self.db_ili_version is None or self.db_ili_version > 3:
Expand Down
6 changes: 3 additions & 3 deletions modelbaker/iliwrapper/ili2dbtools.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@ def get_tool_version(tool, db_ili_version):
if db_ili_version == 3:
return "3.11.3"
else:
return "5.1.0"
return "5.1.1"
elif tool == DbIliMode.ili2pg:
if db_ili_version == 3:
return "3.11.2"
else:
return "5.1.0"
return "5.1.1"
elif tool == DbIliMode.ili2mssql:
if db_ili_version == 3:
return "3.12.2"
else:
return "5.1.0"
return "5.1.1"

return "0"

Expand Down
157 changes: 157 additions & 0 deletions tests/test_multiple_geom_gpkg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
"""
/***************************************************************************
-------------------
begin : 28.10.2024
git sha : :%H$
copyright : (C) 2024 by Dave Signer
email : [email protected]
***************************************************************************/
/***************************************************************************
* *
* 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 datetime
import logging
import os
import pathlib
import tempfile

from osgeo import gdal
from qgis.core import QgsProject
from qgis.testing import start_app, unittest

import modelbaker.utils.db_utils as db_utils
from modelbaker.dataobjects.project import Project
from modelbaker.db_factory.gpkg_command_config_manager import GpkgCommandConfigManager
from modelbaker.generator.generator import Generator
from modelbaker.iliwrapper import iliimporter
from modelbaker.iliwrapper.globals import DbIliMode
from tests.utils import iliimporter_config, testdata_path

start_app()

test_path = pathlib.Path(__file__).parent.absolute()


class TestMultipleGeometriesPerTable(unittest.TestCase):
@classmethod
def setUpClass(cls):
"""Run before all tests."""
cls.basetestpath = tempfile.mkdtemp()

def test_multiple_geom_geopackage(self):
"""
Checks when the gdal version is sufficient (means >=3.8) if tables are created with multiple geometries and the correct layers are generated.
This of course depends with what gdal version the current images are built.
"""

importer = iliimporter.Importer()
importer.tool = DbIliMode.ili2gpkg
importer.configuration = iliimporter_config(importer.tool)
importer.configuration.ilifile = testdata_path("ilimodels/MultipleGeom.ili")
importer.configuration.ilimodels = "MultipleGeom"
importer.configuration.dbfile = os.path.join(
self.basetestpath,
"tmp_multiple_geom_{:%Y%m%d%H%M%S%f}.gpkg".format(datetime.datetime.now()),
)
importer.configuration.srs_code = 2056
importer.configuration.inheritance = "smart2"
importer.configuration.create_basket_col = True

# create it when there's a sufficient gdal version
importer.configuration.create_gpkg_multigeom = self._sufficient_gdal()

importer.stdout.connect(self.print_info)
importer.stderr.connect(self.print_error)
assert importer.run() == iliimporter.Importer.SUCCESS

# check geometry table
# check if there are multiple geometry columns of the same table
db_connector = db_utils.get_db_connector(importer.configuration)
tables_with_multiple_geometries = db_connector.multiple_geometry_tables()

# should have multiple when having a sufficient gdal and otherwise not
if self._sufficient_gdal():
assert len(tables_with_multiple_geometries) > 0
else:
assert len(tables_with_multiple_geometries) == 0

# create project
config_manager = GpkgCommandConfigManager(importer.configuration)
uri = config_manager.get_uri()

generator = Generator(
tool=DbIliMode.ili2gpkg,
uri=uri,
inheritance=importer.configuration.inheritance,
consider_basket_handling=True,
)

project = Project()

available_layers = generator.layers()
relations, _ = generator.relations(available_layers)
legend = generator.legend(available_layers)
project.layers = available_layers
project.relations = relations
project.legend = legend
project.post_generate()

qgis_project = QgsProject.instance()
project.create(None, qgis_project)

# check layertree
root = qgis_project.layerTreeRoot()
assert root is not None

tree_layers = root.findLayers()
assert len(tree_layers) == 7

{layer.name() for layer in tree_layers}
expected_layer_names_with_multigeometry = {
"NoGeomClass",
"POI",
"GOI (Point)",
"T_ILI2DB_BASKET",
"T_ILI2DB_DATASET",
"GOI (Line)",
"GOI (Surface)",
}
expected_layer_names_without_multigeometry = {
"NoGeomClass",
"GOI",
"POI",
"GOI (Line)",
"GOI (Surface)",
"T_ILI2DB_BASKET",
"T_ILI2DB_DATASET",
}

if self._sufficient_gdal():
assert {
layer.name() for layer in tree_layers
} == expected_layer_names_with_multigeometry
else:
assert {
layer.name() for layer in tree_layers
} == expected_layer_names_without_multigeometry

def _sufficient_gdal(self):
return bool(int(gdal.VersionInfo("VERSION_NUM")) >= 3080000)

def print_info(self, text):
logging.info(text)

def print_error(self, text):
logging.error(text)

def tearDown(self):
QgsProject.instance().removeAllMapLayers()
QgsProject.instance().clear()
32 changes: 32 additions & 0 deletions tests/testdata/ilimodels/MultipleGeom.ili
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
INTERLIS 2.3;

MODEL MultipleGeom (en)
AT "http://modelbaker.ch"
VERSION "2024-06-22" =

IMPORTS GeometryCHLV95_V1;

DOMAIN
Line = POLYLINE WITH (STRAIGHTS) VERTEX GeometryCHLV95_V1.Coord2;
Surface = SURFACE WITH (STRAIGHTS) VERTEX GeometryCHLV95_V1.Coord2 WITHOUT OVERLAPS > 0.005;

TOPIC Spots =

CLASS POI =
Name: TEXT;
Point: GeometryCHLV95_V1.Coord2;
END POI;

CLASS GOI =
Name: TEXT;
Point: GeometryCHLV95_V1.Coord2;
Line: Line;
Surface: Surface;
END GOI;

CLASS NoGeomClass =
Name: TEXT;
END NoGeomClass;
END Spots;

END MultipleGeom.

0 comments on commit 1029271

Please sign in to comment.