netload 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.
- netload/__init__.py +68 -0
- netload/cli.py +182 -0
- netload/exceptions.py +45 -0
- netload/exporters.py +249 -0
- netload/network.py +200 -0
- netload/parser.py +221 -0
- netload/py.typed +0 -0
- netload/table.py +55 -0
- netload/wkt.py +119 -0
- netload-0.1.0.dist-info/METADATA +295 -0
- netload-0.1.0.dist-info/RECORD +15 -0
- netload-0.1.0.dist-info/WHEEL +5 -0
- netload-0.1.0.dist-info/entry_points.txt +2 -0
- netload-0.1.0.dist-info/licenses/LICENSE +21 -0
- netload-0.1.0.dist-info/top_level.txt +1 -0
netload/__init__.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""netload: a lightweight parser for PTV Visum ``.net`` files."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .exceptions import (
|
|
6
|
+
VisumNetEncodingError,
|
|
7
|
+
VisumNetError,
|
|
8
|
+
VisumNetParseError,
|
|
9
|
+
)
|
|
10
|
+
from .network import Network
|
|
11
|
+
from .parser import parse_net, parse_net_with_encoding
|
|
12
|
+
from .table import Table
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.0"
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"Network",
|
|
18
|
+
"Table",
|
|
19
|
+
"VisumNetEncodingError",
|
|
20
|
+
"VisumNetError",
|
|
21
|
+
"VisumNetParseError",
|
|
22
|
+
"parse_net",
|
|
23
|
+
"read_net",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def read_net(
|
|
28
|
+
path: str,
|
|
29
|
+
encoding: str | None = None,
|
|
30
|
+
sep: str = ";",
|
|
31
|
+
infer_types: bool = False,
|
|
32
|
+
) -> Network:
|
|
33
|
+
"""Read a PTV Visum ``.net`` file and return a :class:`Network`.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
path:
|
|
38
|
+
Path of the ``.net`` file.
|
|
39
|
+
encoding:
|
|
40
|
+
Optional explicit encoding. When omitted, the encoding is detected
|
|
41
|
+
automatically (BOM, then UTF-8, then Windows-1251).
|
|
42
|
+
sep:
|
|
43
|
+
Field separator used inside tables (default ``";"``).
|
|
44
|
+
infer_types:
|
|
45
|
+
When ``True``, apply a light numeric type inference on every column.
|
|
46
|
+
The default preserves the original string values.
|
|
47
|
+
|
|
48
|
+
Returns
|
|
49
|
+
-------
|
|
50
|
+
A :class:`Network` exposing every parsed table as a
|
|
51
|
+
``pandas.DataFrame``.
|
|
52
|
+
|
|
53
|
+
Raises
|
|
54
|
+
------
|
|
55
|
+
FileNotFoundError
|
|
56
|
+
If ``path`` does not exist.
|
|
57
|
+
netload.exceptions.VisumNetEncodingError
|
|
58
|
+
If the file cannot be decoded.
|
|
59
|
+
netload.exceptions.VisumNetParseError
|
|
60
|
+
If the file structure is invalid.
|
|
61
|
+
"""
|
|
62
|
+
tables, resolved_encoding = parse_net_with_encoding(
|
|
63
|
+
path,
|
|
64
|
+
encoding=encoding,
|
|
65
|
+
sep=sep,
|
|
66
|
+
infer_types=infer_types,
|
|
67
|
+
)
|
|
68
|
+
return Network(tables, source=path, encoding=resolved_encoding)
|
netload/cli.py
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""Command line interface for :mod:`netload`."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
from typing import List, Optional
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
11
|
+
parser = argparse.ArgumentParser(
|
|
12
|
+
prog="netload",
|
|
13
|
+
description=(
|
|
14
|
+
"Read and transform any PTV Visum/Simetra .net network file "
|
|
15
|
+
"into CSV, JSON or GeoJSON."
|
|
16
|
+
),
|
|
17
|
+
)
|
|
18
|
+
parser.add_argument("input", help="Path to the .net file")
|
|
19
|
+
parser.add_argument(
|
|
20
|
+
"--list-tables",
|
|
21
|
+
action="store_true",
|
|
22
|
+
help="List the parsed tables with their fields (columns)",
|
|
23
|
+
)
|
|
24
|
+
parser.add_argument(
|
|
25
|
+
"--export-csv",
|
|
26
|
+
metavar="DIR",
|
|
27
|
+
help="Export every table to CSV files inside DIR",
|
|
28
|
+
)
|
|
29
|
+
parser.add_argument(
|
|
30
|
+
"--export-json",
|
|
31
|
+
metavar="DIR",
|
|
32
|
+
help="Export every table to JSON files inside DIR",
|
|
33
|
+
)
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
"--export-geojson",
|
|
36
|
+
metavar="DIR",
|
|
37
|
+
help="Export tables with geometry to GeoJSON files inside DIR",
|
|
38
|
+
)
|
|
39
|
+
parser.add_argument(
|
|
40
|
+
"--tables",
|
|
41
|
+
metavar="NAME[,NAME...]",
|
|
42
|
+
help=(
|
|
43
|
+
"Limit the export to specific tables (comma-separated, e.g. "
|
|
44
|
+
"NODE,LINK). Default: all tables."
|
|
45
|
+
),
|
|
46
|
+
)
|
|
47
|
+
parser.add_argument(
|
|
48
|
+
"--table",
|
|
49
|
+
metavar="NAME",
|
|
50
|
+
help="Select a single table to transform (used with --to-*)",
|
|
51
|
+
)
|
|
52
|
+
parser.add_argument(
|
|
53
|
+
"--to-csv",
|
|
54
|
+
metavar="FILE",
|
|
55
|
+
help="Write the selected table to a CSV file",
|
|
56
|
+
)
|
|
57
|
+
parser.add_argument(
|
|
58
|
+
"--to-json",
|
|
59
|
+
metavar="FILE",
|
|
60
|
+
help="Write the selected table to a JSON file",
|
|
61
|
+
)
|
|
62
|
+
parser.add_argument(
|
|
63
|
+
"--to-geojson",
|
|
64
|
+
metavar="FILE",
|
|
65
|
+
help="Write the selected table to a GeoJSON file",
|
|
66
|
+
)
|
|
67
|
+
parser.add_argument(
|
|
68
|
+
"--sep",
|
|
69
|
+
default=";",
|
|
70
|
+
help="Field separator used by the file and CSV export (default: ';')",
|
|
71
|
+
)
|
|
72
|
+
parser.add_argument(
|
|
73
|
+
"--encoding",
|
|
74
|
+
default=None,
|
|
75
|
+
help="Force a file encoding (default: auto-detect)",
|
|
76
|
+
)
|
|
77
|
+
return parser
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _print_summary(tables: dict) -> None:
|
|
81
|
+
print("Visum network loaded successfully")
|
|
82
|
+
print()
|
|
83
|
+
print("Tables:")
|
|
84
|
+
for name, table in tables.items():
|
|
85
|
+
print(f"- {name}: {len(table.df)} rows")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _print_tables_with_fields(tables: dict) -> None:
|
|
89
|
+
print("Visum network loaded successfully")
|
|
90
|
+
print()
|
|
91
|
+
print("Tables and fields:")
|
|
92
|
+
for name, table in tables.items():
|
|
93
|
+
fields = ", ".join(table.df.columns) or "(no columns)"
|
|
94
|
+
print(f"- {name}: {len(table.df)} rows")
|
|
95
|
+
print(f" fields: {fields}")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def main(argv: Optional[List[str]] = None) -> int:
|
|
99
|
+
args = build_parser().parse_args(argv)
|
|
100
|
+
|
|
101
|
+
from .parser import parse_net
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
tables = parse_net(args.input, encoding=args.encoding, sep=args.sep)
|
|
105
|
+
except FileNotFoundError:
|
|
106
|
+
print(f"error: file not found: {args.input}", file=sys.stderr)
|
|
107
|
+
return 2
|
|
108
|
+
|
|
109
|
+
from .network import Network
|
|
110
|
+
|
|
111
|
+
network = Network(tables, source=args.input, encoding="")
|
|
112
|
+
|
|
113
|
+
if args.table is not None:
|
|
114
|
+
return _transform_single_table(network, args)
|
|
115
|
+
|
|
116
|
+
if args.list_tables:
|
|
117
|
+
_print_tables_with_fields(network.tables)
|
|
118
|
+
else:
|
|
119
|
+
_print_summary(network.tables)
|
|
120
|
+
|
|
121
|
+
written = []
|
|
122
|
+
selected = None
|
|
123
|
+
if args.tables:
|
|
124
|
+
selected = [name.strip() for name in args.tables.split(",") if name.strip()]
|
|
125
|
+
|
|
126
|
+
if args.export_csv:
|
|
127
|
+
written += network.export_csv(args.export_csv, sep=args.sep, tables=selected)
|
|
128
|
+
print(f"\nExported {len(written)} CSV files to {args.export_csv}")
|
|
129
|
+
if args.export_json:
|
|
130
|
+
written += network.export_json(args.export_json, tables=selected)
|
|
131
|
+
print(f"\nExported {len(written)} JSON files to {args.export_json}")
|
|
132
|
+
if args.export_geojson:
|
|
133
|
+
written += network.export_geojson(args.export_geojson, tables=selected)
|
|
134
|
+
print(f"\nExported {len(written)} GeoJSON files to {args.export_geojson}")
|
|
135
|
+
|
|
136
|
+
return 0
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _transform_single_table(network: Network, args: argparse.Namespace) -> int:
|
|
140
|
+
from .exceptions import VisumNetError
|
|
141
|
+
from .exporters import export_table_csv, export_table_geojson, export_table_json
|
|
142
|
+
|
|
143
|
+
if args.table not in network:
|
|
144
|
+
print(
|
|
145
|
+
f"error: table {args.table!r} not found. Available: "
|
|
146
|
+
f"{', '.join(network.table_names)}",
|
|
147
|
+
file=sys.stderr,
|
|
148
|
+
)
|
|
149
|
+
return 2
|
|
150
|
+
|
|
151
|
+
targets = [
|
|
152
|
+
flag
|
|
153
|
+
for flag in ("to_csv", "to_json", "to_geojson")
|
|
154
|
+
if getattr(args, flag) is not None
|
|
155
|
+
]
|
|
156
|
+
if not targets:
|
|
157
|
+
print("error: --table requires at least one of --to-csv/--to-json/--to-geojson",
|
|
158
|
+
file=sys.stderr)
|
|
159
|
+
return 2
|
|
160
|
+
|
|
161
|
+
df = network[args.table]
|
|
162
|
+
for flag in targets:
|
|
163
|
+
path = getattr(args, flag)
|
|
164
|
+
if flag == "to_csv":
|
|
165
|
+
export_table_csv(df, path, sep=args.sep)
|
|
166
|
+
elif flag == "to_json":
|
|
167
|
+
export_table_json(df, path)
|
|
168
|
+
else:
|
|
169
|
+
if not export_table_geojson(df, path):
|
|
170
|
+
print(
|
|
171
|
+
f"warning: table {args.table!r} has no geometry; "
|
|
172
|
+
f"GeoJSON not written",
|
|
173
|
+
file=sys.stderr,
|
|
174
|
+
)
|
|
175
|
+
continue
|
|
176
|
+
print(f"Exported table {args.table} -> {path}")
|
|
177
|
+
|
|
178
|
+
return 0
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
if __name__ == "__main__":
|
|
182
|
+
sys.exit(main())
|
netload/exceptions.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Public exceptions for :mod:`netload`."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class VisumNetError(Exception):
|
|
9
|
+
"""Base exception for all errors raised by :mod:`netload`."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class VisumNetParseError(VisumNetError):
|
|
13
|
+
"""Raised when a ``.net`` file cannot be parsed correctly.
|
|
14
|
+
|
|
15
|
+
Attributes
|
|
16
|
+
----------
|
|
17
|
+
source:
|
|
18
|
+
Path of the file being parsed.
|
|
19
|
+
line:
|
|
20
|
+
Line number where the problem occurred (1-based), if known.
|
|
21
|
+
message:
|
|
22
|
+
Human readable description of the problem.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, source: str, message: str, line: Optional[int] = None) -> None:
|
|
26
|
+
self.source = source
|
|
27
|
+
self.line = line
|
|
28
|
+
self.message = message
|
|
29
|
+
location = f"{source}:{line}" if line is not None else source
|
|
30
|
+
super().__init__(f"{location}: {message}")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class VisumNetEncodingError(VisumNetError):
|
|
34
|
+
"""Raised when the encoding of a ``.net`` file cannot be determined/decoded.
|
|
35
|
+
|
|
36
|
+
Attributes
|
|
37
|
+
----------
|
|
38
|
+
source:
|
|
39
|
+
Path of the file being parsed.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self, source: str, message: str) -> None:
|
|
43
|
+
self.source = source
|
|
44
|
+
self.message = message
|
|
45
|
+
super().__init__(f"{source}: {message}")
|
netload/exporters.py
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"""Export helpers: CSV, JSON and GeoJSON."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence
|
|
9
|
+
|
|
10
|
+
import pandas as pd
|
|
11
|
+
|
|
12
|
+
from .exceptions import VisumNetError
|
|
13
|
+
from .wkt import parse_wkt
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
16
|
+
from .network import Network
|
|
17
|
+
from .table import Table
|
|
18
|
+
|
|
19
|
+
# Column names that are known to hold WKT geometry in .net files.
|
|
20
|
+
_WKT_COLUMNS = ("WKTSURFACE", "WKTPOLY", "GEOMETRY")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _resolve_tables(
|
|
24
|
+
network: "Network", tables: Optional[Sequence[str]]
|
|
25
|
+
) -> List["Table"]:
|
|
26
|
+
"""Return the tables to export.
|
|
27
|
+
|
|
28
|
+
When ``tables`` is ``None`` every table is returned (file order);
|
|
29
|
+
otherwise only the requested table names (file order is preserved).
|
|
30
|
+
"""
|
|
31
|
+
if tables is None:
|
|
32
|
+
return list(network.tables.values())
|
|
33
|
+
|
|
34
|
+
resolved: List["Table"] = []
|
|
35
|
+
for name in tables:
|
|
36
|
+
if name not in network.tables:
|
|
37
|
+
raise VisumNetError(
|
|
38
|
+
f"table {name!r} not found in the network "
|
|
39
|
+
f"(tables: {', '.join(network.table_names) or 'none'})"
|
|
40
|
+
)
|
|
41
|
+
resolved.append(network.tables[name])
|
|
42
|
+
return resolved
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# -- small helpers ----------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _records_to_jsonable(df: pd.DataFrame) -> List[Dict[str, Any]]:
|
|
49
|
+
"""Convert a DataFrame to a list of JSON-safe records (NaN -> None)."""
|
|
50
|
+
object_frame = df.astype(object).where(pd.notna(df), None)
|
|
51
|
+
return object_frame.to_dict("records")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _geometry_for_row(row: pd.Series, geometry_col: Optional[str]) -> Optional[Dict[str, Any]]:
|
|
55
|
+
if geometry_col is None:
|
|
56
|
+
return None
|
|
57
|
+
value = row.get(geometry_col)
|
|
58
|
+
if value is None or (isinstance(value, float) and pd.isna(value)):
|
|
59
|
+
return None
|
|
60
|
+
return parse_wkt(str(value))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# -- single-table exporters -------------------------------------------------
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def export_table_csv(
|
|
67
|
+
df: pd.DataFrame,
|
|
68
|
+
path: str | os.PathLike[str],
|
|
69
|
+
sep: str = ";",
|
|
70
|
+
encoding: str = "utf-8",
|
|
71
|
+
) -> None:
|
|
72
|
+
"""Write one DataFrame to a CSV file."""
|
|
73
|
+
df.to_csv(
|
|
74
|
+
path,
|
|
75
|
+
sep=sep,
|
|
76
|
+
index=False,
|
|
77
|
+
encoding=encoding,
|
|
78
|
+
lineterminator="\n",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def export_table_json(
|
|
83
|
+
df: pd.DataFrame,
|
|
84
|
+
path: str | os.PathLike[str],
|
|
85
|
+
encoding: str = "utf-8",
|
|
86
|
+
) -> None:
|
|
87
|
+
"""Write one DataFrame to a JSON array of records."""
|
|
88
|
+
with open(path, "w", encoding=encoding) as handle:
|
|
89
|
+
json.dump(
|
|
90
|
+
_records_to_jsonable(df),
|
|
91
|
+
handle,
|
|
92
|
+
ensure_ascii=False,
|
|
93
|
+
indent=2,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _pick_geometry_column(df: pd.DataFrame) -> Optional[str]:
|
|
98
|
+
columns = set(df.columns)
|
|
99
|
+
for candidate in _WKT_COLUMNS:
|
|
100
|
+
if candidate in columns:
|
|
101
|
+
return candidate
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def table_geometry_kind(df: pd.DataFrame) -> Optional[str]:
|
|
106
|
+
"""Describe the geometry of a table.
|
|
107
|
+
|
|
108
|
+
Returns ``"WKT"`` when a WKT geometry column exists, ``"XY"`` when the
|
|
109
|
+
table has ``XCOORD``/``YCOORD`` columns, otherwise ``None``.
|
|
110
|
+
"""
|
|
111
|
+
if _pick_geometry_column(df) is not None:
|
|
112
|
+
return "WKT"
|
|
113
|
+
if "XCOORD" in df.columns and "YCOORD" in df.columns:
|
|
114
|
+
return "XY"
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def export_table_geojson(
|
|
119
|
+
df: pd.DataFrame,
|
|
120
|
+
path: str | os.PathLike[str],
|
|
121
|
+
encoding: str = "utf-8",
|
|
122
|
+
) -> bool:
|
|
123
|
+
"""Write one DataFrame to a GeoJSON ``FeatureCollection``.
|
|
124
|
+
|
|
125
|
+
Geometry is taken from a WKT column (``WKTSURFACE``, ``WKTPOLY``,
|
|
126
|
+
``GEOMETRY``) when available, otherwise from ``XCOORD``/``YCOORD`` as
|
|
127
|
+
Point features.
|
|
128
|
+
|
|
129
|
+
Returns ``False`` when the table has no geometry and nothing is written.
|
|
130
|
+
"""
|
|
131
|
+
geometry_col = _pick_geometry_column(df)
|
|
132
|
+
|
|
133
|
+
if geometry_col is None and not (
|
|
134
|
+
"XCOORD" in df.columns and "YCOORD" in df.columns
|
|
135
|
+
):
|
|
136
|
+
return False
|
|
137
|
+
|
|
138
|
+
geometry_keys = {geometry_col, "XCOORD", "YCOORD"} if geometry_col else {"XCOORD", "YCOORD"}
|
|
139
|
+
property_columns = [c for c in df.columns if c not in geometry_keys]
|
|
140
|
+
|
|
141
|
+
features: List[Dict[str, Any]] = []
|
|
142
|
+
for _, row in df.iterrows():
|
|
143
|
+
if geometry_col is not None:
|
|
144
|
+
geometry = _geometry_for_row(row, geometry_col)
|
|
145
|
+
else:
|
|
146
|
+
x = row.get("XCOORD")
|
|
147
|
+
y = row.get("YCOORD")
|
|
148
|
+
if x is None or y is None or pd.isna(x) or pd.isna(y):
|
|
149
|
+
geometry = None
|
|
150
|
+
else:
|
|
151
|
+
geometry = {"type": "Point", "coordinates": [float(x), float(y)]}
|
|
152
|
+
properties = {
|
|
153
|
+
col: row.get(col) for col in property_columns
|
|
154
|
+
}
|
|
155
|
+
for key in list(properties):
|
|
156
|
+
value = properties[key]
|
|
157
|
+
if value is not None and isinstance(value, float) and pd.isna(value):
|
|
158
|
+
properties[key] = None
|
|
159
|
+
features.append({"type": "Feature", "geometry": geometry, "properties": properties})
|
|
160
|
+
|
|
161
|
+
feature_collection: Dict[str, Any] = {
|
|
162
|
+
"type": "FeatureCollection",
|
|
163
|
+
"features": features,
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
with open(path, "w", encoding=encoding) as handle:
|
|
167
|
+
json.dump(feature_collection, handle, ensure_ascii=False, indent=2)
|
|
168
|
+
|
|
169
|
+
return True
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# -- network-level exporters -------------------------------------------------
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def export_tables(
|
|
176
|
+
network: "Network",
|
|
177
|
+
output_dir: str | os.PathLike[str],
|
|
178
|
+
sep: str = ";",
|
|
179
|
+
encoding: str = "utf-8",
|
|
180
|
+
skip_empty: bool = True,
|
|
181
|
+
tables: Optional[Sequence[str]] = None,
|
|
182
|
+
) -> List[str]:
|
|
183
|
+
"""Write one CSV file per table of ``network`` into ``output_dir``.
|
|
184
|
+
|
|
185
|
+
When ``tables`` is given (e.g. ``["NODE", "LINK"]``), only those tables
|
|
186
|
+
are exported; by default every table is exported.
|
|
187
|
+
"""
|
|
188
|
+
out_dir = Path(output_dir)
|
|
189
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
190
|
+
|
|
191
|
+
written: List[str] = []
|
|
192
|
+
for table in _resolve_tables(network, tables):
|
|
193
|
+
if skip_empty and len(table.df) == 0:
|
|
194
|
+
continue
|
|
195
|
+
path = out_dir / f"{table.name}.csv"
|
|
196
|
+
export_table_csv(table.df, path, sep=sep, encoding=encoding)
|
|
197
|
+
written.append(str(path))
|
|
198
|
+
return written
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def export_json(
|
|
202
|
+
network: "Network",
|
|
203
|
+
output_dir: str | os.PathLike[str],
|
|
204
|
+
encoding: str = "utf-8",
|
|
205
|
+
skip_empty: bool = True,
|
|
206
|
+
tables: Optional[Sequence[str]] = None,
|
|
207
|
+
) -> List[str]:
|
|
208
|
+
"""Write one JSON file (array of records) per table into ``output_dir``.
|
|
209
|
+
|
|
210
|
+
When ``tables`` is given, only those tables are exported; by default
|
|
211
|
+
every table is exported.
|
|
212
|
+
"""
|
|
213
|
+
out_dir = Path(output_dir)
|
|
214
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
215
|
+
|
|
216
|
+
written: List[str] = []
|
|
217
|
+
for table in _resolve_tables(network, tables):
|
|
218
|
+
if skip_empty and len(table.df) == 0:
|
|
219
|
+
continue
|
|
220
|
+
path = out_dir / f"{table.name}.json"
|
|
221
|
+
export_table_json(table.df, path, encoding=encoding)
|
|
222
|
+
written.append(str(path))
|
|
223
|
+
return written
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def export_geojson(
|
|
227
|
+
network: "Network",
|
|
228
|
+
output_dir: str | os.PathLike[str],
|
|
229
|
+
encoding: str = "utf-8",
|
|
230
|
+
skip_empty: bool = True,
|
|
231
|
+
tables: Optional[Sequence[str]] = None,
|
|
232
|
+
) -> List[str]:
|
|
233
|
+
"""Write a GeoJSON file for every requested table that has geometry.
|
|
234
|
+
|
|
235
|
+
When ``tables`` is given, only those tables are considered; by default
|
|
236
|
+
every table is considered. Tables without any geometry (no WKT column,
|
|
237
|
+
no ``XCOORD``/``YCOORD``) are skipped.
|
|
238
|
+
"""
|
|
239
|
+
out_dir = Path(output_dir)
|
|
240
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
241
|
+
|
|
242
|
+
written: List[str] = []
|
|
243
|
+
for table in _resolve_tables(network, tables):
|
|
244
|
+
if skip_empty and len(table.df) == 0:
|
|
245
|
+
continue
|
|
246
|
+
path = out_dir / f"{table.name}.geojson"
|
|
247
|
+
if export_table_geojson(table.df, path, encoding=encoding):
|
|
248
|
+
written.append(str(path))
|
|
249
|
+
return written
|