xplan-tools 1.13.1__py3-none-any.whl → 1.14.0__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.
xplan_tools/model/base.py CHANGED
@@ -3,9 +3,12 @@
3
3
  The classes provide some utility, (de-)serialization and/or validation methods to simplify access and manipulation.
4
4
  """
5
5
 
6
+ from __future__ import annotations
7
+
6
8
  import datetime
7
9
  import logging
8
10
  import re
11
+ from copy import deepcopy
9
12
  from importlib import import_module
10
13
  from pathlib import Path
11
14
  from types import ModuleType, NoneType
@@ -22,7 +25,7 @@ from typing import (
22
25
  get_args,
23
26
  get_origin,
24
27
  )
25
- from uuid import UUID
28
+ from uuid import UUID, uuid4
26
29
 
27
30
  from lxml.etree import _Element
28
31
  from osgeo import ogr
@@ -312,6 +315,46 @@ class BaseCollection(BaseModel):
312
315
  """Yields features stored in the collection."""
313
316
  return (feature for feature in self.features.values())
314
317
 
318
+ def make_copy(
319
+ self, with_id_map: bool = False
320
+ ) -> "BaseCollection" | tuple["BaseCollection", dict]:
321
+ """Return a copy of the collection with new IDs.
322
+
323
+ All feature IDs are renewed and respective references are updated.
324
+
325
+ Args:
326
+ with_id_map: whether to additionally return a map of old IDs to new IDs
327
+ """
328
+ id_map = {key: uuid4() for key in self.features.keys()}
329
+ new_features = {}
330
+ for old_feature in self.features.values():
331
+ new_id = str(id_map[old_feature.id])
332
+ new_feature = deepcopy(old_feature)
333
+ new_feature.id = new_id
334
+ for assocation in old_feature.get_associations():
335
+ old_value = getattr(old_feature, assocation)
336
+ if isinstance(old_value, UUID):
337
+ new_value = id_map[str(old_value)]
338
+ setattr(new_feature, assocation, new_value)
339
+ elif isinstance(old_value, list):
340
+ new_list = [
341
+ id_map[str(item)] if isinstance(item, UUID) else item
342
+ for item in old_value
343
+ ]
344
+ setattr(new_feature, assocation, new_list)
345
+ new_features[new_id] = new_feature
346
+
347
+ new_collection = BaseCollection(
348
+ features=new_features,
349
+ srid=self.srid,
350
+ version=self.version,
351
+ appschema=self.appschema,
352
+ )
353
+ if with_id_map:
354
+ return new_collection, id_map
355
+ else:
356
+ return new_collection
357
+
315
358
  def add_style_properties(
316
359
  self, to_text: bool = False, always_populate_schriftinhalt: bool = False
317
360
  ) -> None:
@@ -7,7 +7,6 @@ Create Date: 2026-01-08 10:58:26.988690
7
7
 
8
8
  from typing import Sequence, Union
9
9
 
10
- import sqlalchemy as sa
11
10
  from alembic import op
12
11
 
13
12
  from xplan_tools.settings import get_settings
@@ -25,9 +24,7 @@ schema = settings.db_schema
25
24
  def upgrade() -> None:
26
25
  """Upgrade schema.
27
26
 
28
- Creates indexes on `refs.base_id` and refs.related_id` and normalizes all polygon
29
- geometries in `coretable.geometry` to counter-clockwise orientation
30
- using `ST_ForcePolygonCCW` in-place.
27
+ Creates indexes on `refs.base_id` and refs.related_id`.
31
28
  """
32
29
  op.create_index(
33
30
  op.f("ix_refs_base_id"),
@@ -43,19 +40,6 @@ def upgrade() -> None:
43
40
  schema=schema,
44
41
  if_not_exists=True,
45
42
  )
46
- coretable = sa.Table(
47
- "coretable",
48
- sa.MetaData(),
49
- sa.Column("geometry"),
50
- sa.Column("geometry_type"),
51
- schema=schema,
52
- )
53
- stmt = (
54
- sa.update(coretable)
55
- .values(geometry=sa.func.ST_ForcePolygonCCW(coretable.c.geometry))
56
- .where(coretable.c.geometry_type == "polygon")
57
- )
58
- op.execute(stmt)
59
43
 
60
44
 
61
45
  def downgrade() -> None:
xplan_tools/model/orm.py CHANGED
@@ -20,7 +20,6 @@ from sqlalchemy import (
20
20
  Text,
21
21
  Uuid,
22
22
  case,
23
- cast,
24
23
  literal,
25
24
  select,
26
25
  )
@@ -104,13 +103,8 @@ class PGGeometry(TypeDecorator):
104
103
 
105
104
  def bind_expression(self, bindvalue):
106
105
  """Transform incoming geometries to the SRID of the DB and force CCW orientation for polygons."""
107
- is_polygon = Geometry._is_polygon(cast(bindvalue, Geometry))
108
- case_clause = case(
109
- (is_polygon, func.ST_ForcePolygonCCW(bindvalue)),
110
- else_=bindvalue,
111
- )
112
106
  return func.ST_Transform(
113
- case_clause,
107
+ bindvalue,
114
108
  func.Find_SRID(
115
109
  get_settings().db_schema or "public", "coretable", "geometry"
116
110
  ),
xplan_tools/util/style.py CHANGED
@@ -49,6 +49,10 @@ def add_style_properties_to_feature(
49
49
  "alias", prop_info["enum_info"][value]["name"]
50
50
  ),
51
51
  )
52
+ elif prop_info["stereotype"] == "Codelist":
53
+ text = str(value)
54
+ if text.startswith("urn:"):
55
+ text = text.split(f"urn:xplan:{prop_info['typename']}:")[1]
52
56
  elif prop_info["stereotype"] == "Measure":
53
57
  text = f"{value:n} {uom_map.get(prop_info['uom'], prop_info['uom'])}"
54
58
  else:
@@ -63,8 +67,8 @@ def add_style_properties_to_feature(
63
67
  remove_namespace = re.sub(
64
68
  r"xplan:", "", remove_subindexes
65
69
  ) # xplan:|(.P_|SO_)[a-zA-Z]*\/
66
- attr, path_index, datatype, sub_attr = re.match(
67
- r"^(?P<attr>\w*)\[?(?P<index>\d)?]?/?(?P<datatype>\w{2}_\w*)?/?(?P<sub_attr>\w*)?$",
70
+ attr, path_index, datatype, sub_attr, sub_attr_index = re.match(
71
+ r"^(?P<attr>\w*)\[?(?P<index>\d)?]?/?(?P<datatype>\w{2}_\w*)?/?(?P<sub_attr>\w*)?\[?(?P<sub_attr_index>\d)?]?$",
68
72
  remove_namespace,
69
73
  ).groups()
70
74
 
@@ -83,6 +87,11 @@ def add_style_properties_to_feature(
83
87
  if sub_attr:
84
88
  name = sub_attr
85
89
  value = getattr(value, sub_attr)
90
+ if isinstance(value, list):
91
+ index = 0
92
+ if sub_attr_index:
93
+ index = max(int(sub_attr_index) - 1, 0)
94
+ value = value[index]
86
95
 
87
96
  prop_info = model.get_property_info(name)
88
97
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xplan-tools
3
- Version: 1.13.1
3
+ Version: 1.14.0
4
4
  Summary: Manage XPlanung data
5
5
  License: EUPL-1.2-or-later
6
6
  License-File: LICENSE.md
@@ -21,12 +21,12 @@ xplan_tools/model/appschema/xplan60.py,sha256=W85nw3dWYFYCPi0B3MR_BYCSyL7F-amw0Y
21
21
  xplan_tools/model/appschema/xplan61.py,sha256=J0ZhbgyZD5ZOvIOYXWCo2R4AygCe0QC5u5Ope2XPoM0,1319028
22
22
  xplan_tools/model/appschema/xtrasse20.py,sha256=5zRbkFOOSOKqy5h8INzI6UvCmG0KqGAzNBQWXmfgaQw,537890
23
23
  xplan_tools/model/appschema/xwp09.py,sha256=fYpLgKnL6vEscSPWxBWmB_VU4bidyQv0C3w91I_nmms,218518
24
- xplan_tools/model/base.py,sha256=C8DAORkmQ0aR_4QQKK4YONO0RBYpNZk3vVGuyS5shQ4,27935
24
+ xplan_tools/model/base.py,sha256=Zjs1svwzXcRM9nz5JdQZna44C5Zk4pqdHlHLnRmbq_U,29614
25
25
  xplan_tools/model/migrations/env.py,sha256=dJw3GH3_pJn2LuiflD1wwJi7Dq-9avm56xdRc50Wzpg,1974
26
26
  xplan_tools/model/migrations/script.py.mako,sha256=04kgeBtNMa4cCnG8CfQcKt6P6rnloIfj8wy0u_DBydM,704
27
27
  xplan_tools/model/migrations/versions/3c3445a58565_base_schema.py,sha256=nsgcqTDFkcejdT4YqRfyQeDbWAHMqZJKVyZnUm5BY9A,7857
28
- xplan_tools/model/migrations/versions/f8b74c08ec07_add_refs_indexes_ensure_polygon_ccw.py,sha256=RCDG-vT6jiHqvg29BCnYoM1YKl9sJw_I5vamdXr62zU,1767
29
- xplan_tools/model/orm.py,sha256=_OPGbIx3C93RZsJZL-Rx52hWIgwqHlN_yW7Y815Om24,11936
28
+ xplan_tools/model/migrations/versions/f8b74c08ec07_add_refs_indexes_ensure_polygon_ccw.py,sha256=7qE9tkGeOKD1zxTiNxkiZNmF_1CNwylAblQC3c8aWzU,1239
29
+ xplan_tools/model/orm.py,sha256=THjjxFmmnIqfwYOZ_89AQJcOZdO3wM_mOk3QaG7J2rY,11720
30
30
  xplan_tools/resources/styles.py,sha256=h0ezUqZrsXMQz2bTcF0nIyoI-IxMItstHx1j1xYGi3c,45010
31
31
  xplan_tools/settings/__init__.py,sha256=yQdL0KFclWVUBE0ZMUTkktKMa9D78RDcPrHkK23Z9m8,89
32
32
  xplan_tools/settings/settings.py,sha256=bABcqw7--TU-khpE9l-2XkavlQ-xuVZwmBmJiD0yRVw,462
@@ -40,10 +40,10 @@ xplan_tools/transform/migrate_60_61.py,sha256=S-fdnTjQUf8LGnEvsOY73zZYDvyVgIhVew
40
40
  xplan_tools/transform/migrate_6x_plu.py,sha256=3w5UfjdUKFUiLma3Ppc9fAogPVxVshtUzzve0Dv0nWM,216797
41
41
  xplan_tools/transform/transformer.py,sha256=2O4Aq_eTo9e2JOUwq9tJnfv6dwOxqNDdTGPtV_SAL1w,26216
42
42
  xplan_tools/util/__init__.py,sha256=OqOorl5s8BZANbLeFE9wWlozvA64RINn9wigMkds2u8,11704
43
- xplan_tools/util/style.py,sha256=6Iyj-fsmnJylkJo-70wCFjfsbbwUGAS5QTN2IMJAVdI,7503
43
+ xplan_tools/util/style.py,sha256=xFAK-QH2XEEUo7AxXWx35s7hJtmrr1XaRHiqPH3YEMM,7959
44
44
  xplan_tools/util/validate.py,sha256=3inTRLHrVonfQ9FDjtvSNKlAGem5DKtEvLqW7NxwWQs,3327
45
- xplan_tools-1.13.1.dist-info/METADATA,sha256=snQDjzBtxlSLcGODMFq85g4qF63ooP1sBTn0Pj12hM0,4824
46
- xplan_tools-1.13.1.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
47
- xplan_tools-1.13.1.dist-info/entry_points.txt,sha256=NV98QKVV3vDuWnJtWsKZvcpEH2N7SF1yHXvVMet23Ps,52
48
- xplan_tools-1.13.1.dist-info/licenses/LICENSE.md,sha256=b8nnCcy_4Nd_v_okJ6mDKCvi64jkexzbSfIag7TR5mU,13827
49
- xplan_tools-1.13.1.dist-info/RECORD,,
45
+ xplan_tools-1.14.0.dist-info/METADATA,sha256=_LdrPenkuJh4E1in4U4Jenh61EMwE1gZ4_j1wx7ohbE,4824
46
+ xplan_tools-1.14.0.dist-info/WHEEL,sha256=kJCRJT_g0adfAJzTx2GUMmS80rTJIVHRCfG0DQgLq3o,88
47
+ xplan_tools-1.14.0.dist-info/entry_points.txt,sha256=NV98QKVV3vDuWnJtWsKZvcpEH2N7SF1yHXvVMet23Ps,52
48
+ xplan_tools-1.14.0.dist-info/licenses/LICENSE.md,sha256=b8nnCcy_4Nd_v_okJ6mDKCvi64jkexzbSfIag7TR5mU,13827
49
+ xplan_tools-1.14.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.3.0
2
+ Generator: poetry-core 2.3.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any