@@ -8,7 +8,8 @@ def __init__(self, dataframe, demand_col=None, demand_name=None, supply_name=Non
8
8
"""
9
9
An object that stores the relationship between a set of demand locations and a set of supply locations.
10
10
Use this initializer if the coverage matrix has already been created, otherwise this can be created from two
11
- geodataframes using the :meth:`~allagash.coverage.Coverage.from_geodataframes` factory method.
11
+ geodataframes using the :meth:`~allagash.coverage.Coverage.from_geodataframes` or
12
+ :meth:`~allagash.coverage.Coverage.from_spatially_enabled_dataframes` factory methods.
12
13
13
14
.. code-block:: python
14
15
@@ -115,7 +116,9 @@ def from_geodataframes(cls, demand_df, supply_df, demand_id_col, supply_id_col,
115
116
locations. Required if generating partial coverage.
116
117
:param str coverage_type: (optional) The type of coverage this represents. If not supplied, the default is
117
118
"binary". Options are "binary" and "partial".
118
- :return:
119
+
120
+ :return: The coverage
121
+ :rtype: ~allagash.coverage.Coverage
119
122
"""
120
123
cls ._validate_from_geodataframes (coverage_type , demand_col , demand_df , demand_id_col , demand_name , supply_df ,
121
124
supply_id_col )
@@ -147,6 +150,61 @@ def from_geodataframes(cls, demand_df, supply_df, demand_id_col, supply_id_col,
147
150
supply_name = supply_name ,
148
151
coverage_type = coverage_type )
149
152
153
+ @classmethod
154
+ def from_spatially_enabled_dataframes (cls , demand_df , supply_df , demand_id_col , supply_id_col , demand_name = None ,
155
+ supply_name = None , demand_col = None , coverage_type = "binary" ,
156
+ demand_geometry_col = 'SHAPE' , supply_geometry_col = 'SHAPE' ):
157
+ """
158
+ Creates a new Coverage from two spatially enabled (arcgis) dataframes representing the demand and supply locations.
159
+ The coverage is determined by intersecting the two dataframes.
160
+
161
+ :param ~pandas.DataFrame demand_df: The spatially enabled dataframe containing the demand locations
162
+ :param ~pandas.DataFrame supply_df: The spatially enavled dataframe containing the supply locations
163
+ :param str demand_id_col: The name of the column that has unique identifiers for the demand locations
164
+ :param str supply_id_col: The name of the column that has unique identifiers for the supply locations
165
+ :param str demand_name: (optional) The name of the demand to use. If not supplied, a random name is generated.
166
+ :param str supply_name: (optional) The name of the supply to use. If not supplied, a random name is generated.
167
+ :param str demand_col: (optional) The name of the column that stores the amount of demand for the demand
168
+ locations. Required if generating partial coverage.
169
+ :param str coverage_type: (optional) The type of coverage this represents. If not supplied, the default is
170
+ "binary". Options are "binary" and "partial".
171
+ :param str demand_geometry_col: (optional) The name of the field storing the geometry in the demand dataframe.
172
+ If not supplied, the default is "SHAPE".
173
+ :param str supply_geometry_col: (optional) The name of the field storing the geometry in the supply dataframe.
174
+ If not supplied, the default is "SHAPE".
175
+ :return: The coverage
176
+ :rtype: ~allagash.coverage.Coverage
177
+ """
178
+
179
+ cls ._validate_from_spatially_enabled_dataframes (coverage_type , demand_col , demand_df , demand_id_col , demand_name , supply_df ,
180
+ supply_id_col , demand_geometry_col , supply_geometry_col )
181
+ data = []
182
+ if coverage_type .lower () == 'binary' :
183
+ for index , row in demand_df .iterrows ():
184
+ contains = supply_df [supply_geometry_col ].geom .contains (row [demand_geometry_col ]).tolist ()
185
+ if demand_col :
186
+ contains .insert (0 , row [demand_col ])
187
+ data .append (contains )
188
+ elif coverage_type .lower () == 'partial' :
189
+ for index , row in demand_df .iterrows ():
190
+ demand_area = row [demand_geometry_col ].area
191
+ intersection_area = supply_df [supply_geometry_col ].geom .intersect (row [demand_geometry_col ]).geom .area
192
+ partial_coverage = ((intersection_area / demand_area ) * row [demand_col ]).tolist ()
193
+ if demand_col :
194
+ partial_coverage .insert (0 , row [demand_col ])
195
+ data .append (partial_coverage )
196
+ else :
197
+ raise ValueError (f"Invalid coverage type '{ coverage_type } '" )
198
+ columns = supply_df [supply_id_col ].tolist ()
199
+ if demand_col :
200
+ columns .insert (0 , demand_col )
201
+ df = pd .DataFrame .from_records (data , index = demand_df [demand_id_col ], columns = columns )
202
+ return Coverage (df ,
203
+ demand_col = demand_col ,
204
+ demand_name = demand_name ,
205
+ supply_name = supply_name ,
206
+ coverage_type = coverage_type )
207
+
150
208
@classmethod
151
209
def _validate_from_geodataframes (cls , coverage_type , demand_col , demand_df , demand_id_col , demand_name , supply_df ,
152
210
supply_id_col ):
@@ -172,3 +230,33 @@ def _validate_from_geodataframes(cls, coverage_type, demand_col, demand_df, dema
172
230
raise ValueError (f"Invalid coverage type '{ coverage_type } '" )
173
231
if coverage_type .lower () == "partial" and demand_col is None :
174
232
raise ValueError (f"demand_col is required when generating partial coverage" )
233
+
234
+ @classmethod
235
+ def _validate_from_spatially_enabled_dataframes (cls , coverage_type , demand_col , demand_df , demand_id_col , demand_name , supply_df ,
236
+ supply_id_col , demand_geometry_col , supply_geometry_col ):
237
+ if not isinstance (demand_df , pd .DataFrame ):
238
+ raise TypeError (f"Expected 'Dataframe' type for demand_df, got '{ type (demand_df )} '" )
239
+ if not isinstance (supply_df , pd .DataFrame ):
240
+ raise TypeError (f"Expected 'Dataframe' type for supply_df, got '{ type (supply_df )} '" )
241
+ if not isinstance (demand_id_col , str ):
242
+ raise TypeError (f"Expected 'str' type for demand_id_col, got '{ type (demand_id_col )} '" )
243
+ if not isinstance (supply_id_col , str ):
244
+ raise TypeError (f"Expected 'str' type for demand_id_col, got '{ type (supply_id_col )} '" )
245
+ if not isinstance (demand_name , str ) and demand_name is not None :
246
+ raise TypeError (f"Expected 'str' type for demand_name, got '{ type (demand_name )} '" )
247
+ if not isinstance (coverage_type , str ):
248
+ raise TypeError (f"Expected 'str' type for coverage_type, got '{ type (coverage_type )} '" )
249
+ if demand_col and demand_col not in demand_df .columns :
250
+ raise ValueError (f"'{ demand_col } ' not in dataframe" )
251
+ if demand_id_col and demand_id_col not in demand_df .columns :
252
+ raise ValueError (f"'{ demand_id_col } ' not in dataframe" )
253
+ if supply_id_col and supply_id_col not in supply_df .columns :
254
+ raise ValueError (f"'{ supply_id_col } ' not in dataframe" )
255
+ if demand_geometry_col and demand_geometry_col not in demand_df .columns :
256
+ raise ValueError (f"'{ demand_geometry_col } ' not in dataframe" )
257
+ if supply_geometry_col and supply_geometry_col not in supply_df .columns :
258
+ raise ValueError (f"'{ supply_geometry_col } ' not in dataframe" )
259
+ if coverage_type .lower () not in ("binary" , "partial" ):
260
+ raise ValueError (f"Invalid coverage type '{ coverage_type } '" )
261
+ if coverage_type .lower () == "partial" and demand_col is None :
262
+ raise ValueError (f"demand_col is required when generating partial coverage" )
0 commit comments