1
1
from __future__ import annotations
2
2
3
- import contextlib
4
3
from collections .abc import Sequence
5
4
from copy import copy
6
5
from dataclasses import dataclass
7
6
from functools import partial
8
- from itertools import chain
9
7
from typing import Any , Callable
10
8
11
9
import geopandas as gpd
@@ -123,34 +121,22 @@ def _get_collection_shape(
123
121
outline_alpha : None | float = None ,
124
122
** kwargs : Any ,
125
123
) -> PatchCollection :
126
- print (shapes )
127
- patches = []
128
- # remove empty points/polygons
129
- shapes = shapes [shapes ["geometry" ].apply (lambda geom : not geom .is_empty )]
130
-
131
- polygon_df = shapes [
132
- shapes ["geometry" ].apply (lambda geom : geom .geom_type == "Polygon" ) # type: ignore[call-overload]
133
- ]
134
- multipolygon_df = shapes [
135
- shapes ["geometry" ].apply (lambda geom : geom .geom_type == "MultiPolygon" ) # type: ignore[call-overload]
136
- ]
137
- circle_df = shapes [
138
- shapes ["geometry" ].apply (lambda geom : geom .geom_type == "Point" ) # type: ignore[call-overload]
139
- ]
140
-
141
- if len (polygon_df ) > 0 :
142
- patches += [Polygon (p .exterior .coords , closed = True ) for p in polygon_df ["geometry" ]]
143
- if len (circle_df ) > 0 :
144
- patches += [
145
- Circle ((circ .x , circ .y ), radius = r * s ) for circ , r in zip (circle_df ["geometry" ], circle_df ["radius" ])
146
- ]
147
- if len (multipolygon_df ) > 0 :
148
- patches += [_make_patch_from_multipolygon (mp ) for mp in multipolygon_df ["geometry" ]]
149
-
150
- # flatten list since multipolygons cause a nested list
151
- with contextlib .suppress (Exception ):
152
- patches = list (chain .from_iterable ([x ] if not isinstance (x , list ) else x for x in patches ))
153
-
124
+ """
125
+ Get a PatchCollection for rendering given geometries with specified colors and outlines.
126
+
127
+ Args:
128
+ - shapes (list[GeoDataFrame]): List of geometrical shapes.
129
+ - c: Color parameter.
130
+ - s (float): Size of the shape.
131
+ - norm: Normalization for the color map.
132
+ - fill_alpha (float, optional): Opacity for the fill color.
133
+ - outline_alpha (float, optional): Opacity for the outline.
134
+ - **kwargs: Additional keyword arguments.
135
+
136
+ Returns
137
+ -------
138
+ - PatchCollection: Collection of patches for rendering.
139
+ """
154
140
cmap = kwargs ["cmap" ]
155
141
156
142
try :
@@ -169,16 +155,60 @@ def _get_collection_shape(
169
155
if render_params .outline_params .outline :
170
156
outline_c = ColorConverter ().to_rgba_array (render_params .outline_params .outline_color )
171
157
outline_c [..., - 1 ] = render_params .outline_alpha
158
+ outline_c = outline_c .tolist ()
172
159
else :
173
- outline_c = None
160
+ outline_c = [None ]
161
+ outline_c = outline_c * fill_c .shape [0 ]
162
+
163
+ shapes_df = pd .DataFrame (shapes , copy = True )
164
+
165
+ # remove empty points/polygons
166
+ shapes_df = shapes_df [shapes_df ["geometry" ].apply (lambda geom : not geom .is_empty )]
167
+
168
+ rows = []
169
+
170
+ def assign_fill_and_outline_to_row (
171
+ shapes : list [GeoDataFrame ], fill_c : list [Any ], outline_c : list [Any ], row : pd .Series , idx : int
172
+ ) -> None :
173
+ if len (shapes ) > 1 and len (fill_c ) == 1 :
174
+ row ["fill_c" ] = fill_c
175
+ row ["outline_c" ] = outline_c
176
+ else :
177
+ row ["fill_c" ] = fill_c [idx ]
178
+ row ["outline_c" ] = outline_c [idx ]
179
+
180
+ # Match colors to the geometry, potentially expanding the row in case of
181
+ # multipolygons
182
+ for idx , row in shapes_df .iterrows ():
183
+ geom = row ["geometry" ]
184
+ if geom .geom_type == "Polygon" :
185
+ row = row .to_dict ()
186
+ row ["geometry" ] = Polygon (geom .exterior .coords , closed = True )
187
+ assign_fill_and_outline_to_row (shapes , fill_c , outline_c , row , idx )
188
+ rows .append (row )
189
+
190
+ elif geom .geom_type == "MultiPolygon" :
191
+ mp = _make_patch_from_multipolygon (geom )
192
+ for _ , m in enumerate (mp ):
193
+ mp_copy = row .to_dict ()
194
+ mp_copy ["geometry" ] = m
195
+ assign_fill_and_outline_to_row (shapes , fill_c , outline_c , mp_copy , idx )
196
+ rows .append (mp_copy )
197
+
198
+ elif geom .geom_type == "Point" :
199
+ row = row .to_dict ()
200
+ row ["geometry" ] = Circle ((geom .x , geom .y ), radius = row ["radius" ])
201
+ assign_fill_and_outline_to_row (shapes , fill_c , outline_c , row , idx )
202
+ rows .append (row )
203
+
204
+ patches = pd .DataFrame (rows )
174
205
175
206
return PatchCollection (
176
- patches ,
207
+ patches [ "geometry" ]. values . tolist () ,
177
208
snap = False ,
178
- # zorder=4,
179
209
lw = render_params .outline_params .linewidth ,
180
- facecolor = fill_c ,
181
- edgecolor = outline_c ,
210
+ facecolor = patches [ " fill_c" ] ,
211
+ edgecolor = None if all ( outline is None for outline in outline_c ) else outline_c ,
182
212
** kwargs ,
183
213
)
184
214
0 commit comments