NCZ2Geo 0.1.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.
- ncz2geo/__init__.py +30 -0
- ncz2geo/cli.py +119 -0
- ncz2geo/eplan_catalog.py +2235 -0
- ncz2geo/geojson.py +91 -0
- ncz2geo/mpyy_catalog.py +1323 -0
- ncz2geo/ncz_engine/__init__.py +17 -0
- ncz2geo/ncz_engine/model.py +62 -0
- ncz2geo/ncz_engine/v2/__init__.py +5 -0
- ncz2geo/ncz_engine/v2/attributes.py +238 -0
- ncz2geo/ncz_engine/v2/binary.py +104 -0
- ncz2geo/ncz_engine/v2/blocks.py +201 -0
- ncz2geo/ncz_engine/v2/cache.py +134 -0
- ncz2geo/ncz_engine/v2/geometry.py +630 -0
- ncz2geo/ncz_engine/v2/parser.py +211 -0
- ncz2geo/plangml.py +260 -0
- ncz2geo/plangml_schema.py +117 -0
- ncz2geo/py.typed +1 -0
- ncz2geo/reader.py +211 -0
- ncz2geo-0.1.0.dist-info/METADATA +234 -0
- ncz2geo-0.1.0.dist-info/RECORD +24 -0
- ncz2geo-0.1.0.dist-info/WHEEL +5 -0
- ncz2geo-0.1.0.dist-info/entry_points.txt +2 -0
- ncz2geo-0.1.0.dist-info/licenses/LICENSE +280 -0
- ncz2geo-0.1.0.dist-info/top_level.txt +1 -0
ncz2geo/__init__.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""NCZ2Geo: Netcad NCZ/NCA reader with PlanGML and e-Plan style metadata."""
|
|
2
|
+
|
|
3
|
+
from .geojson import entities_to_feature_collection, write_geojson
|
|
4
|
+
from .plangml import (
|
|
5
|
+
LayerClassification,
|
|
6
|
+
classify_entity,
|
|
7
|
+
classify_layer,
|
|
8
|
+
classify_layers,
|
|
9
|
+
detect_plan_type,
|
|
10
|
+
normalize_layer_name,
|
|
11
|
+
)
|
|
12
|
+
from .reader import LayerSummary, NetcadReader, inspect_source, parse_netcad
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.0"
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"LayerClassification",
|
|
18
|
+
"LayerSummary",
|
|
19
|
+
"NetcadReader",
|
|
20
|
+
"__version__",
|
|
21
|
+
"classify_entity",
|
|
22
|
+
"classify_layer",
|
|
23
|
+
"classify_layers",
|
|
24
|
+
"detect_plan_type",
|
|
25
|
+
"entities_to_feature_collection",
|
|
26
|
+
"inspect_source",
|
|
27
|
+
"normalize_layer_name",
|
|
28
|
+
"parse_netcad",
|
|
29
|
+
"write_geojson",
|
|
30
|
+
]
|
ncz2geo/cli.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Command-line interface for NCZ2Geo."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from . import __version__
|
|
11
|
+
from .geojson import write_geojson
|
|
12
|
+
from .plangml import classify_layer
|
|
13
|
+
from .reader import NetcadReader, parse_netcad
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
17
|
+
parser = argparse.ArgumentParser(
|
|
18
|
+
prog="ncz2geo",
|
|
19
|
+
description="Inspect Netcad NCZ/NCA files and export PlanGML-aware GeoJSON.",
|
|
20
|
+
)
|
|
21
|
+
parser.add_argument("--version", action="version", version=f"NCZ2Geo {__version__}")
|
|
22
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
23
|
+
|
|
24
|
+
inspect_parser = subparsers.add_parser("inspect", help="List NCZ/NCA layers and PlanGML matches.")
|
|
25
|
+
inspect_parser.add_argument("source", type=Path)
|
|
26
|
+
inspect_parser.add_argument("--plan-type", default="UIP", choices=("UIP", "NIP", "CDP"))
|
|
27
|
+
inspect_parser.add_argument("--json", action="store_true", help="Print machine-readable JSON.")
|
|
28
|
+
|
|
29
|
+
convert_parser = subparsers.add_parser("convert", help="Convert an NCZ/NCA file to styled GeoJSON.")
|
|
30
|
+
convert_parser.add_argument("source", type=Path)
|
|
31
|
+
convert_parser.add_argument("output", type=Path)
|
|
32
|
+
convert_parser.add_argument("--plan-type", default="UIP", choices=("UIP", "NIP", "CDP"))
|
|
33
|
+
convert_parser.add_argument(
|
|
34
|
+
"--layers",
|
|
35
|
+
help="Comma-separated layer codes to decode; defaults to all layers.",
|
|
36
|
+
)
|
|
37
|
+
return parser
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def main(argv: list[str] | None = None) -> int:
|
|
41
|
+
parser = build_parser()
|
|
42
|
+
args = parser.parse_args(argv)
|
|
43
|
+
if args.command == "inspect":
|
|
44
|
+
return _inspect(args)
|
|
45
|
+
if args.command == "convert":
|
|
46
|
+
return _convert(args)
|
|
47
|
+
parser.error(f"Unknown command: {args.command}")
|
|
48
|
+
return 2
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _inspect(args: argparse.Namespace) -> int:
|
|
52
|
+
reader = NetcadReader(args.source).index()
|
|
53
|
+
summaries = reader.layer_summaries()
|
|
54
|
+
classified = [
|
|
55
|
+
{
|
|
56
|
+
**summary.to_dict(),
|
|
57
|
+
"plangml": classify_layer(summary.layer_name, args.plan_type).to_dict(),
|
|
58
|
+
}
|
|
59
|
+
for summary in summaries
|
|
60
|
+
]
|
|
61
|
+
if args.json:
|
|
62
|
+
print(
|
|
63
|
+
json.dumps(
|
|
64
|
+
{
|
|
65
|
+
"source": str(args.source),
|
|
66
|
+
"backend": reader.backend,
|
|
67
|
+
"from_cache": reader.from_cache,
|
|
68
|
+
"version_name": reader.version_name,
|
|
69
|
+
"epsg": reader.epsg,
|
|
70
|
+
"projection_text": reader.projection_text,
|
|
71
|
+
"plan_type": args.plan_type,
|
|
72
|
+
"layers": classified,
|
|
73
|
+
},
|
|
74
|
+
ensure_ascii=False,
|
|
75
|
+
indent=2,
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
return 0
|
|
79
|
+
|
|
80
|
+
print(f"Source: {args.source}")
|
|
81
|
+
print(f"Backend: {reader.backend}")
|
|
82
|
+
print(f"Plan type: {args.plan_type}")
|
|
83
|
+
if reader.version_name:
|
|
84
|
+
print(f"Version: {reader.version_name}")
|
|
85
|
+
if reader.epsg:
|
|
86
|
+
print(f"CRS: {reader.epsg}")
|
|
87
|
+
if not classified:
|
|
88
|
+
print("No supported Netcad geometry layers found.")
|
|
89
|
+
return 0
|
|
90
|
+
print("Layers:")
|
|
91
|
+
for item in classified:
|
|
92
|
+
families = ", ".join(item["families"]) or "-"
|
|
93
|
+
plangml = item["plangml"]
|
|
94
|
+
identity = plangml["identity"] or {}
|
|
95
|
+
label = ""
|
|
96
|
+
if plangml["style"]:
|
|
97
|
+
label = plangml["style"].get("label", "")
|
|
98
|
+
tabaka = identity.get("tabaka") or plangml["style_key"] or "unmatched"
|
|
99
|
+
print(
|
|
100
|
+
f" {item['layer_code']:>3} {item['layer_name'] or '(unnamed)'} "
|
|
101
|
+
f"{item['record_count']} record(s) [{families}] {tabaka} {label}"
|
|
102
|
+
)
|
|
103
|
+
return 0
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _convert(args: argparse.Namespace) -> int:
|
|
107
|
+
if args.layers:
|
|
108
|
+
layer_codes = [int(part.strip()) for part in args.layers.split(",") if part.strip()]
|
|
109
|
+
reader = NetcadReader(args.source).index()
|
|
110
|
+
entities = reader.decode_layers(layer_codes)
|
|
111
|
+
else:
|
|
112
|
+
entities = parse_netcad(args.source).entities
|
|
113
|
+
collection = write_geojson(entities, args.output, plan_type=args.plan_type)
|
|
114
|
+
print(f"Wrote {len(collection['features'])} feature(s) to {args.output}")
|
|
115
|
+
return 0
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
if __name__ == "__main__":
|
|
119
|
+
raise SystemExit(main(sys.argv[1:]))
|