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 +2 -2
- huff/models.py +44 -19
- huff/ors.py +9 -4
- huff/osm.py +46 -3
- huff/tests/data/Haslach.qmd +43 -43
- huff/tests/data/Haslach_supermarkets.qmd +43 -43
- huff/tests/tests_huff.py +61 -8
- {huff-1.3.0.dist-info → huff-1.3.1.dist-info}/METADATA +8 -3
- {huff-1.3.0.dist-info → huff-1.3.1.dist-info}/RECORD +11 -11
- {huff-1.3.0.dist-info → huff-1.3.1.dist-info}/WHEEL +0 -0
- {huff-1.3.0.dist-info → huff-1.3.1.dist-info}/top_level.txt +0 -0
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.
|
8
|
-
# Last update: 2025-05-
|
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.
|
8
|
-
# Last update: 2025-05-
|
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
|
53
|
+
def get_isochrones_gdf(self):
|
54
54
|
|
55
55
|
return self.isochrones_gdf
|
56
56
|
|
57
|
-
def
|
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
|
-
|
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
|
-
|
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
|
-
|
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 (
|
1069
|
+
def probabilities (
|
1070
|
+
self,
|
1071
|
+
transformation = "LCT"
|
1072
|
+
):
|
1064
1073
|
|
1065
|
-
|
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
|
-
|
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
|
-
|
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 (
|
1101
|
+
def flows (
|
1102
|
+
self,
|
1103
|
+
transformation = "LCT"
|
1104
|
+
):
|
1090
1105
|
|
1091
|
-
|
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
|
-
|
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.
|
8
|
-
# Last update: 2025-05-
|
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.
|
8
|
-
# Last update: 2025-05-
|
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
|
-
|
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
|
huff/tests/data/Haslach.qmd
CHANGED
@@ -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.
|
8
|
-
# Last update: 2025-05-
|
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=[
|
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.
|
173
|
+
Haslach_buffers = Haslach_buf.get_buffers_gdf()
|
174
174
|
# Extracting points and buffer polygons
|
175
175
|
|
176
|
-
|
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
|
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
|
-
|
186
|
+
Haslach_districts_buf[0].to_file("Haslach_districts_buf.shp")
|
186
187
|
# Save joined points as shapefile
|
187
188
|
|
188
|
-
print(
|
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.
|
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
|
42
|
+
- OpenRouteService(1) Client:
|
40
43
|
- Creating transport costs matrix from origins and destinations
|
41
44
|
- Creating isochrones from origins and destinations
|
42
|
-
- OpenStreetMap
|
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=
|
3
|
-
huff/models.py,sha256=
|
4
|
-
huff/ors.py,sha256=
|
5
|
-
huff/osm.py,sha256=
|
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=
|
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=
|
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=
|
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.
|
22
|
-
huff-1.3.
|
23
|
-
huff-1.3.
|
24
|
-
huff-1.3.
|
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
|
File without changes
|