ncftools 0.6.0__tar.gz → 0.6.2__tar.gz
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.
- {ncftools-0.6.0 → ncftools-0.6.2}/PKG-INFO +5 -2
- {ncftools-0.6.0 → ncftools-0.6.2}/README.md +3 -0
- {ncftools-0.6.0 → ncftools-0.6.2}/ncftools/__init__.py +1 -1
- {ncftools-0.6.0 → ncftools-0.6.2}/ncftools/nc2shp.py +45 -46
- {ncftools-0.6.0 → ncftools-0.6.2}/ncftools/transzone1.py +9 -1
- {ncftools-0.6.0 → ncftools-0.6.2}/ncftools.egg-info/PKG-INFO +5 -2
- {ncftools-0.6.0 → ncftools-0.6.2}/ncftools.egg-info/requires.txt +1 -1
- {ncftools-0.6.0 → ncftools-0.6.2}/pyproject.toml +2 -2
- {ncftools-0.6.0 → ncftools-0.6.2}/MANIFEST.in +0 -0
- {ncftools-0.6.0 → ncftools-0.6.2}/ncftools/cli.py +0 -0
- {ncftools-0.6.0 → ncftools-0.6.2}/ncftools/describe.py +0 -0
- {ncftools-0.6.0 → ncftools-0.6.2}/ncftools/meshinfo.py +0 -0
- {ncftools-0.6.0 → ncftools-0.6.2}/ncftools/tests/__init__.py +0 -0
- {ncftools-0.6.0 → ncftools-0.6.2}/ncftools/transzone2.py +0 -0
- {ncftools-0.6.0 → ncftools-0.6.2}/ncftools.egg-info/SOURCES.txt +0 -0
- {ncftools-0.6.0 → ncftools-0.6.2}/ncftools.egg-info/dependency_links.txt +0 -0
- {ncftools-0.6.0 → ncftools-0.6.2}/ncftools.egg-info/entry_points.txt +0 -0
- {ncftools-0.6.0 → ncftools-0.6.2}/ncftools.egg-info/top_level.txt +0 -0
- {ncftools-0.6.0 → ncftools-0.6.2}/requirements.txt +0 -0
- {ncftools-0.6.0 → ncftools-0.6.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ncftools
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: A collection of tools for working with NetCDF files
|
|
5
5
|
Author-email: aaronchh <aaronhsu219@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -12,7 +12,7 @@ Description-Content-Type: text/markdown
|
|
|
12
12
|
Requires-Dist: numpy>=1.20.0
|
|
13
13
|
Requires-Dist: netCDF4>=1.6.0
|
|
14
14
|
Requires-Dist: geopandas>=0.12.0
|
|
15
|
-
Requires-Dist: shapely>=
|
|
15
|
+
Requires-Dist: shapely>=2.0.0
|
|
16
16
|
|
|
17
17
|
# NCFTOOLS
|
|
18
18
|
|
|
@@ -69,6 +69,9 @@ nc2shp -i mesh.nc -q
|
|
|
69
69
|
Buffer triangle mesh faces by +1 m to form a transition zone, then select all faces that intersect it.
|
|
70
70
|
Outputs `trans_zone.shp` and `trans_zone_extend.shp`.
|
|
71
71
|
|
|
72
|
+
Triangles are identified from the `type` attribute column when present; otherwise they are
|
|
73
|
+
detected automatically by vertex count (closed rings with 3 unique vertices).
|
|
74
|
+
|
|
72
75
|
```bash
|
|
73
76
|
transzone1 -i SHP_NC/FlowFM_net_faces.shp
|
|
74
77
|
transzone1 -i SHP_NC/FlowFM_net_faces.shp -o SHP_TRANS
|
|
@@ -53,6 +53,9 @@ nc2shp -i mesh.nc -q
|
|
|
53
53
|
Buffer triangle mesh faces by +1 m to form a transition zone, then select all faces that intersect it.
|
|
54
54
|
Outputs `trans_zone.shp` and `trans_zone_extend.shp`.
|
|
55
55
|
|
|
56
|
+
Triangles are identified from the `type` attribute column when present; otherwise they are
|
|
57
|
+
detected automatically by vertex count (closed rings with 3 unique vertices).
|
|
58
|
+
|
|
56
59
|
```bash
|
|
57
60
|
transzone1 -i SHP_NC/FlowFM_net_faces.shp
|
|
58
61
|
transzone1 -i SHP_NC/FlowFM_net_faces.shp -o SHP_TRANS
|
|
@@ -15,8 +15,7 @@ import sys
|
|
|
15
15
|
import netCDF4 as nc
|
|
16
16
|
import numpy as np
|
|
17
17
|
import geopandas as gpd
|
|
18
|
-
|
|
19
|
-
from shapely.ops import unary_union
|
|
18
|
+
import shapely
|
|
20
19
|
|
|
21
20
|
|
|
22
21
|
def read_mesh_netcdf(file_path, quiet=False):
|
|
@@ -71,65 +70,65 @@ def create_polygons(face_x, face_y, quiet=False):
|
|
|
71
70
|
"""
|
|
72
71
|
Build Shapely Polygon objects from mesh face coordinate arrays.
|
|
73
72
|
|
|
73
|
+
Vectorized over all faces at once: the per-node validity mask, ring
|
|
74
|
+
coordinates, and polygon construction/repair are computed with numpy
|
|
75
|
+
and shapely's array API rather than a per-face Python loop.
|
|
76
|
+
|
|
74
77
|
Returns:
|
|
75
78
|
list[Polygon]
|
|
76
79
|
"""
|
|
77
80
|
_print("Creating polygons from mesh faces...", quiet)
|
|
78
81
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
except Exception as e:
|
|
114
|
-
n_invalid += 1
|
|
115
|
-
if not quiet:
|
|
116
|
-
print(f" Warning: face {i} skipped: {e}")
|
|
82
|
+
x_data = np.ma.getdata(face_x).astype(np.float64)
|
|
83
|
+
y_data = np.ma.getdata(face_y).astype(np.float64)
|
|
84
|
+
valid = (
|
|
85
|
+
~np.ma.getmaskarray(face_x)
|
|
86
|
+
& ~np.ma.getmaskarray(face_y)
|
|
87
|
+
& (np.abs(x_data) < 1e30)
|
|
88
|
+
& (np.abs(y_data) < 1e30)
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
n_valid_per_face = valid.sum(axis=1)
|
|
92
|
+
keep_face = n_valid_per_face >= 3
|
|
93
|
+
|
|
94
|
+
row_idx, col_idx = np.nonzero(valid & keep_face[:, None])
|
|
95
|
+
coords = np.column_stack((x_data[row_idx, col_idx], y_data[row_idx, col_idx]))
|
|
96
|
+
|
|
97
|
+
kept_face_ids = np.nonzero(keep_face)[0]
|
|
98
|
+
ring_id = np.full(len(keep_face), -1, dtype=np.int64)
|
|
99
|
+
ring_id[kept_face_ids] = np.arange(kept_face_ids.size)
|
|
100
|
+
ring_indices = ring_id[row_idx]
|
|
101
|
+
|
|
102
|
+
rings = shapely.linearrings(coords, indices=ring_indices)
|
|
103
|
+
polys = shapely.polygons(rings)
|
|
104
|
+
|
|
105
|
+
bad = ~shapely.is_valid(polys) | shapely.is_empty(polys)
|
|
106
|
+
if bad.any():
|
|
107
|
+
polys[bad] = shapely.buffer(polys[bad], 0)
|
|
108
|
+
bad = ~shapely.is_valid(polys) | shapely.is_empty(polys)
|
|
109
|
+
|
|
110
|
+
n_invalid = int(len(face_x) - keep_face.sum()) + int(bad.sum())
|
|
111
|
+
polys = polys[~bad]
|
|
112
|
+
node_counts = n_valid_per_face[kept_face_ids][~bad]
|
|
113
|
+
|
|
114
|
+
tri = int((node_counts == 3).sum())
|
|
115
|
+
quad = int((node_counts == 4).sum())
|
|
117
116
|
|
|
118
117
|
if not quiet:
|
|
119
|
-
print(f" Valid polygons: {len(
|
|
118
|
+
print(f" Valid polygons: {len(polys)} "
|
|
120
119
|
f"(triangles: {tri}, quads: {quad}, skipped: {n_invalid})")
|
|
121
120
|
|
|
122
|
-
return
|
|
121
|
+
return list(polys)
|
|
123
122
|
|
|
124
123
|
|
|
125
124
|
def create_geodataframe(polygons, crs="EPSG:3826"):
|
|
126
125
|
"""Build a GeoDataFrame from a list of Shapely polygons."""
|
|
127
126
|
data = {
|
|
128
127
|
'face_id': range(len(polygons)),
|
|
129
|
-
'area':
|
|
128
|
+
'area': shapely.area(polygons),
|
|
130
129
|
'type': [
|
|
131
|
-
'triangle' if len(
|
|
132
|
-
else 'quadrilateral' if len(
|
|
130
|
+
'triangle' if len(p.exterior.coords) == 4
|
|
131
|
+
else 'quadrilateral' if len(p.exterior.coords) == 5
|
|
133
132
|
else 'other'
|
|
134
133
|
for p in polygons
|
|
135
134
|
],
|
|
@@ -140,7 +139,7 @@ def create_geodataframe(polygons, crs="EPSG:3826"):
|
|
|
140
139
|
def dissolve_geodataframe(gdf, quiet=False):
|
|
141
140
|
"""Dissolve all polygons into a single geometry."""
|
|
142
141
|
_print("Dissolving polygons...", quiet)
|
|
143
|
-
geom =
|
|
142
|
+
geom = shapely.union_all(gdf.geometry.values)
|
|
144
143
|
dissolved = gpd.GeoDataFrame(
|
|
145
144
|
{'id': [1], 'total_area': [geom.area], 'count': [len(gdf)]},
|
|
146
145
|
geometry=[geom],
|
|
@@ -31,7 +31,15 @@ def build_transition_zone(faces_shp, output_dir="SHP_TRANS", quiet=False):
|
|
|
31
31
|
|
|
32
32
|
gdf = gpd.read_file(faces_shp)
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
if "type" in gdf.columns:
|
|
35
|
+
triangles = gdf[gdf["type"] == "triangle"].copy()
|
|
36
|
+
else:
|
|
37
|
+
# Detect triangles by vertex count (closed ring has 4 coords for 3 unique vertices)
|
|
38
|
+
is_triangle = gdf.geometry.apply(
|
|
39
|
+
lambda g: len(g.exterior.coords) == 4
|
|
40
|
+
)
|
|
41
|
+
triangles = gdf[is_triangle].copy()
|
|
42
|
+
|
|
35
43
|
if triangles.empty:
|
|
36
44
|
raise RuntimeError("No triangle faces found in attribute table.")
|
|
37
45
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ncftools
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: A collection of tools for working with NetCDF files
|
|
5
5
|
Author-email: aaronchh <aaronhsu219@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -12,7 +12,7 @@ Description-Content-Type: text/markdown
|
|
|
12
12
|
Requires-Dist: numpy>=1.20.0
|
|
13
13
|
Requires-Dist: netCDF4>=1.6.0
|
|
14
14
|
Requires-Dist: geopandas>=0.12.0
|
|
15
|
-
Requires-Dist: shapely>=
|
|
15
|
+
Requires-Dist: shapely>=2.0.0
|
|
16
16
|
|
|
17
17
|
# NCFTOOLS
|
|
18
18
|
|
|
@@ -69,6 +69,9 @@ nc2shp -i mesh.nc -q
|
|
|
69
69
|
Buffer triangle mesh faces by +1 m to form a transition zone, then select all faces that intersect it.
|
|
70
70
|
Outputs `trans_zone.shp` and `trans_zone_extend.shp`.
|
|
71
71
|
|
|
72
|
+
Triangles are identified from the `type` attribute column when present; otherwise they are
|
|
73
|
+
detected automatically by vertex count (closed rings with 3 unique vertices).
|
|
74
|
+
|
|
72
75
|
```bash
|
|
73
76
|
transzone1 -i SHP_NC/FlowFM_net_faces.shp
|
|
74
77
|
transzone1 -i SHP_NC/FlowFM_net_faces.shp -o SHP_TRANS
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ncftools"
|
|
7
|
-
version = "0.6.
|
|
7
|
+
version = "0.6.2"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name = "aaronchh", email = "aaronhsu219@gmail.com" },
|
|
10
10
|
]
|
|
@@ -16,7 +16,7 @@ dependencies = [
|
|
|
16
16
|
"numpy>=1.20.0",
|
|
17
17
|
"netCDF4>=1.6.0",
|
|
18
18
|
"geopandas>=0.12.0",
|
|
19
|
-
"shapely>=
|
|
19
|
+
"shapely>=2.0.0",
|
|
20
20
|
]
|
|
21
21
|
classifiers = [
|
|
22
22
|
"Programming Language :: Python :: 3",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|