xplan-tools 1.12.1__py3-none-any.whl → 1.13.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
@@ -6,9 +6,13 @@ The classes provide some utility, (de-)serialization and/or validation methods t
6
6
  import datetime
7
7
  import logging
8
8
  import re
9
- from types import NoneType
9
+ from importlib import import_module
10
+ from pathlib import Path
11
+ from types import ModuleType, NoneType
10
12
  from typing import (
13
+ Annotated,
11
14
  Any,
15
+ ClassVar,
12
16
  Iterator,
13
17
  Literal,
14
18
  Optional,
@@ -26,6 +30,8 @@ from pydantic import (
26
30
  AnyUrl,
27
31
  BaseModel,
28
32
  ConfigDict,
33
+ Field,
34
+ RootModel,
29
35
  SerializationInfo,
30
36
  SerializeAsAny,
31
37
  ValidationInfo,
@@ -34,6 +40,8 @@ from pydantic import (
34
40
  field_validator,
35
41
  model_validator,
36
42
  )
43
+ from pydantic_extra_types.semantic_version import SemanticVersion
44
+ from semver import Version
37
45
 
38
46
  from xplan_tools.model import model_factory
39
47
  from xplan_tools.model.adapters import CoretableAdapter, GMLAdapter, JsonFGAdapter
@@ -50,6 +58,183 @@ ogr.UseExceptions()
50
58
 
51
59
  logger = logging.getLogger(__name__)
52
60
 
61
+ APPSCHEMA_MODULE_NAMES: list[str] = [
62
+ file.stem for file in (Path(__file__).parent / "appschema").glob("x*.py")
63
+ ]
64
+
65
+
66
+ class Appschema(BaseModel):
67
+ """Models a supported appschema.
68
+
69
+ Used to access metadata like name, version and description for an appschema.
70
+
71
+ During instantion, validation if an appschema is supported occurs, based on the modules
72
+ in `appschema` subdirectory.
73
+
74
+ The instance stores a reference to the appschema's module, which is used in the
75
+ model_factory method the retrieve appschema classes.
76
+
77
+ Usage example:
78
+ ```
79
+ # get a featuretype from appschema
80
+ appschema = Appschema.from_prefix(prefix="xplan", version="6.0")
81
+ plan = appschema.model_factory(name="BP_Plan")
82
+ # show list of supported appschemas
83
+ Appschema.supported_appschemas()
84
+ ```
85
+ """
86
+
87
+ model_config = ConfigDict(arbitrary_types_allowed=True)
88
+
89
+ _supported_appschemas: ClassVar[list["Appschema"] | None] = None
90
+
91
+ module: Annotated[ModuleType, Field(repr=False)]
92
+ full_name: Annotated[
93
+ str,
94
+ Field(
95
+ description="The full name of the appschema as defined in the UML model."
96
+ ),
97
+ ]
98
+ full_version: Annotated[
99
+ SemanticVersion,
100
+ Field(description="The semantic version of the appschema."),
101
+ ]
102
+ namespace_uri: Annotated[
103
+ AnyUrl,
104
+ Field(description="The namespace URI of the appschema."),
105
+ ]
106
+ prefix: Annotated[
107
+ str,
108
+ Field(description="The (namespace) prefix or shortname of the appschema."),
109
+ ]
110
+ description: Annotated[
111
+ str | None,
112
+ Field(
113
+ description="Description of the appschema, if defined.",
114
+ repr=False,
115
+ ),
116
+ ] = None
117
+
118
+ @field_validator("full_version", mode="before")
119
+ @classmethod
120
+ def _coerce_version(cls, v: Any):
121
+ """Attempts to convert a version string to a full semantic version."""
122
+ if isinstance(v, str):
123
+ return Version.parse(v, optional_minor_and_patch=True)
124
+ return v
125
+
126
+ def model_factory(self, name: str) -> type["BaseFeature"]:
127
+ """Factory method for retrieving the corresponding pydantic model representation of a feature class.
128
+
129
+ Args:
130
+ name: name of the feature class
131
+
132
+ Raises:
133
+ ValueError: requested FeatureType not found
134
+
135
+ Returns:
136
+ BaseFeature: The concrete feature class inheriting from BaseFeature.
137
+ """
138
+ try:
139
+ return getattr(self.module, name.replace("_", ""))
140
+ except AttributeError:
141
+ raise ValueError(f"featuretype {name!r} not found for appschema {self!r}")
142
+
143
+ @classmethod
144
+ def supported_appschemas(cls) -> list["Appschema"]:
145
+ """Return a list of currently supported appschemas."""
146
+ if not cls._supported_appschemas:
147
+ cls._supported_appschemas = [
148
+ cls.from_module(f"{__package__}.appschema.{module_name}")
149
+ for module_name in APPSCHEMA_MODULE_NAMES
150
+ ]
151
+ return cls._supported_appschemas
152
+
153
+ @classmethod
154
+ def from_prefix(cls, prefix: str, version: str) -> "Appschema":
155
+ """Builds an Appschema instance from the appschema's prefix and version.
156
+
157
+ Args:
158
+ prefix: the namespace prefix of the appschema, e.g. `xplan` or `xtrasse`
159
+ version: the version of the appschema; major and minor are required
160
+ """
161
+ sem_version = Version.parse(version, optional_minor_and_patch=True)
162
+ versions = []
163
+ candidates = []
164
+ for appschema in filter(
165
+ lambda appschema: appschema.prefix == prefix, cls.supported_appschemas()
166
+ ):
167
+ # if exact match, return
168
+ if (
169
+ sem_version.major == appschema.full_version.major
170
+ and sem_version.minor == appschema.full_version.minor
171
+ ):
172
+ return appschema
173
+ # if versions are compatible (modules minor version higher or equal), add to candidates
174
+ elif sem_version.is_compatible(appschema.full_version):
175
+ candidates.append(appschema)
176
+ else:
177
+ versions.append(
178
+ f"{appschema.full_version.major}.{appschema.full_version.minor}"
179
+ )
180
+ # return appschema with newer minor version, if found
181
+ # TODO: ensure minor version compatibility without version migration, e.g. via model_validator
182
+ if candidates:
183
+ return sorted(candidates, reverse=True)[0]
184
+ if versions:
185
+ e = NotImplementedError(
186
+ f"version {version!r} not supported for prefix {prefix!r}"
187
+ )
188
+ e.add_note(f"available versions: {', '.join(sorted(versions))}")
189
+ raise e
190
+ else:
191
+ raise NotImplementedError(f"no appschema with prefix {prefix!r}")
192
+
193
+ @classmethod
194
+ def from_namespace(cls, namespace: str) -> "Appschema":
195
+ """Builds an Appschema instance from the appschema's namespace.
196
+
197
+ Args:
198
+ namespace: the namespace URI of the appschema
199
+ """
200
+ uri = AnyUrl(namespace)
201
+ for appschema in filter(
202
+ lambda appschema: appschema.namespace_uri == uri, cls.supported_appschemas()
203
+ ):
204
+ if appschema.namespace_uri == uri:
205
+ return appschema
206
+ raise NotImplementedError(f"no appschema with namespace {namespace!r}")
207
+
208
+ @classmethod
209
+ def from_module(cls, module_name: str) -> "Appschema":
210
+ """Builds an Appschema instance from module name.
211
+
212
+ Args:
213
+ module_name: the fully qualified module name
214
+ """
215
+ try:
216
+ module = import_module(module_name)
217
+ model_cls: type[RootModel] = getattr(module, "Model")
218
+ except (ImportError, AttributeError) as exc:
219
+ raise RuntimeError(
220
+ f"Unable to resolve appschema metadata for module {module_name!r}"
221
+ ) from exc
222
+
223
+ if not issubclass(model_cls, RootModel):
224
+ raise ValueError(f"expected RootModel, got {model_cls!r}")
225
+
226
+ metadata = model_cls.model_fields["root"]
227
+
228
+ return cls.model_validate(
229
+ metadata.json_schema_extra
230
+ | {"description": metadata.description, "module": module}
231
+ )
232
+
233
+ @property
234
+ def version(self) -> str:
235
+ """The version of the appschema in `<major>.<minor>` format."""
236
+ return f"{self.full_version.major}.{self.full_version.minor}"
237
+
53
238
 
54
239
  class BaseCollection(BaseModel):
55
240
  """Container for features that provides validation of references.
@@ -333,6 +518,11 @@ class BaseFeature(BaseModel, GMLAdapter, CoretableAdapter, JsonFGAdapter):
333
518
  geom_model = [arg for arg in args if arg is not NoneType]
334
519
  return geom_model
335
520
 
521
+ @classmethod
522
+ def appschema(cls) -> Appschema:
523
+ """Return metadata about the application schema for the feature class."""
524
+ return Appschema.from_module(cls.__module__)
525
+
336
526
  @classmethod
337
527
  def get_version(cls) -> str:
338
528
  """Returns the application schema version of the object."""
@@ -2,12 +2,13 @@ from alembic import context
2
2
  from sqlalchemy import engine_from_config, pool
3
3
 
4
4
  from xplan_tools.model.orm import Base
5
+ from xplan_tools.settings import get_settings
5
6
 
6
7
  config = context.config
7
8
 
8
9
  target_metadata = Base.metadata
9
10
 
10
- SCHEMA = config.get_main_option("custom_schema")
11
+ settings = get_settings()
11
12
 
12
13
 
13
14
  def run_migrations_offline() -> None:
@@ -59,7 +60,7 @@ def run_migrations_online() -> None:
59
60
  context.configure(
60
61
  connection=connection,
61
62
  target_metadata=target_metadata,
62
- version_table_schema=SCHEMA,
63
+ version_table_schema=settings.db_schema,
63
64
  )
64
65
 
65
66
  with context.begin_transaction():
@@ -12,19 +12,22 @@ from alembic import op
12
12
  from geoalchemy2.types import Geometry
13
13
  from sqlalchemy.dialects import postgresql
14
14
 
15
+ from xplan_tools.settings import get_settings
16
+
15
17
  # revision identifiers, used by Alembic.
16
18
  revision: str = "3c3445a58565"
17
19
  down_revision: Union[str, Sequence[str], None] = None
18
20
  branch_labels: Union[str, Sequence[str], None] = None
19
21
  depends_on: Union[str, Sequence[str], None] = None
20
22
 
23
+ settings = get_settings()
24
+ schema = settings.db_schema
25
+ srid = settings.db_srid
26
+ views = settings.db_views
27
+
21
28
 
22
29
  def upgrade() -> None:
23
30
  """Upgrade schema."""
24
- config = op.get_context().config
25
- schema = config.get_main_option("custom_schema")
26
- srid = int(config.get_main_option("srid", 25832))
27
-
28
31
  op.create_table(
29
32
  "coretable",
30
33
  sa.Column(
@@ -171,7 +174,7 @@ def upgrade() -> None:
171
174
  schema=schema,
172
175
  )
173
176
 
174
- if config.get_main_option("with_views"):
177
+ if views:
175
178
  view_context = {"schema": schema or "public", "srid": srid}
176
179
  op.execute(
177
180
  sa.DDL(
@@ -221,7 +224,6 @@ def upgrade() -> None:
221
224
 
222
225
  def downgrade() -> None:
223
226
  """Downgrade schema."""
224
- schema = op.get_context().config.get_main_option("custom_schema")
225
227
  view_context = {"schema": schema or "public"}
226
228
  op.execute(
227
229
  sa.DDL(
@@ -10,12 +10,17 @@ from typing import Sequence, Union
10
10
  import sqlalchemy as sa
11
11
  from alembic import op
12
12
 
13
+ from xplan_tools.settings import get_settings
14
+
13
15
  # revision identifiers, used by Alembic.
14
16
  revision: str = "f8b74c08ec07"
15
17
  down_revision: Union[str, Sequence[str], None] = "3c3445a58565"
16
18
  branch_labels: Union[str, Sequence[str], None] = None
17
19
  depends_on: Union[str, Sequence[str], None] = None
18
20
 
21
+ settings = get_settings()
22
+ schema = settings.db_schema
23
+
19
24
 
20
25
  def upgrade() -> None:
21
26
  """Upgrade schema.
@@ -24,7 +29,6 @@ def upgrade() -> None:
24
29
  geometries in `coretable.geometry` to counter-clockwise orientation
25
30
  using `ST_ForcePolygonCCW` in-place.
26
31
  """
27
- schema = op.get_context().config.get_main_option("custom_schema")
28
32
  op.create_index(
29
33
  op.f("ix_refs_base_id"),
30
34
  "refs",
xplan_tools/model/orm.py CHANGED
@@ -10,7 +10,6 @@ from geoalchemy2.admin.dialects.sqlite import register_sqlite_mapping
10
10
  from sqlalchemy import (
11
11
  JSON,
12
12
  BigInteger,
13
- Column,
14
13
  Computed,
15
14
  DateTime,
16
15
  Dialect,
@@ -18,12 +17,10 @@ from sqlalchemy import (
18
17
  Identity,
19
18
  Integer,
20
19
  String,
21
- Table,
22
20
  Text,
23
21
  Uuid,
24
22
  case,
25
23
  cast,
26
- event,
27
24
  literal,
28
25
  select,
29
26
  )
@@ -39,6 +36,7 @@ from sqlalchemy.orm import (
39
36
  from sqlalchemy.sql import func
40
37
  from sqlalchemy.types import TypeDecorator
41
38
 
39
+ from xplan_tools.settings import get_settings
42
40
  from xplan_tools.util import linearize_geom
43
41
 
44
42
  register_sqlite_mapping({"ST_AsEWKT": "AsEWKT"})
@@ -113,7 +111,9 @@ class PGGeometry(TypeDecorator):
113
111
  )
114
112
  return func.ST_Transform(
115
113
  case_clause,
116
- func.Find_SRID(self.schema_name, self.table_name, self.column_name),
114
+ func.Find_SRID(
115
+ get_settings().db_schema or "public", "coretable", "geometry"
116
+ ),
117
117
  type_=self,
118
118
  )
119
119
 
@@ -129,17 +129,9 @@ class TextJSON(TypeDecorator):
129
129
  return json.loads(value)
130
130
 
131
131
 
132
- @event.listens_for(Column, "after_parent_attach")
133
- def _column_attached(column: Column, table: Table):
134
- """Add schema, table and column name to PGGeometry for Find_SRID function."""
135
- if column.name == "geometry":
136
- PGGeometry.column_name = column.name
137
- PGGeometry.schema_name = table.schema or "public"
138
- PGGeometry.table_name = table.name
139
-
140
-
141
132
  class Feature(Base):
142
133
  __tablename__ = "coretable"
134
+ __table_args__ = {"schema": get_settings().db_schema}
143
135
  pk: Mapped[int] = mapped_column(
144
136
  Integer().with_variant(BigInteger, "postgresql"),
145
137
  Identity(always=True),
@@ -182,12 +174,14 @@ class Feature(Base):
182
174
  cascade="all, delete-orphan",
183
175
  primaryjoin="Feature.id==Refs.base_id",
184
176
  lazy="selectin",
177
+ passive_deletes=True,
185
178
  )
186
179
  refs_inv: Mapped[list["Refs"]] = relationship(
187
180
  back_populates="feature_inv",
188
181
  cascade="all, delete-orphan",
189
182
  primaryjoin="Feature.id==Refs.related_id",
190
183
  lazy="selectin",
184
+ passive_deletes=True,
191
185
  )
192
186
 
193
187
  __mapper_args__ = {"primary_key": [id]}
@@ -306,6 +300,7 @@ class Feature(Base):
306
300
 
307
301
  class Refs(Base):
308
302
  __tablename__ = "refs"
303
+ __table_args__ = {"schema": get_settings().db_schema}
309
304
  pk: Mapped[int] = mapped_column(
310
305
  Integer().with_variant(BigInteger, "postgresql"),
311
306
  Identity(always=True),
@@ -313,13 +308,13 @@ class Refs(Base):
313
308
  )
314
309
  base_id: Mapped[UUID] = mapped_column(
315
310
  ForeignKey(
316
- "coretable.id", ondelete="CASCADE", deferrable=True, initially="DEFERRED"
311
+ Feature.id, ondelete="CASCADE", deferrable=True, initially="DEFERRED"
317
312
  ),
318
313
  index=True,
319
314
  )
320
315
  related_id: Mapped[UUID] = mapped_column(
321
316
  ForeignKey(
322
- "coretable.id", ondelete="CASCADE", deferrable=True, initially="DEFERRED"
317
+ Feature.id, ondelete="CASCADE", deferrable=True, initially="DEFERRED"
323
318
  ),
324
319
  index=True,
325
320
  )
@@ -327,12 +322,12 @@ class Refs(Base):
327
322
  rel_inv: Mapped[Optional[str]] = mapped_column(
328
323
  String(50).with_variant(Text, "geopackage")
329
324
  )
330
- feature: Mapped["Feature"] = relationship(
325
+ feature: Mapped[Feature] = relationship(
331
326
  back_populates="refs",
332
327
  primaryjoin="Feature.id==Refs.base_id",
333
328
  viewonly=True,
334
329
  )
335
- feature_inv: Mapped["Feature"] = relationship(
330
+ feature_inv: Mapped[Feature] = relationship(
336
331
  back_populates="refs_inv",
337
332
  primaryjoin="Feature.id==Refs.related_id",
338
333
  viewonly=True,
@@ -340,7 +335,6 @@ class Refs(Base):
340
335
 
341
336
  __mapper_args__ = {
342
337
  "primary_key": [base_id, related_id],
343
- "confirm_deleted_rows": False,
344
338
  }
345
339
 
346
340
  def __repr__(self) -> str:
@@ -0,0 +1,3 @@
1
+ from .settings import ENV_PREFIX, get_settings
2
+
3
+ __all__ = ["get_settings", "ENV_PREFIX"]
@@ -0,0 +1,23 @@
1
+ from functools import lru_cache
2
+
3
+ from pydantic_settings import BaseSettings, SettingsConfigDict
4
+
5
+ ENV_PREFIX = "xmas_"
6
+
7
+
8
+ class Settings(BaseSettings):
9
+ model_config = SettingsConfigDict(
10
+ extra="ignore",
11
+ env_prefix=ENV_PREFIX,
12
+ env_file=".env",
13
+ env_file_encoding="utf-8",
14
+ )
15
+
16
+ db_srid: int = 25832
17
+ db_schema: str | None = None
18
+ db_views: bool = True
19
+
20
+
21
+ @lru_cache
22
+ def get_settings() -> Settings:
23
+ return Settings()
@@ -12,8 +12,6 @@ import httpx
12
12
  import yaml
13
13
  from osgeo import gdal, ogr
14
14
  from pydantic import AnyUrl, ValidationError
15
- from sqlalchemy import text
16
- from sqlalchemy.exc import SQLAlchemyError
17
15
 
18
16
  from xplan_tools.model import model_factory
19
17
  from xplan_tools.resources.styles import RULES
@@ -297,28 +295,6 @@ class RasterReferenceUtil(ExternalReferenceUtil):
297
295
  return _check_validity(AnyUrl(f"file://{vsimem_path}"))
298
296
 
299
297
 
300
- def check_schema_accessibility(engine, schema: str):
301
- """Raises an exception if the schema does not exist or is not accessible to the current user."""
302
- try:
303
- with engine.connect() as conn:
304
- # Check if the schema exists and is accessible
305
- result = conn.execute(
306
- text("""
307
- SELECT schema_name
308
- FROM information_schema.schemata
309
- WHERE schema_name = :schema
310
- """),
311
- {"schema": schema},
312
- )
313
- if result.scalar() is None:
314
- raise RuntimeError(
315
- f"Schema '{schema}' does not exist or is not accessible."
316
- )
317
-
318
- except SQLAlchemyError as e:
319
- raise RuntimeError(f"Error checking schema '{schema}': {e}")
320
-
321
-
322
298
  class _Versions(str, Enum):
323
299
  _4_1 = "4.1"
324
300
  _5_4 = "5.4"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xplan-tools
3
- Version: 1.12.1
3
+ Version: 1.13.0
4
4
  Summary: Manage XPlanung data
5
5
  License: EUPL-1.2-or-later
6
6
  License-File: LICENSE.md
@@ -13,6 +13,7 @@ Classifier: License :: OSI Approved :: European Union Public Licence 1.2 (EUPL 1
13
13
  Classifier: Operating System :: OS Independent
14
14
  Classifier: Programming Language :: Python :: 3
15
15
  Requires-Dist: alembic (>=1.16.4,<2)
16
+ Requires-Dist: deprecated
16
17
  Requires-Dist: gdal (>=3.6.3)
17
18
  Requires-Dist: geoalchemy2 (>=0.16)
18
19
  Requires-Dist: httpx (>=0.27.2)
@@ -22,6 +23,8 @@ Requires-Dist: openpyxl (>=3.1)
22
23
  Requires-Dist: pandas (>=2.2.2)
23
24
  Requires-Dist: psycopg[binary] (>=3.2)
24
25
  Requires-Dist: pydantic (>=2.10.3)
26
+ Requires-Dist: pydantic-extra-types[semver]
27
+ Requires-Dist: pydantic-settings (>=2.12.0,<3)
25
28
  Requires-Dist: python-dateutil (>=2.9,<3)
26
29
  Requires-Dist: pytz (>=2024.1)
27
30
  Requires-Dist: pyyaml (>=6)
@@ -42,13 +45,13 @@ Description-Content-Type: text/markdown
42
45
  **[Documentation](https://xplan-tools-xleitstelle-xplanung-8446a974e8119a851af45bf94e0717.usercontent.opencode.de/)** | **[Repository](https://gitlab.opencode.de/xleitstelle/xplanung/xplan-tools)**
43
46
 
44
47
 
45
- A Python library to provide some useful means when working with XPlanung and XTrasse data, e.g. format conversion, version migration and database setup. Relies on [Pydantic](https://pydantic.dev) for a Python representation of the XPlanung/XTrasse model as well as serialization/de-serialization and validation.
48
+ A Python library to provide some useful means when working with XPlanung, XTrasse and XWaermeplan data, e.g. format conversion, version migration and database setup. Relies on [Pydantic](https://pydantic.dev) for a Python representation of the XPlanung/XTrasse/XWaermeplan model as well as serialization/de-serialization and validation.
46
49
 
47
- While it comes with a CLI, its modules are also meant to provide other applications a Python representation as well as an interface for XPlanung/XTrasse data.
50
+ While it comes with a CLI, its modules are also meant to provide other applications a Python representation as well as an interface for XPlanung/XTrasse/XWaermeplan data.
48
51
 
49
52
  ## Features
50
53
 
51
- * Conversion between GML, JSON-FG and DB encodings of XPlanung/XTrasse data.
54
+ * Conversion between GML, JSON-FG and DB encodings of XPlanung/XTrasse/XWaermeplan data.
52
55
  * Migration from older versions of XPlanung to the latest one.
53
56
  * Set up a database to store XPlanung data. Supports [PostgreSQL](https://www.postgresql.org)/[PostGIS](https://postgis.net) as well as [GeoPackage](https://www.geopackage.org) and [SpatiaLite](https://www.gaia-gis.it/fossil/libspatialite/index) SQLite databases.
54
57
  * Transformation from XPlanung to [INSPIRE PLU](https://inspire-mif.github.io/uml-models/approved/fc/#_P5531) based on the [official mappings](https://xleitstelle.de/xplanung/transformation-inspire/releases).
@@ -1,32 +1,35 @@
1
- xplan_tools/interface/__init__.py,sha256=Om5PlSEO5C6880hpGAu-Vpa98eGvKegoBthBrSDvjEA,4181
1
+ xplan_tools/interface/__init__.py,sha256=ranXJ28NL2eowWLLnm1jqmUlEwm3R6nvnQcu-KvNZa8,4082
2
2
  xplan_tools/interface/base.py,sha256=qZpQsSYa54omfw_tMfvcyQb4_F--_NFTq5Vc035rHIU,2014
3
- xplan_tools/interface/db.py,sha256=7bkCjO9MReP6hH6_vq8vUoJ-tfqPZaXF4Gkm51P9ZIM,16667
4
- xplan_tools/interface/gml.py,sha256=nBLNYXmf01NQQtpsBh_jix2Y9qktvn15-XRW_Y7b3AY,14048
5
- xplan_tools/interface/jsonfg.py,sha256=gR7XyTMTchntIJnZ4---lw1ob1RSkS9Wgli6Dpnhcaw,8669
3
+ xplan_tools/interface/db.py,sha256=pAWoeWsYXROPpkrI_7IqINaNDbEFN3Iw03O5pndcYtY,18737
4
+ xplan_tools/interface/gml.py,sha256=pPdlziRswIyo6YsO77CxniYt_6_O9uCJj4pSb7bX5sY,15871
5
+ xplan_tools/interface/jsonfg.py,sha256=rxG6_54boqaC_EFEUODbRpx6ZxprUkYTyiHi1R3Y_oI,9083
6
6
  xplan_tools/interface/shape.py,sha256=1aTJrgklHCv74BQjmOfzUlGXIqKSsuWhqA4bDIt3-gM,11295
7
- xplan_tools/main.py,sha256=6jv1ksrSmZB48K1kOLMj0oYIW8gaTKdmCgmWevEpklI,12747
8
- xplan_tools/model/__init__.py,sha256=T61iDdYLI2O2IIjW1eaqFZJIHgozw5IQ7ir3VfkeB20,3096
7
+ xplan_tools/main.py,sha256=fsoV0w5VkRwUyfVGdxm4K4CuP-IR1MlO07Y-nAzzNt0,12903
8
+ xplan_tools/model/__init__.py,sha256=8R-pZb8p8uxOEALGJ6pmeVD4T15-6TufIQ2xOJm3U14,3371
9
9
  xplan_tools/model/adapters/__init__.py,sha256=6Q-zr4hr5msjsc5D1RNjxsQip618Kk2jI3p_CJKpw5c,170
10
10
  xplan_tools/model/adapters/coretable.py,sha256=M9l-ZC--aDo7viVum8XWu9ArOfgBwB619-r0gOpx-q0,6541
11
- xplan_tools/model/adapters/gml.py,sha256=dZDn1R95UnJarWN7-Kcqv0rV5zvredd8MF15BZthaRE,9842
11
+ xplan_tools/model/adapters/gml.py,sha256=3iQRf2RIII8RMtIb_oR3gRXRy0vblaS3PF9BAV_bBUQ,10228
12
12
  xplan_tools/model/adapters/jsonfg.py,sha256=MuAYof4skYXApelilhryndh2KjP5tylgpjAfKR5dakk,3459
13
13
  xplan_tools/model/appschema/__init__.py,sha256=tY-W6RbLZbZZj6AJ1TwIpAQ3CLZy7F6dq2NhvsqTTpU,29
14
- xplan_tools/model/appschema/definitions.py,sha256=oSkLcd5G6JV0fszQ68PzsKCQNIUN_bT3Q8kOnZSHipo,2715
14
+ xplan_tools/model/appschema/definitions.py,sha256=8q2gh_tPej448xF6AgfCmUj6kq8ii14e5AnDAguHZyw,2347
15
15
  xplan_tools/model/appschema/inspire_base.py,sha256=-PizgUNWTKGZKvSMejAZmraW4nvJrS9M9L7TGlwA-c0,2779
16
16
  xplan_tools/model/appschema/inspire_base2.py,sha256=m5NH25fJcAcaT9RXO8BeEZT4qDaXf0bmqKVdjieiEY4,9827
17
17
  xplan_tools/model/appschema/inspire_plu40.py,sha256=vgUs3vFj6DAaGuT3G5EGUB90H35S-3zOwG7Uy4T9-1E,33568
18
- xplan_tools/model/appschema/xplan41.py,sha256=BYgsQhrdauy2KlQF5lmX9xjdMTxnCG_xFjVcwNkvIo8,982316
19
- xplan_tools/model/appschema/xplan54.py,sha256=Q4Tzfz0wu8iQCHUV14c0s1knUJq_aLzYpPHJXUZJV38,1001839
20
- xplan_tools/model/appschema/xplan60.py,sha256=BYAsfRF769ZwK2lm3Y12r2Yf3RGJD9xN0fwVuyBmJaQ,1190936
21
- xplan_tools/model/appschema/xplan61.py,sha256=lO0GIhYwqLxG4wPTxtWQ17XqYOTXkQzSyF2U80WX30k,1309344
22
- xplan_tools/model/appschema/xtrasse20.py,sha256=7YmqRtacXwPuZjQDYEaftEf-2gZ6944pmwOwwSws38s,531712
23
- xplan_tools/model/base.py,sha256=0FrZQWOLFD7nlKnSuOTTyWqjutgpEH9bpkFSPIYofzQ,20518
24
- xplan_tools/model/migrations/env.py,sha256=G4rghfPrPjlGDvt8d7YIHWsiv2xfI8cfUJtX0o6-9hQ,1939
18
+ xplan_tools/model/appschema/xplan41.py,sha256=brr9fZeWiecxe1ZY76GhGBuCiAAr8lyOS_7KpAnWb28,990645
19
+ xplan_tools/model/appschema/xplan54.py,sha256=FtktDeb6Bjvo6Fni3lpk1m9fBXVmL3lpV2eXy6dA9ZI,1011373
20
+ xplan_tools/model/appschema/xplan60.py,sha256=1HCignD1W4bA9DZvZhoEoPiTCSiDQTl6c9JGgFdS70Y,1200398
21
+ xplan_tools/model/appschema/xplan61.py,sha256=st0f3Ih_emUCB1tMcumGAPGNtryBZl16BB-Mqr6o12s,1319024
22
+ xplan_tools/model/appschema/xtrasse20.py,sha256=0Me-y1ydADedhK1fxe8W8fLR1228FMt4RIZU-iYJRro,537913
23
+ xplan_tools/model/appschema/xwp09.py,sha256=Q2oDiAvKjYAbIrHQ2hlwOnbB-XoeaDgi_qJnOVW8k6Y,218599
24
+ xplan_tools/model/base.py,sha256=xbTZyOHuyK3JkNl9DAdzDitKhS9LrJHc3c6cJJDHX5c,27583
25
+ xplan_tools/model/migrations/env.py,sha256=dJw3GH3_pJn2LuiflD1wwJi7Dq-9avm56xdRc50Wzpg,1974
25
26
  xplan_tools/model/migrations/script.py.mako,sha256=04kgeBtNMa4cCnG8CfQcKt6P6rnloIfj8wy0u_DBydM,704
26
- xplan_tools/model/migrations/versions/3c3445a58565_base_schema.py,sha256=O4_oyz2T9hnAPBQMiFiTL208-QT9M1E75GM9pF_TsRI,7951
27
- xplan_tools/model/migrations/versions/f8b74c08ec07_add_refs_indexes_ensure_polygon_ccw.py,sha256=lDtbAMooSOzq4J0WyLVGO2m1uqc6oZz-R1TWNcL8vcY,1735
28
- xplan_tools/model/orm.py,sha256=ET6OScXwGx_Rlis6Mitm8DIjWUVejwKCvWhz0TYE3ok,12131
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
29
30
  xplan_tools/resources/styles.py,sha256=h0ezUqZrsXMQz2bTcF0nIyoI-IxMItstHx1j1xYGi3c,45010
31
+ xplan_tools/settings/__init__.py,sha256=yQdL0KFclWVUBE0ZMUTkktKMa9D78RDcPrHkK23Z9m8,89
32
+ xplan_tools/settings/settings.py,sha256=bABcqw7--TU-khpE9l-2XkavlQ-xuVZwmBmJiD0yRVw,462
30
33
  xplan_tools/transform/__init__.py,sha256=DBIscGFL0H-oifh_XCeB0T8M5el3JGFwNJo8t-qUwTY,2159
31
34
  xplan_tools/transform/mappingtables/XPlanToINSPIRE-SupplementaryRegulation_2_6_2025-10-08.xlsx,sha256=eE0avwlJCBso5zfUXwiY5VNE1x-XgGpWdo5fXKErd3M,85819
32
35
  xplan_tools/transform/mappingtables/XPlanToINSPIRE-ZoningElement_2_6_2025-10-08.xlsx,sha256=bP8q5JYO8MZWCtr3hW4I92gyHY1P5q8ywEUlFPadIfw,77838
@@ -36,11 +39,11 @@ xplan_tools/transform/migrate_54_60.py,sha256=GeVKcfWRaf0vJFuZmvp5jbGJLwB-lX7ebe
36
39
  xplan_tools/transform/migrate_60_61.py,sha256=S-fdnTjQUf8LGnEvsOY73zZYDvyVgIhVewPqyRR3Rcs,614
37
40
  xplan_tools/transform/migrate_6x_plu.py,sha256=3w5UfjdUKFUiLma3Ppc9fAogPVxVshtUzzve0Dv0nWM,216797
38
41
  xplan_tools/transform/transformer.py,sha256=2O4Aq_eTo9e2JOUwq9tJnfv6dwOxqNDdTGPtV_SAL1w,26216
39
- xplan_tools/util/__init__.py,sha256=_YR9fvj4H3fnIBh_rJtkcjgEnn5Dm1kq9pkgLzSyBCU,12585
42
+ xplan_tools/util/__init__.py,sha256=OqOorl5s8BZANbLeFE9wWlozvA64RINn9wigMkds2u8,11704
40
43
  xplan_tools/util/style.py,sha256=6Iyj-fsmnJylkJo-70wCFjfsbbwUGAS5QTN2IMJAVdI,7503
41
44
  xplan_tools/util/validate.py,sha256=3inTRLHrVonfQ9FDjtvSNKlAGem5DKtEvLqW7NxwWQs,3327
42
- xplan_tools-1.12.1.dist-info/METADATA,sha256=lYwXAntjBCLhpwHqaFRvmtFiNKirmnOdS4hHtgnIYis,4658
43
- xplan_tools-1.12.1.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
44
- xplan_tools-1.12.1.dist-info/entry_points.txt,sha256=NV98QKVV3vDuWnJtWsKZvcpEH2N7SF1yHXvVMet23Ps,52
45
- xplan_tools-1.12.1.dist-info/licenses/LICENSE.md,sha256=b8nnCcy_4Nd_v_okJ6mDKCvi64jkexzbSfIag7TR5mU,13827
46
- xplan_tools-1.12.1.dist-info/RECORD,,
45
+ xplan_tools-1.13.0.dist-info/METADATA,sha256=9RF_9DOvPsv7KfMti44bJRpAj64nVLOL9MbwCqjUrxA,4824
46
+ xplan_tools-1.13.0.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
47
+ xplan_tools-1.13.0.dist-info/entry_points.txt,sha256=NV98QKVV3vDuWnJtWsKZvcpEH2N7SF1yHXvVMet23Ps,52
48
+ xplan_tools-1.13.0.dist-info/licenses/LICENSE.md,sha256=b8nnCcy_4Nd_v_okJ6mDKCvi64jkexzbSfIag7TR5mU,13827
49
+ xplan_tools-1.13.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.2.1
2
+ Generator: poetry-core 2.3.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any