|
10 | 10 | import pathlib |
11 | 11 | import warnings |
12 | 12 | from collections import defaultdict |
13 | | -from collections.abc import Hashable, Iterable, Mapping, Sequence |
| 13 | +from collections.abc import Hashable, Iterable, Sequence |
14 | 14 | from contextlib import suppress |
15 | 15 | from dataclasses import dataclass |
16 | 16 | from functools import cached_property |
@@ -514,7 +514,8 @@ def _to_index_array( |
514 | 514 | else: |
515 | 515 | # If the value is still an integer then it had no fill value. |
516 | 516 | # Convert it to a mask array with no masked values. |
517 | | - values = numpy.ma.masked_array(values, mask=numpy.ma.nomask) |
| 517 | + mask = numpy.full_like(values, fill_value=numpy.ma.nomask) |
| 518 | + values = numpy.ma.masked_array(values, mask=mask) |
518 | 519 |
|
519 | 520 | # UGRID conventions allow for zero based or one based indexing. |
520 | 521 | # To be consistent we convert all indexes to zero based. |
@@ -1078,24 +1079,31 @@ def grid_kinds(self) -> frozenset[UGridKind]: |
1078 | 1079 |
|
1079 | 1080 | def _make_polygons(self) -> numpy.ndarray: |
1080 | 1081 | """Generate list of Polygons""" |
1081 | | - # X,Y coords of each node |
1082 | 1082 | topology = self.topology |
| 1083 | + # X,Y coords of each node |
1083 | 1084 | node_x = topology.node_x.values |
1084 | 1085 | node_y = topology.node_y.values |
| 1086 | + # The nodes that each face is constructed from |
1085 | 1087 | face_node = topology.face_node_array |
| 1088 | + |
| 1089 | + # Preallocate an array to put the polygons in |
1086 | 1090 | polygons = numpy.full(topology.face_count, None, dtype=numpy.object_) |
1087 | 1091 |
|
1088 | 1092 | # `shapely.polygons` will make polygons with the same number of vertices. |
1089 | 1093 | # UGRID polygons have arbitrary numbers of vertices. |
1090 | 1094 | # Group polygons by how many vertices they have, then make them in bulk. |
1091 | | - polygons_of_size: Mapping[int, dict[int, numpy.ndarray]] = defaultdict(dict) |
1092 | | - for index, row in enumerate(face_node): |
1093 | | - vertices = row.compressed() |
1094 | | - polygons_of_size[vertices.size][index] = numpy.c_[node_x[vertices], node_y[vertices]] |
1095 | | - |
1096 | | - for size, size_polygons in polygons_of_size.items(): |
1097 | | - coords = numpy.stack(list(size_polygons.values())) |
1098 | | - shapely.polygons(coords, indices=list(size_polygons.keys()), out=polygons) |
| 1095 | + # Polygon sizes can be derived by how many nodes are masked/not masked. |
| 1096 | + polygon_sizes = numpy.sum(~numpy.ma.getmaskarray(face_node), axis=1) |
| 1097 | + unique_sizes = numpy.unique(polygon_sizes) |
| 1098 | + |
| 1099 | + # Make polygons in batches |
| 1100 | + for unique_size in unique_sizes: |
| 1101 | + # Extract the face node data for every polygon of this size |
| 1102 | + indices = numpy.flatnonzero(polygon_sizes == unique_size) |
| 1103 | + nodes = numpy.ma.getdata(face_node)[indices, :unique_size] |
| 1104 | + coords = numpy.stack([node_x[nodes], node_y[nodes]], axis=-1) |
| 1105 | + # Generate the polygons directly in to their correct locations |
| 1106 | + shapely.polygons(coords, indices=indices, out=polygons) |
1099 | 1107 |
|
1100 | 1108 | return polygons |
1101 | 1109 |
|
|
0 commit comments