huff 1.3.0__py3-none-any.whl → 1.3.1__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.3.0
8
- # Last update: 2025-05-22 05:44
7
+ # Version: 1.3.1
8
+ # Last update: 2025-05-28 18:01
9
9
  # Copyright (c) 2025 Thomas Wieland
10
10
  #-----------------------------------------------------------------------
11
11
 
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.3.0
8
- # Last update: 2025-05-22 05:45
7
+ # Version: 1.3.1
8
+ # Last update: 2025-05-28 18:01
9
9
  # Copyright (c) 2025 Thomas Wieland
10
10
  #-----------------------------------------------------------------------
11
11
 
@@ -50,11 +50,11 @@ class CustomerOrigins:
50
50
 
51
51
  return self.metadata
52
52
 
53
- def get_isochrones(self):
53
+ def get_isochrones_gdf(self):
54
54
 
55
55
  return self.isochrones_gdf
56
56
 
57
- def get_buffers(self):
57
+ def get_buffers_gdf(self):
58
58
 
59
59
  return self.buffers_gdf
60
60
 
@@ -1017,8 +1017,7 @@ class MCIModel:
1017
1017
  transformation = "LCT"
1018
1018
  ):
1019
1019
 
1020
- interaction_matrix = self.interaction_matrix
1021
-
1020
+ interaction_matrix = self.interaction_matrix
1022
1021
  interaction_matrix_df = interaction_matrix.get_interaction_matrix_df()
1023
1022
 
1024
1023
  if interaction_matrix_df["t_ij"].isna().all():
@@ -1031,7 +1030,8 @@ class MCIModel:
1031
1030
  cols = ["A_j", "t_ij"]
1032
1031
  )
1033
1032
 
1034
- customer_origins_metadata = interaction_matrix.get_customer_origins().get_metadata()
1033
+ customer_origins = interaction_matrix.get_customer_origins()
1034
+ customer_origins_metadata = customer_origins.get_metadata()
1035
1035
 
1036
1036
  t_ij_weighting = customer_origins_metadata["weighting"][0]["param"]
1037
1037
 
@@ -1040,7 +1040,8 @@ class MCIModel:
1040
1040
  else:
1041
1041
  mci_formula = f"t_ij**{t_ij_weighting}"
1042
1042
 
1043
- supply_locations_metadata = interaction_matrix.get_supply_locations().get_metadata()
1043
+ supply_locations = interaction_matrix.get_supply_locations()
1044
+ supply_locations_metadata = supply_locations.get_metadata()
1044
1045
  attraction_col = supply_locations_metadata["attraction_col"]
1045
1046
  attraction_weighting = supply_locations_metadata["weighting"]
1046
1047
 
@@ -1056,17 +1057,27 @@ class MCIModel:
1056
1057
  if transformation == "ILCT":
1057
1058
  interaction_matrix_df["U_ij"] = np.exp(interaction_matrix_df["U_ij"])
1058
1059
 
1059
- self.interaction_matrix = interaction_matrix_df
1060
+ interaction_matrix = InteractionMatrix(
1061
+ interaction_matrix_df,
1062
+ customer_origins,
1063
+ supply_locations
1064
+ )
1065
+ self.interaction_matrix = interaction_matrix
1060
1066
 
1061
1067
  return self
1062
1068
 
1063
- def probabilities (self):
1069
+ def probabilities (
1070
+ self,
1071
+ transformation = "LCT"
1072
+ ):
1064
1073
 
1065
- interaction_matrix_df = self.interaction_matrix_df
1074
+ interaction_matrix = self.interaction_matrix
1075
+ interaction_matrix_df = interaction_matrix.get_interaction_matrix_df()
1066
1076
 
1067
1077
  if interaction_matrix_df["U_ij"].isna().all():
1068
- self.utility()
1069
- interaction_matrix_df = self.interaction_matrix_df
1078
+ self.utility(transformation = transformation)
1079
+ interaction_matrix = self.interaction_matrix
1080
+ interaction_matrix_df = interaction_matrix.get_interaction_matrix_df()
1070
1081
 
1071
1082
  utility_i = pd.DataFrame(interaction_matrix_df.groupby("i")["U_ij"].sum())
1072
1083
  utility_i = utility_i.rename(columns = {"U_ij": "U_i"})
@@ -1082,14 +1093,22 @@ class MCIModel:
1082
1093
 
1083
1094
  interaction_matrix_df = interaction_matrix_df.drop(columns=["U_i"])
1084
1095
 
1085
- self.interaction_matrix_df = interaction_matrix_df
1096
+ interaction_matrix.interaction_matrix_df = interaction_matrix_df
1097
+ self.interaction_matrix = interaction_matrix
1086
1098
 
1087
1099
  return self
1088
1100
 
1089
- def flows (self):
1101
+ def flows (
1102
+ self,
1103
+ transformation = "LCT"
1104
+ ):
1090
1105
 
1091
- interaction_matrix_df = self.interaction_matrix_df
1106
+ interaction_matrix = self.interaction_matrix
1107
+ interaction_matrix_df = interaction_matrix.get_interaction_matrix_df()
1092
1108
 
1109
+ if "C_i" not in interaction_matrix_df.columns:
1110
+ raise ValueError ("No market size column defined in interaction matrix.")
1111
+
1093
1112
  if interaction_matrix_df["C_i"].isna().all():
1094
1113
  raise ValueError ("Market size column in customer origins not defined. Use CustomerOrigins.define_marketsize()")
1095
1114
 
@@ -1099,8 +1118,9 @@ class MCIModel:
1099
1118
  )
1100
1119
 
1101
1120
  if interaction_matrix_df["p_ij"].isna().all():
1102
- self.probabilities()
1103
- interaction_matrix_df = self.interaction_matrix_df
1121
+ self.probabilities(transformation=transformation)
1122
+ interaction_matrix = self.interaction_matrix
1123
+ interaction_matrix_df = interaction_matrix.get_interaction_matrix_df()
1104
1124
 
1105
1125
  interaction_matrix_df["E_ij"] = interaction_matrix_df["p_ij"] * interaction_matrix_df["C_i"]
1106
1126
 
@@ -1573,7 +1593,7 @@ def get_isochrones(
1573
1593
  profile: str = "driving-car",
1574
1594
  donut: bool = True,
1575
1595
  ors_server: str = "https://api.openrouteservice.org/v2/",
1576
- ors_auth: str = None,
1596
+ ors_auth: str = None,
1577
1597
  timeout = 10,
1578
1598
  delay = 1,
1579
1599
  save_output: bool = True,
@@ -1623,6 +1643,8 @@ def get_isochrones(
1623
1643
  time.sleep(delay)
1624
1644
 
1625
1645
  isochrone_gdf[unique_id_col] = unique_id_values[i]
1646
+
1647
+ isochrone_gdf["segment_minutes"] = isochrone_gdf["segment"]/60
1626
1648
 
1627
1649
  isochrones_gdf = pd.concat(
1628
1650
  [
@@ -1634,6 +1656,9 @@ def get_isochrones(
1634
1656
 
1635
1657
  i = i+1
1636
1658
 
1659
+ isochrones_gdf["segment"] = isochrones_gdf["segment"].astype(int)
1660
+ isochrones_gdf["segment_minutes"] = isochrones_gdf["segment_minutes"].astype(int)
1661
+
1637
1662
  isochrones_gdf.set_crs(
1638
1663
  output_crs,
1639
1664
  allow_override=True,
huff/ors.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.2.1
8
- # Last update: 2025-05-22 05:46
7
+ # Version: 1.3.1
8
+ # Last update: 2025-05-28 18:01
9
9
  # Copyright (c) 2025 Thomas Wieland
10
10
  #-----------------------------------------------------------------------
11
11
 
@@ -119,7 +119,6 @@ class Client:
119
119
  def isochrone(
120
120
  self,
121
121
  locations: list,
122
- id: list = [],
123
122
  segments: list = [900, 600, 300],
124
123
  range_type: str = "time",
125
124
  intersections: str = "true",
@@ -130,6 +129,12 @@ class Client:
130
129
  output_crs: str = "EPSG:4326"
131
130
  ):
132
131
 
132
+ if len(segments) > 10:
133
+ raise ValueError ("ORS client does not allow >10 intervals in an Isochrones query. See https://openrouteservice.org/restrictions/.")
134
+
135
+ if len(locations) > 5:
136
+ raise ValueError ("ORS client does not allow >5 locations in an Isochrones query. See https://openrouteservice.org/restrictions/.")
137
+
133
138
  ors_url = self.server + "isochrones/" + profile
134
139
  auth = self.auth
135
140
 
@@ -200,7 +205,7 @@ class Client:
200
205
 
201
206
  isochrones_gdf["segment"] = 0
202
207
  isochrones_gdf_properties_dict = dict(isochrones_gdf["properties"])
203
-
208
+
204
209
  for i in range(len(isochrones_gdf_properties_dict)):
205
210
  isochrones_gdf.iloc[i,3] = isochrones_gdf_properties_dict[i]["value"]
206
211
 
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.0.0
8
- # Last update: 2025-05-22 05:46
7
+ # Version: 1.3.1
8
+ # Last update: 2025-05-28 18:01
9
9
  # Copyright (c) 2025 Thomas Wieland
10
10
  #-----------------------------------------------------------------------
11
11
 
@@ -173,9 +173,52 @@ def map_with_basemap(
173
173
  extent_img = [sw_lon, ne_lon, sw_lat, ne_lat]
174
174
  ax.imshow(img, extent=extent_img, origin="upper")
175
175
 
176
+ i = 0
176
177
  for layer in layers:
178
+
177
179
  layer_3857 = layer.to_crs(epsg=3857)
178
- layer_3857.plot(ax=ax, alpha=0.6)
180
+
181
+ if styles != {}:
182
+
183
+ layer_style = styles[i]
184
+ layer_color = layer_style["color"]
185
+ layer_alpha = layer_style["alpha"]
186
+
187
+ if isinstance(layer_color, str):
188
+
189
+ layer_3857.plot(
190
+ ax=ax,
191
+ color=layer_color,
192
+ alpha=layer_alpha
193
+ )
194
+
195
+ elif isinstance(layer_color, dict):
196
+
197
+ color_key = list(layer_color.keys())[0]
198
+ color_mapping = layer_color[color_key]
199
+
200
+ if color_key not in layer_3857.columns:
201
+ raise KeyError ("Column", color_key, "not in layer.")
202
+
203
+ for value, color in color_mapping.items():
204
+
205
+ subset = layer_3857[layer_3857[color_key].astype(str) == str(value)]
206
+
207
+ if not subset.empty:
208
+ subset.plot(
209
+ ax=ax,
210
+ color=color,
211
+ alpha=layer_alpha
212
+ )
213
+
214
+ else:
215
+
216
+ layer_3857.plot(
217
+ ax=ax,
218
+ alpha=0.6
219
+ )
220
+
221
+ i = i+1
179
222
 
180
223
  bbox = box(sw_lon, sw_lat, ne_lon, ne_lat)
181
224
  extent_geom = gpd.GeoSeries([bbox], crs=4326).to_crs(epsg=3857).total_bounds
@@ -1,43 +1,43 @@
1
- <!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
2
- <qgis version="3.28.3-Firenze">
3
- <identifier></identifier>
4
- <parentidentifier></parentidentifier>
5
- <language></language>
6
- <type>dataset</type>
7
- <title></title>
8
- <abstract></abstract>
9
- <contact>
10
- <name></name>
11
- <organization></organization>
12
- <position></position>
13
- <voice></voice>
14
- <fax></fax>
15
- <email></email>
16
- <role></role>
17
- </contact>
18
- <links/>
19
- <fees></fees>
20
- <encoding></encoding>
21
- <crs>
22
- <spatialrefsys nativeFormat="Wkt">
23
- <wkt>PROJCRS["DHDN / 3-degree Gauss-Kruger zone 3",BASEGEOGCRS["DHDN",DATUM["Deutsches Hauptdreiecksnetz",ELLIPSOID["Bessel 1841",6377397.155,299.1528128,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4314]],CONVERSION["3-degree Gauss-Kruger zone 3",METHOD["Transverse Mercator",ID["EPSG",9807]],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",9,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8802]],PARAMETER["Scale factor at natural origin",1,SCALEUNIT["unity",1],ID["EPSG",8805]],PARAMETER["False easting",3500000,LENGTHUNIT["metre",1],ID["EPSG",8806]],PARAMETER["False northing",0,LENGTHUNIT["metre",1],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["northing (X)",north,ORDER[1],LENGTHUNIT["metre",1]],AXIS["easting (Y)",east,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Cadastre, engineering survey, topographic mapping."],AREA["Germany - former West Germany onshore between 7�30'E and 10�30'E - states of Baden-Wurtemberg, Bayern, Bremen, Hamberg, Hessen, Niedersachsen, Nordrhein-Westfalen, Rhineland-Pfalz, Schleswig-Holstein."],BBOX[47.27,7.5,55.09,10.51]],ID["EPSG",31467]]</wkt>
24
- <proj4>+proj=tmerc +lat_0=0 +lon_0=9 +k=1 +x_0=3500000 +y_0=0 +ellps=bessel +towgs84=598.1,73.7,418.2,0.202,0.045,-2.455,6.7 +units=m +no_defs</proj4>
25
- <srsid>2647</srsid>
26
- <srid>31467</srid>
27
- <authid>EPSG:31467</authid>
28
- <description>DHDN / 3-degree Gauss-Kruger zone 3</description>
29
- <projectionacronym>tmerc</projectionacronym>
30
- <ellipsoidacronym>EPSG:7004</ellipsoidacronym>
31
- <geographicflag>false</geographicflag>
32
- </spatialrefsys>
33
- </crs>
34
- <extent>
35
- <spatial maxy="0" dimensions="2" maxx="0" maxz="0" crs="EPSG:31467" miny="0" minz="0" minx="0"/>
36
- <temporal>
37
- <period>
38
- <start></start>
39
- <end></end>
40
- </period>
41
- </temporal>
42
- </extent>
43
- </qgis>
1
+ <!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
2
+ <qgis version="3.28.3-Firenze">
3
+ <identifier></identifier>
4
+ <parentidentifier></parentidentifier>
5
+ <language></language>
6
+ <type>dataset</type>
7
+ <title></title>
8
+ <abstract></abstract>
9
+ <contact>
10
+ <name></name>
11
+ <organization></organization>
12
+ <position></position>
13
+ <voice></voice>
14
+ <fax></fax>
15
+ <email></email>
16
+ <role></role>
17
+ </contact>
18
+ <links/>
19
+ <fees></fees>
20
+ <encoding></encoding>
21
+ <crs>
22
+ <spatialrefsys nativeFormat="Wkt">
23
+ <wkt>PROJCRS["DHDN / 3-degree Gauss-Kruger zone 3",BASEGEOGCRS["DHDN",DATUM["Deutsches Hauptdreiecksnetz",ELLIPSOID["Bessel 1841",6377397.155,299.1528128,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4314]],CONVERSION["3-degree Gauss-Kruger zone 3",METHOD["Transverse Mercator",ID["EPSG",9807]],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",9,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8802]],PARAMETER["Scale factor at natural origin",1,SCALEUNIT["unity",1],ID["EPSG",8805]],PARAMETER["False easting",3500000,LENGTHUNIT["metre",1],ID["EPSG",8806]],PARAMETER["False northing",0,LENGTHUNIT["metre",1],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["northing (X)",north,ORDER[1],LENGTHUNIT["metre",1]],AXIS["easting (Y)",east,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Cadastre, engineering survey, topographic mapping."],AREA["Germany - former West Germany onshore between 7�30'E and 10�30'E - states of Baden-Wurtemberg, Bayern, Bremen, Hamberg, Hessen, Niedersachsen, Nordrhein-Westfalen, Rhineland-Pfalz, Schleswig-Holstein."],BBOX[47.27,7.5,55.09,10.51]],ID["EPSG",31467]]</wkt>
24
+ <proj4>+proj=tmerc +lat_0=0 +lon_0=9 +k=1 +x_0=3500000 +y_0=0 +ellps=bessel +towgs84=598.1,73.7,418.2,0.202,0.045,-2.455,6.7 +units=m +no_defs</proj4>
25
+ <srsid>2647</srsid>
26
+ <srid>31467</srid>
27
+ <authid>EPSG:31467</authid>
28
+ <description>DHDN / 3-degree Gauss-Kruger zone 3</description>
29
+ <projectionacronym>tmerc</projectionacronym>
30
+ <ellipsoidacronym>EPSG:7004</ellipsoidacronym>
31
+ <geographicflag>false</geographicflag>
32
+ </spatialrefsys>
33
+ </crs>
34
+ <extent>
35
+ <spatial maxy="0" dimensions="2" maxx="0" maxz="0" crs="EPSG:31467" miny="0" minz="0" minx="0"/>
36
+ <temporal>
37
+ <period>
38
+ <start></start>
39
+ <end></end>
40
+ </period>
41
+ </temporal>
42
+ </extent>
43
+ </qgis>
@@ -1,43 +1,43 @@
1
- <!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
2
- <qgis version="3.28.3-Firenze">
3
- <identifier></identifier>
4
- <parentidentifier></parentidentifier>
5
- <language></language>
6
- <type>dataset</type>
7
- <title></title>
8
- <abstract></abstract>
9
- <contact>
10
- <name></name>
11
- <organization></organization>
12
- <position></position>
13
- <voice></voice>
14
- <fax></fax>
15
- <email></email>
16
- <role></role>
17
- </contact>
18
- <links/>
19
- <fees></fees>
20
- <encoding></encoding>
21
- <crs>
22
- <spatialrefsys nativeFormat="Wkt">
23
- <wkt>PROJCRS["DHDN / 3-degree Gauss-Kruger zone 3",BASEGEOGCRS["DHDN",DATUM["Deutsches Hauptdreiecksnetz",ELLIPSOID["Bessel 1841",6377397.155,299.1528128,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4314]],CONVERSION["3-degree Gauss-Kruger zone 3",METHOD["Transverse Mercator",ID["EPSG",9807]],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",9,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8802]],PARAMETER["Scale factor at natural origin",1,SCALEUNIT["unity",1],ID["EPSG",8805]],PARAMETER["False easting",3500000,LENGTHUNIT["metre",1],ID["EPSG",8806]],PARAMETER["False northing",0,LENGTHUNIT["metre",1],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["northing (X)",north,ORDER[1],LENGTHUNIT["metre",1]],AXIS["easting (Y)",east,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Cadastre, engineering survey, topographic mapping."],AREA["Germany - former West Germany onshore between 7�30'E and 10�30'E - states of Baden-Wurtemberg, Bayern, Bremen, Hamberg, Hessen, Niedersachsen, Nordrhein-Westfalen, Rhineland-Pfalz, Schleswig-Holstein."],BBOX[47.27,7.5,55.09,10.51]],ID["EPSG",31467]]</wkt>
24
- <proj4>+proj=tmerc +lat_0=0 +lon_0=9 +k=1 +x_0=3500000 +y_0=0 +ellps=bessel +towgs84=598.1,73.7,418.2,0.202,0.045,-2.455,6.7 +units=m +no_defs</proj4>
25
- <srsid>2647</srsid>
26
- <srid>31467</srid>
27
- <authid>EPSG:31467</authid>
28
- <description>DHDN / 3-degree Gauss-Kruger zone 3</description>
29
- <projectionacronym>tmerc</projectionacronym>
30
- <ellipsoidacronym>EPSG:7004</ellipsoidacronym>
31
- <geographicflag>false</geographicflag>
32
- </spatialrefsys>
33
- </crs>
34
- <extent>
35
- <spatial maxy="0" dimensions="2" maxx="0" maxz="0" crs="EPSG:31467" miny="0" minz="0" minx="0"/>
36
- <temporal>
37
- <period>
38
- <start></start>
39
- <end></end>
40
- </period>
41
- </temporal>
42
- </extent>
43
- </qgis>
1
+ <!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
2
+ <qgis version="3.28.3-Firenze">
3
+ <identifier></identifier>
4
+ <parentidentifier></parentidentifier>
5
+ <language></language>
6
+ <type>dataset</type>
7
+ <title></title>
8
+ <abstract></abstract>
9
+ <contact>
10
+ <name></name>
11
+ <organization></organization>
12
+ <position></position>
13
+ <voice></voice>
14
+ <fax></fax>
15
+ <email></email>
16
+ <role></role>
17
+ </contact>
18
+ <links/>
19
+ <fees></fees>
20
+ <encoding></encoding>
21
+ <crs>
22
+ <spatialrefsys nativeFormat="Wkt">
23
+ <wkt>PROJCRS["DHDN / 3-degree Gauss-Kruger zone 3",BASEGEOGCRS["DHDN",DATUM["Deutsches Hauptdreiecksnetz",ELLIPSOID["Bessel 1841",6377397.155,299.1528128,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4314]],CONVERSION["3-degree Gauss-Kruger zone 3",METHOD["Transverse Mercator",ID["EPSG",9807]],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",9,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8802]],PARAMETER["Scale factor at natural origin",1,SCALEUNIT["unity",1],ID["EPSG",8805]],PARAMETER["False easting",3500000,LENGTHUNIT["metre",1],ID["EPSG",8806]],PARAMETER["False northing",0,LENGTHUNIT["metre",1],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["northing (X)",north,ORDER[1],LENGTHUNIT["metre",1]],AXIS["easting (Y)",east,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Cadastre, engineering survey, topographic mapping."],AREA["Germany - former West Germany onshore between 7�30'E and 10�30'E - states of Baden-Wurtemberg, Bayern, Bremen, Hamberg, Hessen, Niedersachsen, Nordrhein-Westfalen, Rhineland-Pfalz, Schleswig-Holstein."],BBOX[47.27,7.5,55.09,10.51]],ID["EPSG",31467]]</wkt>
24
+ <proj4>+proj=tmerc +lat_0=0 +lon_0=9 +k=1 +x_0=3500000 +y_0=0 +ellps=bessel +towgs84=598.1,73.7,418.2,0.202,0.045,-2.455,6.7 +units=m +no_defs</proj4>
25
+ <srsid>2647</srsid>
26
+ <srid>31467</srid>
27
+ <authid>EPSG:31467</authid>
28
+ <description>DHDN / 3-degree Gauss-Kruger zone 3</description>
29
+ <projectionacronym>tmerc</projectionacronym>
30
+ <ellipsoidacronym>EPSG:7004</ellipsoidacronym>
31
+ <geographicflag>false</geographicflag>
32
+ </spatialrefsys>
33
+ </crs>
34
+ <extent>
35
+ <spatial maxy="0" dimensions="2" maxx="0" maxz="0" crs="EPSG:31467" miny="0" minz="0" minx="0"/>
36
+ <temporal>
37
+ <period>
38
+ <start></start>
39
+ <end></end>
40
+ </period>
41
+ </temporal>
42
+ </extent>
43
+ </qgis>
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.3.0
8
- # Last update: 2025-05-22 05:41
7
+ # Version: 1.3.1
8
+ # Last update: 2025-05-28 13:10
9
9
  # Copyright (c) 2025 Thomas Wieland
10
10
  #-----------------------------------------------------------------------
11
11
 
@@ -68,7 +68,7 @@ Haslach_supermarkets.define_attraction_weighting(
68
68
  # Define attraction weighting (gamma)
69
69
 
70
70
  Haslach_supermarkets.isochrones(
71
- segments_minutes=[5, 10],
71
+ segments_minutes=[3, 6, 9, 12, 15],
72
72
  profile = "foot-walking",
73
73
  save_output=True,
74
74
  ors_auth="5b3ce3597851110001cf62480a15aafdb5a64f4d91805929f8af6abd",
@@ -170,22 +170,44 @@ Wieland2015_fit.summary()
170
170
  # Buffer analysis:
171
171
 
172
172
  Haslach_supermarkets_gdf = Haslach_supermarkets.get_geodata_gpd_original()
173
- Haslach_buffers = Haslach_buf.get_buffers()
173
+ Haslach_buffers = Haslach_buf.get_buffers_gdf()
174
174
  # Extracting points and buffer polygons
175
175
 
176
- spj_test = point_spatial_join(
176
+ Haslach_districts_buf = point_spatial_join(
177
177
  polygon_gdf = Haslach_buffers,
178
178
  point_gdf = Haslach_supermarkets_gdf,
179
179
  polygon_ref_cols = ["BEZEICHN", "segment"],
180
180
  point_stat_col = "VKF_qm"
181
181
  )
182
182
  # Spatial join with buffers and points
183
- # Statistics for supermarkets by statistical districts
183
+ # Statistics for supermarket selling space by buffers of statistical districts
184
+ # (How much selling space in 500, 1000, and 1500 metres?)
184
185
 
185
- spj_test[0].to_file("spj_test.shp")
186
+ Haslach_districts_buf[0].to_file("Haslach_districts_buf.shp")
186
187
  # Save joined points as shapefile
187
188
 
188
- print(spj_test[1])
189
+ print(Haslach_districts_buf[1])
190
+ # Showing df with overlay statistics
191
+
192
+
193
+ # Isochrones analysis:
194
+
195
+ Haslach_districts = Haslach.get_geodata_gpd_original()
196
+
197
+ Haslach_supermarkets_iso = point_spatial_join(
198
+ polygon_gdf = Haslach_supermarkets_isochrones,
199
+ point_gdf = Haslach_districts,
200
+ polygon_ref_cols = ["LFDNR", "segment"],
201
+ point_stat_col = "pop"
202
+ )
203
+ # Spatial join with isochrones and points
204
+ # Statistics for population by isochrones of supermarkets
205
+ # (How much population in 5, 10, and 15 minutes?)
206
+
207
+ Haslach_supermarkets_iso[0].to_file("Haslach_supermarkets_iso.shp")
208
+ # Save joined points as shapefile
209
+
210
+ print(Haslach_supermarkets_iso[1])
189
211
  # Showing df with overlay statistics
190
212
 
191
213
 
@@ -202,6 +224,37 @@ map_with_basemap(
202
224
  Haslach_gdf,
203
225
  Haslach_supermarkets_gdf
204
226
  ],
227
+ styles={
228
+ 0: {
229
+ "color": {
230
+ "segment_minutes": {
231
+ "3": "green",
232
+ "6": "yellow",
233
+ "9": "orange",
234
+ "12": "red",
235
+ "13": "darkred"
236
+ }
237
+ },
238
+ "alpha": 0.3
239
+ },
240
+ 1: {
241
+ "color": "green",
242
+ "alpha": 1
243
+ },
244
+ 2: {
245
+ "color": {
246
+ "Name": {
247
+ "Aldi S├╝d": "blue",
248
+ "Edeka": "yellow",
249
+ "Lidl": "red",
250
+ "Netto": "orange",
251
+ "Real": "black",
252
+ "Treff 3000": "fuchsia"
253
+ }
254
+ },
255
+ "alpha": 1
256
+ }
257
+ },
205
258
  output_filepath = "Haslach_map.png"
206
259
  )
207
260
  # Map with three layers and OSM basemap
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: huff
3
- Version: 1.3.0
3
+ Version: 1.3.1
4
4
  Summary: huff: Huff Model Market Area Analysis
5
5
  Author: Thomas Wieland
6
6
  Author-email: geowieland@googlemail.com
@@ -18,6 +18,9 @@ Requires-Dist: openpyxl
18
18
 
19
19
  # huff: Huff Model Market Area Analysis
20
20
 
21
+ This Python library is designed for performing market area analyses with the Huff Model (Huff 1962, 1964) and/or the Multiplicative Competitive Interaction (MCI) Model (Nakanishi and Cooper 1974, 1982). Users may load point shapefiles (or CSV, XLSX) of customer origins and supply locations and conduct a market area analysis step by step. The package also includes supplementary GIS functions, including clients for OpenRouteService(1) for network analysis (e.g., transport cost matrix) and OpenStreetMap(2) for simple maps. See Huff and McCallum (2008) or Wieland (2017) for a description of the models and their practical application.
22
+
23
+
21
24
  ## Author
22
25
 
23
26
  Thomas Wieland [ORCID](https://orcid.org/0000-0001-5168-9846) [EMail](mailto:geowieland@googlemail.com)
@@ -36,10 +39,10 @@ See the /tests directory for usage examples of most of the included functions.
36
39
  - Fitting MCI model with >= 2 independent variables
37
40
  - MCI model market simulation
38
41
  - **GIS tools**:
39
- - OpenRouteService Client (1):
42
+ - OpenRouteService(1) Client:
40
43
  - Creating transport costs matrix from origins and destinations
41
44
  - Creating isochrones from origins and destinations
42
- - OpenStreetMap Client (2):
45
+ - OpenStreetMap(2) Client:
43
46
  - Creating simple maps with OSM basemap
44
47
  - Other GIS tools:
45
48
  - Creating buffers from geodata
@@ -51,8 +54,10 @@ See the /tests directory for usage examples of most of the included functions.
51
54
  - Creating origins/destinations objects from point geodata
52
55
 
53
56
  (1) © openrouteservice.org by HeiGIT | Map data © OpenStreetMap contributors | https://openrouteservice.org/
57
+
54
58
  (2) © OpenStreetMap contributors | available under the Open Database License | https://www.openstreetmap.org/
55
59
 
60
+
56
61
  ## Literature
57
62
  - Huff DL (1962) *Determination of Intra-Urban Retail Trade Areas*.
58
63
  - Huff DL (1964) Defining and estimating a trading area. *Journal of Marketing* 28(4): 34–38. [10.2307/1249154](https://doi.org/10.2307/1249154)
@@ -1,24 +1,24 @@
1
1
  huff/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- huff/gistools.py,sha256=yEinx-rq9o8yXW6CgVIVsb1Rwi0WKnvCDRGD52h8jxo,6857
3
- huff/models.py,sha256=hqR3jcFBD2h3SPX_U7YApQrql7yBUw2frmLF4uhVTfI,59645
4
- huff/ors.py,sha256=D38PN2Az9kUbAqsFpb4W5Z5pSkbKSOH1W5IBlhos7ZA,11570
5
- huff/osm.py,sha256=mj9FFxieMd5Gv8Nn72nt89dHSfzZYGQt2dYHOWfx6hY,5464
2
+ huff/gistools.py,sha256=LfMhalPk6rNTQSfT1U9UyJgZP1OWdlnv1MSDfP_c0Ik,6857
3
+ huff/models.py,sha256=xpXBM1rTF_HxW5c7-EELs3-sMXBcYo7-X2kSjKbe12k,60856
4
+ huff/ors.py,sha256=DmzeSYgYekXNposD7PH3Sgw8Jpw3KePYeO8DZMKMhl0,11929
5
+ huff/osm.py,sha256=zYAB3EhijfSl1H5bsGxYnPMd-8t_MDKKXTlzA1XWQ5U,6858
6
6
  huff/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- huff/tests/tests_huff.py,sha256=kWUob6X7DvznSo6Nh9yaffkbdEt7XUK1s-JOVZBLz2g,5588
7
+ huff/tests/tests_huff.py,sha256=7C1sIzHVNBD8kv7DJkrUERuxDnNDJUpmqzA1KozQbtk,7207
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
11
- huff/tests/data/Haslach.qmd,sha256=j9i4_Pz7ZMSG2UDSb3nuhJpw0KWXIRhiiDymqJP6_Fo,2479
11
+ huff/tests/data/Haslach.qmd,sha256=JlcOYzG4vI1NH1IuOpxwIPnJsCyC-pDRAI00TzEvNf0,2522
12
12
  huff/tests/data/Haslach.shp,sha256=s7ks-ukOIKMJCD5x6m0MO6pwkg1USvhudQKTg74ib1E,212
13
13
  huff/tests/data/Haslach.shx,sha256=VEMghRPP_HUYIuGoxR7X0eHQe9LnO4s8JP4twfzKyyk,132
14
14
  huff/tests/data/Haslach_supermarkets.cpg,sha256=OtMDH1UDpEBK-CUmLugjLMBNTqZoPULF3QovKiesmCQ,5
15
15
  huff/tests/data/Haslach_supermarkets.dbf,sha256=4fTBxntDvQ8qFPdGK82ywJd2Xq_9nApDyi3h5_KPFSc,21282
16
16
  huff/tests/data/Haslach_supermarkets.prj,sha256=2Jy1Vlzh7UxQ1MXpZ9UYLs2SxfrObj2xkEkZyLqmGTY,437
17
- huff/tests/data/Haslach_supermarkets.qmd,sha256=j9i4_Pz7ZMSG2UDSb3nuhJpw0KWXIRhiiDymqJP6_Fo,2479
17
+ huff/tests/data/Haslach_supermarkets.qmd,sha256=JlcOYzG4vI1NH1IuOpxwIPnJsCyC-pDRAI00TzEvNf0,2522
18
18
  huff/tests/data/Haslach_supermarkets.shp,sha256=X7QbQ0BTMag_B-bDRbpr-go2BQIXo3Y8zMAKpYZmlps,324
19
19
  huff/tests/data/Haslach_supermarkets.shx,sha256=j23QHX-SmdAeN04rw0x8nUOran-OCg_T6r_LvzzEPWs,164
20
20
  huff/tests/data/Wieland2015.xlsx,sha256=SaVM-Hi5dBTmf2bzszMnZ2Ec8NUE05S_5F2lQj0ayS0,19641
21
- huff-1.3.0.dist-info/METADATA,sha256=4qAsnzZ3lkPANRMjBrkPhj61HoBfC7cHkambNIZjSfI,3884
22
- huff-1.3.0.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
23
- huff-1.3.0.dist-info/top_level.txt,sha256=nlzX-PxZNFmIxANIJMySuIFPihd6qOBkRlhIC28NEsQ,5
24
- huff-1.3.0.dist-info/RECORD,,
21
+ huff-1.3.1.dist-info/METADATA,sha256=cTxd7O_4ey6N3X-dgVrLhIgiPdJiucQqaSKcGlIfyzw,4530
22
+ huff-1.3.1.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
23
+ huff-1.3.1.dist-info/top_level.txt,sha256=nlzX-PxZNFmIxANIJMySuIFPihd6qOBkRlhIC28NEsQ,5
24
+ huff-1.3.1.dist-info/RECORD,,
File without changes