Skip to content

Commit bf2738f

Browse files
songololoclaude
andcommitted
Add custom decay_fn API replacing betas, update docs and QGIS plugin
- New `decay_fn` parameter (expression string using variable `p`) replaces `betas`, `min_threshold_wt`, and `spatial_tolerance` on centrality, accessibility, mixed-use, and stats functions - New `cityseer.decay` module with helpers: exponential, linear, flat, gaussian, logistic - Rename node_beta → node_decay, node_betweenness_beta → node_betweenness_decay - Unify weighted/unweighted metrics into single decay-controlled output (remove _wt/_nw column suffixes) - Add meval crate for Rust-side expression parsing - Update QGIS plugin for new API (centrality, accessibility, stats) - Add comprehensive docs: decay functions, column naming conventions, output reference tables, CityNetwork examples - Segment centrality retains old betas API (analytical integral) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3dc010c commit bf2738f

38 files changed

+2088
-1273
lines changed

docs/generate_docs.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ def custom_format_signature(sig: inspect.Signature, colon: bool = True) -> str:
238238
("cityseer.tools.mock", None, here / "src/pages/tools/mock.md"),
239239
("cityseer.tools.util", None, here / "src/pages/tools/util.md"),
240240
("cityseer.network", None, here / "src/pages/api/network.md"),
241+
("cityseer.decay", None, here / "src/pages/api/decay.md"),
241242
]
242243
for module_name, module, output_path in module_file_maps:
243244
render.configure(template_directory=here / "pdoc_templates", docformat="numpy", math=True)

docs/plots/plots.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
)
3636
# custom colourmap
3737
segment_harmonic_vals = nodes_gdf["cc_seg_harmonic_800"]
38-
mixed_uses_vals = nodes_gdf["cc_hill_q0_800_wt"]
38+
mixed_uses_vals = nodes_gdf["cc_hill_q0_800"]
3939
cmap = colors.LinearSegmentedColormap.from_list("cityseer", ["#64c1ff", "#d32f2f"])
4040
segment_harmonic_vals = colors.Normalize()(segment_harmonic_vals)
4141
segment_harmonic_cols = cmap(segment_harmonic_vals)

docs/src/layouts/PageLayout.astro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const currentPath = Astro.url.pathname
1212
const navPaths = [
1313
'/intro',
1414
'/api/network',
15+
'/api/decay',
1516
'/plugin',
1617
'/tools/io',
1718
'/tools/graphs',

docs/src/pages/api/decay.md

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
---
2+
layout: ../../layouts/PageLayout.astro
3+
---
4+
<section class="module">
5+
6+
# decay
7+
8+
9+
Helper functions for generating decay expression strings. Decay expressions use a single variable `p` representing normalised progress from 0 (source) to 1 (distance/time cutoff). The helpers below produce expression strings that can be passed directly to the `decay_fn` parameter of [`compute_stats`](/metrics/layers#compute-stats), [`compute_accessibilities`](/metrics/layers#compute-accessibilities), and similar functions.
10+
11+
```python
12+
from cityseer import decay
13+
from cityseer.metrics import layers
14+
15+
nodes_gdf, data_gdf = layers.compute_stats(
16+
...,
17+
distances=[1200],
18+
decay_fn=decay.gaussian(peak=400, cutoff=1200, std=150),
19+
)
20+
```
21+
22+
Available presets:
23+
24+
```text
25+
exponential (default) gaussian(peak, cutoff) linear
26+
╷ ╷ ╭─╮ ╷╲
27+
│╲ │ ╱ ╲ │ ╲
28+
│ ╲ │ ╱ ╲ │ ╲
29+
│ ╲ │ ╱ ╲ │ ╲
30+
│ ╲ │ ╱ ╲ │ ╲
31+
│ ╰─╮ │ ╱ ╲ │ ╲
32+
│ ╰───╮ │ ╱ ╰╮ │ ╲
33+
│ ╰──────── │╱ ╰─── │ ╲
34+
╵─────────────────── ╵─────────────────── ╵────────╳
35+
p=0 p=1 p=0 p=1 p=0 p=1
36+
37+
logistic(midpoint, cutoff) flat (no decay)
38+
╷──────╮ ╷───────────────
39+
│ ╲ │
40+
│ ╲ │
41+
│ │ │
42+
│ ╲ │
43+
│ ╲ │
44+
│ ╰╮ │
45+
│ ╰──────── │
46+
╵─────────────────── ╵───────────────
47+
p=0 p=1 p=0 p=1
48+
```
49+
50+
51+
52+
<div class="function">
53+
54+
## exponential
55+
56+
57+
<div class="content">
58+
<span class="name">exponential</span><div class="signature">
59+
<span class="pt">(</span>
60+
<div class="param">
61+
<span class="pn">steepness</span>
62+
<span class="pc">:</span>
63+
<span class="pa"> float = 4.0</span>
64+
</div>
65+
<span class="pt">)-&gt;[</span>
66+
<span class="pr">str</span>
67+
<span class="pt">]</span>
68+
</div>
69+
</div>
70+
71+
72+
Exponential decay: weight = exp(-steepness * p). At p=1 (the cutoff), weight = exp(-steepness). The default steepness of 4 gives ~1.8% weight at the cutoff boundary, matching cityseer's historical default.
73+
### Parameters
74+
<div class="param-set">
75+
<div class="def">
76+
<div class="name">steepness</div>
77+
<div class="type">float</div>
78+
</div>
79+
<div class="desc">
80+
81+
Controls how quickly weight decays. Higher values produce steeper decay. Common values: 2 (~13.5% at cutoff), 4 (~1.8%), 6 (~0.25%).</div>
82+
</div>
83+
84+
85+
</div>
86+
87+
88+
<div class="function">
89+
90+
## linear
91+
92+
93+
<div class="content">
94+
<span class="name">linear</span><div class="signature">
95+
<span class="pt">(</span>
96+
<span class="pt">)-&gt;[</span>
97+
<span class="pr">str</span>
98+
<span class="pt">]</span>
99+
</div>
100+
</div>
101+
102+
103+
Linear decay from 1 at the source to 0 at the cutoff.
104+
105+
</div>
106+
107+
108+
<div class="function">
109+
110+
## flat
111+
112+
113+
<div class="content">
114+
<span class="name">flat</span><div class="signature">
115+
<span class="pt">(</span>
116+
<span class="pt">)-&gt;[</span>
117+
<span class="pr">str</span>
118+
<span class="pt">]</span>
119+
</div>
120+
</div>
121+
122+
123+
No decay: constant weight of 1 everywhere within the cutoff.
124+
125+
</div>
126+
127+
128+
<div class="function">
129+
130+
## gaussian
131+
132+
133+
<div class="content">
134+
<span class="name">gaussian</span><div class="signature multiline">
135+
<span class="pt">(</span>
136+
<div class="param">
137+
<span class="pn">peak</span>
138+
<span class="pc">:</span>
139+
<span class="pa"> float</span>
140+
</div>
141+
<div class="param">
142+
<span class="pn">cutoff</span>
143+
<span class="pc">:</span>
144+
<span class="pa"> float</span>
145+
</div>
146+
<div class="param">
147+
<span class="pn">std</span>
148+
<span class="pc">:</span>
149+
<span class="pa"> float | None = None</span>
150+
</div>
151+
<span class="pt">)-&gt;[</span>
152+
<span class="pr">str</span>
153+
<span class="pt">]</span>
154+
</div>
155+
</div>
156+
157+
158+
Gaussian bell curve peaking at a specified location.
159+
### Parameters
160+
<div class="param-set">
161+
<div class="def">
162+
<div class="name">peak</div>
163+
<div class="type">float</div>
164+
</div>
165+
<div class="desc">
166+
167+
The point at which the weight is highest, in the same units as ``cutoff`` (e.g. metres or minutes).</div>
168+
</div>
169+
170+
<div class="param-set">
171+
<div class="def">
172+
<div class="name">cutoff</div>
173+
<div class="type">float</div>
174+
</div>
175+
<div class="desc">
176+
177+
The cutoff threshold in the same units as ``peak`` (must match the ``distances`` or ``minutes`` parameter).</div>
178+
</div>
179+
180+
<div class="param-set">
181+
<div class="def">
182+
<div class="name">std</div>
183+
<div class="type">float</div>
184+
</div>
185+
<div class="desc">
186+
187+
Standard deviation controlling the width of the bell curve, in the same units as ``peak``/``cutoff``. If not provided, defaults to ``peak / 2``.</div>
188+
</div>
189+
190+
191+
</div>
192+
193+
194+
<div class="function">
195+
196+
## logistic
197+
198+
199+
<div class="content">
200+
<span class="name">logistic</span><div class="signature multiline">
201+
<span class="pt">(</span>
202+
<div class="param">
203+
<span class="pn">midpoint</span>
204+
<span class="pc">:</span>
205+
<span class="pa"> float</span>
206+
</div>
207+
<div class="param">
208+
<span class="pn">cutoff</span>
209+
<span class="pc">:</span>
210+
<span class="pa"> float</span>
211+
</div>
212+
<div class="param">
213+
<span class="pn">rate</span>
214+
<span class="pc">:</span>
215+
<span class="pa"> float = 0.05</span>
216+
</div>
217+
<span class="pt">)-&gt;[</span>
218+
<span class="pr">str</span>
219+
<span class="pt">]</span>
220+
</div>
221+
</div>
222+
223+
224+
Logistic (sigmoid) decay centred at a specified location. Weight transitions from ~1 (near source) to ~0 (beyond midpoint). The transition is centred at ``midpoint``.
225+
### Parameters
226+
<div class="param-set">
227+
<div class="def">
228+
<div class="name">midpoint</div>
229+
<div class="type">float</div>
230+
</div>
231+
<div class="desc">
232+
233+
The point at which weight = 0.5, in the same units as ``cutoff``.</div>
234+
</div>
235+
236+
<div class="param-set">
237+
<div class="def">
238+
<div class="name">cutoff</div>
239+
<div class="type">float</div>
240+
</div>
241+
<div class="desc">
242+
243+
The cutoff threshold in the same units as ``midpoint`` (must match the ``distances`` or ``minutes`` parameter).</div>
244+
</div>
245+
246+
<div class="param-set">
247+
<div class="def">
248+
<div class="name">rate</div>
249+
<div class="type">float</div>
250+
</div>
251+
<div class="desc">
252+
253+
Steepness of the transition. Higher values produce a sharper step. The rate is specified in per-unit terms; internally it is scaled to normalised ``p`` coordinates.</div>
254+
</div>
255+
256+
257+
</div>
258+
259+
260+
261+
</section>

0 commit comments

Comments
 (0)