huff 1.5.6__py3-none-any.whl → 1.5.8__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.
huff/gistools.py CHANGED
@@ -4,8 +4,8 @@
4
4
  # Author: Thomas Wieland
5
5
  # ORCID: 0000-0001-5168-9846
6
6
  # mail: geowieland@googlemail.com
7
- # Version: 1.4.2
8
- # Last update: 2025-07-31 18:31
7
+ # Version: 1.4.3
8
+ # Last update: 2025-08-07 17:20
9
9
  # Copyright (c) 2025 Thomas Wieland
10
10
  #-----------------------------------------------------------------------
11
11
 
@@ -83,7 +83,7 @@ def distance_matrix(
83
83
  matrix.append(row)
84
84
 
85
85
  if lines_gdf:
86
- return line_data
86
+ return gp.GeoDataFrame(line_data)
87
87
  else:
88
88
  return matrix
89
89
 
@@ -95,9 +95,15 @@ def buffers(
95
95
  donut: bool = True,
96
96
  save_output: bool = True,
97
97
  output_filepath: str = "buffers.shp",
98
- output_crs: str = "EPSG:4326"
98
+ output_crs: str = "EPSG:4326"
99
99
  ):
100
+
101
+ if point_gdf.crs.is_geographic:
102
+ print(f"WARNING: Point GeoDataFrame has geographic coordinate system {point_gdf.crs}. Results may be invalid.")
100
103
 
104
+ if unique_id_col not in point_gdf.columns:
105
+ raise KeyError(f"No column {unique_id_col} in input GeoDataFrame")
106
+
101
107
  all_buffers_gdf = gp.GeoDataFrame(
102
108
  columns=[
103
109
  unique_id_col,
@@ -146,15 +152,72 @@ def buffers(
146
152
 
147
153
  all_buffers_gdf = all_buffers_gdf.to_crs(output_crs)
148
154
 
149
- if save_output:
150
-
151
- all_buffers_gdf.to_file(output_filepath)
152
-
155
+ if save_output:
156
+ all_buffers_gdf.to_file(output_filepath)
153
157
  print ("Saved as", output_filepath)
154
158
 
155
159
  return all_buffers_gdf
156
160
 
157
161
 
162
+ def polygon_select(
163
+ gdf: gp.GeoDataFrame,
164
+ gdf_unique_id_col: str,
165
+ gdf_polygon_select: gp.GeoDataFrame,
166
+ gdf_polygon_select_unique_id_col: str,
167
+ distance: int,
168
+ within: bool = False,
169
+ save_output: bool = True,
170
+ output_filepath: str = "polygon_select.shp",
171
+ output_crs: str = "EPSG:4326"
172
+ ):
173
+
174
+ if gdf.crs != gdf_polygon_select.crs:
175
+ raise ValueError(f"Coordinate reference systems of inputs do not match. Polygons: {str(gdf.crs)}, points: {str(gdf_polygon_select.crs)}")
176
+
177
+ if gdf_unique_id_col not in gdf.columns:
178
+ raise KeyError(f"No column {gdf_unique_id_col} in input GeoDataFrame")
179
+
180
+ if gdf_polygon_select_unique_id_col not in gdf_polygon_select.columns:
181
+ raise KeyError(f"No column {gdf_polygon_select_unique_id_col} in input GeoDataFrame for selection")
182
+
183
+ if gdf.crs.is_geographic:
184
+ print(f"WARNING: Input GeoDataFrames have geographic coordinate system {gdf.crs}. Results may be invalid.")
185
+
186
+ if len(gdf) > 1:
187
+ print(f"WARNING: Input GeoDataFrame 'gdf' includes > 1 objects. Using the first only.")
188
+ gdf = gdf[0]
189
+
190
+ gdf_buffer = buffers(
191
+ point_gdf = gdf,
192
+ unique_id_col = gdf_unique_id_col,
193
+ distances = [distance],
194
+ save_output = True,
195
+ output_filepath = "gdf_buffer.shp",
196
+ output_crs = output_crs
197
+ )
198
+
199
+ gdf_buffer = gdf_buffer.geometry.union_all()
200
+
201
+ gdf_polygon_select = gdf_polygon_select.to_crs(output_crs)
202
+
203
+ gdf_select_intersects = gdf_polygon_select[
204
+ gdf_polygon_select.geometry.intersects(gdf_buffer)
205
+ ]
206
+
207
+ if within:
208
+ gdf_select_intersects = gdf_select_intersects[gdf_select_intersects.geometry.within(gdf_buffer)]
209
+
210
+ gdf_select_intersects_unique_ids = gdf_select_intersects[gdf_polygon_select_unique_id_col].unique()
211
+
212
+ gdf_polygon_select_selection = gdf_polygon_select[gdf_polygon_select[gdf_polygon_select_unique_id_col].isin(gdf_select_intersects_unique_ids)]
213
+
214
+ if save_output:
215
+ gdf_polygon_select_selection.to_file(output_filepath)
216
+ print ("Saved as", output_filepath)
217
+
218
+ return gdf_polygon_select_selection
219
+
220
+
158
221
  def overlay_difference(
159
222
  polygon_gdf: gp.GeoDataFrame,
160
223
  sort_col: str = None,
@@ -254,6 +317,7 @@ def point_spatial_join(
254
317
  spatial_join_stat
255
318
  ]
256
319
 
320
+
257
321
  def map_with_basemap(
258
322
  layers: list,
259
323
  osm_basemap: bool = True,
huff/models.py CHANGED
@@ -4,8 +4,8 @@
4
4
  # Author: Thomas Wieland
5
5
  # ORCID: 0000-0001-5168-9846
6
6
  # mail: geowieland@googlemail.com
7
- # Version: 1.5.5
8
- # Last update: 2025-07-26 13:42
7
+ # Version: 1.5.6
8
+ # Last update: 2025-08-01 14:00
9
9
  # Copyright (c) 2025 Thomas Wieland
10
10
  #-----------------------------------------------------------------------
11
11
 
@@ -590,6 +590,12 @@ class InteractionMatrix:
590
590
  if interaction_matrix_metadata["fit"]["function"] == "huff_ml_fit":
591
591
  print("Fit method " + interaction_matrix_metadata["fit"]["method"] + " (Converged: " + str(interaction_matrix_metadata["fit"]["minimize_success"]) + ")")
592
592
 
593
+ return [
594
+ customer_origins_metadata,
595
+ supply_locations_metadata,
596
+ interaction_matrix_metadata
597
+ ]
598
+
593
599
  def transport_costs(
594
600
  self,
595
601
  network: bool = True,
@@ -1642,6 +1648,8 @@ class HuffModel:
1642
1648
 
1643
1649
  print("----------------------------------")
1644
1650
 
1651
+ huff_modelfit = None
1652
+
1645
1653
  if interaction_matrix_metadata != {} and "fit" in interaction_matrix_metadata and interaction_matrix_metadata["fit"]["function"] is not None:
1646
1654
  print("Parameter estimation")
1647
1655
  print("Fit function " + interaction_matrix_metadata["fit"]["function"])
@@ -1682,6 +1690,13 @@ class HuffModel:
1682
1690
 
1683
1691
  print("----------------------------------")
1684
1692
 
1693
+ return [
1694
+ customer_origins_metadata,
1695
+ supply_locations_metadata,
1696
+ interaction_matrix_metadata,
1697
+ huff_modelfit
1698
+ ]
1699
+
1685
1700
  def mci_fit(
1686
1701
  self,
1687
1702
  cols: list = ["A_j", "t_ij"],
@@ -2341,6 +2356,7 @@ class MCIModel:
2341
2356
 
2342
2357
  customer_origins_metadata = interaction_matrix.get_customer_origins().get_metadata()
2343
2358
  supply_locations_metadata = interaction_matrix.get_supply_locations().get_metadata()
2359
+ interaction_matrix_metadata = interaction_matrix.get_metadata()
2344
2360
 
2345
2361
  print("Multiplicative Competitive Interaction Model")
2346
2362
  print("--------------------------------------------")
@@ -2371,7 +2387,10 @@ class MCIModel:
2371
2387
 
2372
2388
  print("--------------------------------------------")
2373
2389
 
2390
+ mci_modelfit = None
2391
+
2374
2392
  mci_modelfit = self.modelfit()
2393
+
2375
2394
  if mci_modelfit is not None:
2376
2395
 
2377
2396
  print ("Goodness-of-fit for probabilities")
@@ -2402,6 +2421,13 @@ class MCIModel:
2402
2421
  print(APE_df.to_string(index=False))
2403
2422
 
2404
2423
  print("--------------------------------------------")
2424
+
2425
+ return [
2426
+ customer_origins_metadata,
2427
+ supply_locations_metadata,
2428
+ interaction_matrix_metadata,
2429
+ mci_modelfit
2430
+ ]
2405
2431
 
2406
2432
  def utility(
2407
2433
  self,
huff/osm.py CHANGED
@@ -4,8 +4,8 @@
4
4
  # Author: Thomas Wieland
5
5
  # ORCID: 0000-0001-5168-9846
6
6
  # mail: geowieland@googlemail.com
7
- # Version: 1.4.2
8
- # Last update: 2025-07-31 18:24
7
+ # Version: 1.4.3
8
+ # Last update: 2025-08-07 17:21
9
9
  # Copyright (c) 2025 Thomas Wieland
10
10
  #-----------------------------------------------------------------------
11
11
 
@@ -23,7 +23,7 @@ class Client:
23
23
  self,
24
24
  server = "http://a.tile.openstreetmap.org/",
25
25
  headers = {
26
- 'User-Agent': 'huff.osm/1.0.0 (your_name@your_email_provider.com)'
26
+ 'User-Agent': 'huff.osm/1.4.3 (your_name@your_email_provider.com)'
27
27
  }
28
28
  ):
29
29
 
@@ -40,23 +40,31 @@ class Client:
40
40
 
41
41
  osm_url = self.server + f"{zoom}/{x}/{y}.png"
42
42
 
43
- response = requests.get(
44
- osm_url,
45
- headers = self.headers,
46
- timeout = timeout
47
- )
48
-
49
- if response.status_code == 200:
50
-
51
- with tempfile.NamedTemporaryFile(delete=False, suffix='.png') as tmp_file:
52
- tmp_file.write(response.content)
53
- tmp_file_path = tmp_file.name
54
- return Image.open(tmp_file_path)
43
+ try:
55
44
 
56
- else:
57
-
58
- print(f"Error while accessing OSM server. Status code: {response.status_code} - {response.reason}")
59
-
45
+ response = requests.get(
46
+ osm_url,
47
+ headers = self.headers,
48
+ timeout = timeout
49
+ )
50
+
51
+ if response.status_code == 200:
52
+
53
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.png') as tmp_file:
54
+ tmp_file.write(response.content)
55
+ tmp_file_path = tmp_file.name
56
+ return Image.open(tmp_file_path)
57
+
58
+ else:
59
+
60
+ print(f"Error while accessing OSM server with URL {osm_url}. Status code: {response.status_code} - {response.reason}")
61
+
62
+ return None
63
+
64
+ except Exception as e:
65
+
66
+ print(f"Error while accessing OSM server with URL {osm_url}. Error message: {e}")
67
+
60
68
  return None
61
69
 
62
70
 
huff/tests/tests_huff.py CHANGED
@@ -4,8 +4,8 @@
4
4
  # Author: Thomas Wieland
5
5
  # ORCID: 0000-0001-5168-9846
6
6
  # mail: geowieland@googlemail.com
7
- # Version: 1.5.6
8
- # Last update: 2025-07-31 18:33
7
+ # Version: 1.5.8
8
+ # Last update: 2025-08-07 17:20
9
9
  # Copyright (c) 2025 Thomas Wieland
10
10
  #-----------------------------------------------------------------------
11
11
 
@@ -1,20 +1,20 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: huff
3
- Version: 1.5.6
3
+ Version: 1.5.8
4
4
  Summary: huff: Huff Model Market Area Analysis
5
5
  Author: Thomas Wieland
6
6
  Author-email: geowieland@googlemail.com
7
7
  Description-Content-Type: text/markdown
8
- Requires-Dist: geopandas
9
- Requires-Dist: pandas
10
- Requires-Dist: numpy
11
- Requires-Dist: statsmodels
12
- Requires-Dist: shapely
13
- Requires-Dist: requests
14
- Requires-Dist: matplotlib
15
- Requires-Dist: pillow
16
- Requires-Dist: contextily
17
- Requires-Dist: openpyxl
8
+ Requires-Dist: geopandas==0.14.4
9
+ Requires-Dist: pandas==2.2.3
10
+ Requires-Dist: numpy==1.26.3
11
+ Requires-Dist: statsmodels==0.14.1
12
+ Requires-Dist: shapely==2.0.4
13
+ Requires-Dist: requests==2.31.0
14
+ Requires-Dist: matplotlib==3.8.2
15
+ Requires-Dist: pillow==10.2.0
16
+ Requires-Dist: contextily==1.6.2
17
+ Requires-Dist: openpyxl==3.1.4
18
18
 
19
19
  # huff: Huff Model Market Area Analysis
20
20
 
@@ -28,11 +28,13 @@ Thomas Wieland [ORCID](https://orcid.org/0000-0001-5168-9846) [EMail](mailto:geo
28
28
  See the /tests directory for usage examples of most of the included functions.
29
29
 
30
30
 
31
- ## Updates v1.5.6
31
+ ## Updates v1.5.8
32
+ - Bugfixes:
33
+ - buffers() now checks whether unique_id_col exists
34
+ - buffers() now checks whether input gdf has geographic coordinate system
35
+ - download_tile() which is used in map_with_basemap() now controls for timeouts
32
36
  - Extensions:
33
- - gistools.distance_matrix() now includes line geometry output
34
- - Other
35
- - map_with_basemap() now belongs to huff.gistools
37
+ - Function polygon_select(): Selection of polygons from point's airline distance
36
38
 
37
39
 
38
40
  ## Features
@@ -1,10 +1,10 @@
1
1
  huff/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- huff/gistools.py,sha256=cffGqNRaCaIE0f52j5bGpgbdxMxy8zmeMQqPhrVSfOE,12890
3
- huff/models.py,sha256=yrV4enajAG0bHCgrGh3w-SNX5kJDJnuE28i6RXN30HE,134903
2
+ huff/gistools.py,sha256=L7fw9AE749kgzhFW8kpiPXHF8WWQIpD1dKDX2zSicO0,15511
3
+ huff/models.py,sha256=KBey1HLPKDHLfT6WkDofeF3XGhp8c19FfAyhietvM8U,135564
4
4
  huff/ors.py,sha256=JlO2UEishQX87PIiktksOrVT5QdB-GEWgjXcxoR_KuA,11929
5
- huff/osm.py,sha256=1a74HkKgAJYLWdthnAslOEJiHYz7qI3dk7u4ku6Jq0o,3563
5
+ huff/osm.py,sha256=vQMBocUt8r_2UhVAPzA9csOTrOuVHonTPk12L-4eBG4,3856
6
6
  huff/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- huff/tests/tests_huff.py,sha256=B7C74NhXPtuwWY8X-5dxrB5GQLnT4OqgId4ZSGaTBQo,13095
7
+ huff/tests/tests_huff.py,sha256=lbbmxJEZZ8kTnW9qwLmNTOOKgHOVOXsBCrSuKKz0umw,13095
8
8
  huff/tests/data/Haslach.cpg,sha256=OtMDH1UDpEBK-CUmLugjLMBNTqZoPULF3QovKiesmCQ,5
9
9
  huff/tests/data/Haslach.dbf,sha256=GVPIt05OzDO7UrRDcsMhiYWvyXAPg6Z-qkiysFzj-fc,506
10
10
  huff/tests/data/Haslach.prj,sha256=2Jy1Vlzh7UxQ1MXpZ9UYLs2SxfrObj2xkEkZyLqmGTY,437
@@ -24,7 +24,7 @@ huff/tests/data/Haslach_supermarkets.qmd,sha256=JlcOYzG4vI1NH1IuOpxwIPnJsCyC-pDR
24
24
  huff/tests/data/Haslach_supermarkets.shp,sha256=X7QbQ0BTMag_B-bDRbpr-go2BQIXo3Y8zMAKpYZmlps,324
25
25
  huff/tests/data/Haslach_supermarkets.shx,sha256=j23QHX-SmdAeN04rw0x8nUOran-OCg_T6r_LvzzEPWs,164
26
26
  huff/tests/data/Wieland2015.xlsx,sha256=H4rxCFlctn44-O6mIyeFf67FlgvznLX7xZqpoWYS41A,25788
27
- huff-1.5.6.dist-info/METADATA,sha256=4ZRQx28t5HIXwoHSlNG-S2Q0hocMBAnhmgkYEkV95PI,6023
28
- huff-1.5.6.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
29
- huff-1.5.6.dist-info/top_level.txt,sha256=nlzX-PxZNFmIxANIJMySuIFPihd6qOBkRlhIC28NEsQ,5
30
- huff-1.5.6.dist-info/RECORD,,
27
+ huff-1.5.8.dist-info/METADATA,sha256=VBE1R3rh3RlLMGMHZfdLci6MA9HrOhLWiwm7808B8p8,6282
28
+ huff-1.5.8.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
29
+ huff-1.5.8.dist-info/top_level.txt,sha256=nlzX-PxZNFmIxANIJMySuIFPihd6qOBkRlhIC28NEsQ,5
30
+ huff-1.5.8.dist-info/RECORD,,
File without changes