This commit is contained in:
Andras Schmelczer 2026-07-03 18:28:56 +01:00
parent 909e241907
commit 1ee796b282
29 changed files with 250 additions and 126 deletions

View file

@ -34,7 +34,7 @@ DROP_CATEGORIES = {
"emergency/water_tank",
"leisure/bleachers",
"leisure/schoolyard",
# Park "furniture" / incidental features not parks; they massively
# Park "furniture" / incidental features, not parks; they massively
# inflated the Park count (picnic_table ~15k, outdoor_seating ~5.8k).
"leisure/bandstand",
"leisure/bird_hide",
@ -222,7 +222,7 @@ DROP_CATEGORIES = {
"public_transport/entrance",
"public_transport/station",
"public_transport/stop_position",
# Education amenities — schools come from GIAS instead. OSM coverage for
# Education amenities. Schools come from GIAS instead. OSM coverage for
# tertiary education, tutoring, and childcare is too noisy/incomplete to be
# useful on a property-search map.
"amenity/school",
@ -398,7 +398,7 @@ _CATEGORIES: list[tuple[str, str, str, list[str]]] = [
"tourism/theme_park",
# bicycle_rental/boat_rental/marina/slipway used to live here and
# made up ~46% of the bucket (cycle-hire docks, boat ramps); they
# are infrastructure, not entertainment venues — see DROP_CATEGORIES.
# are infrastructure, not entertainment venues. See DROP_CATEGORIES.
"leisure/hackerspace",
"leisure/yes",
],
@ -722,7 +722,7 @@ _CATEGORIES: list[tuple[str, str, str, list[str]]] = [
[
"leisure/fitness_centre",
# leisure/fitness_station (outdoor pull-up bars / trim-trail
# apparatus, ~2.5k) is not a gym — see DROP_CATEGORIES.
# apparatus, ~2.5k) is not a gym. See DROP_CATEGORIES.
"amenity/dojo",
"amenity/dancing_school",
],
@ -849,8 +849,8 @@ _CATEGORIES: list[tuple[str, str, str, list[str]]] = [
"healthcare/pharmacy",
"shop/chemist",
# healthcare/alternative, shop/herbalist and shop/health (homeopaths,
# herbalists, generic "health" shops) are not dispensing pharmacies
# — see DROP_CATEGORIES.
# herbalists, generic "health" shops) are not dispensing pharmacies.
# See DROP_CATEGORIES.
],
),
# "Hospital & Clinic" used to be one bucket; an actual hospital and a small
@ -878,7 +878,7 @@ _CATEGORIES: list[tuple[str, str, str, list[str]]] = [
"healthcare/laboratory",
"healthcare/rehabilitation",
"healthcare/vaccination_centre",
# healthcare/yes (untyped junk rows) is dropped — see DROP_CATEGORIES.
# healthcare/yes (untyped junk rows) is dropped. See DROP_CATEGORIES.
],
),
(
@ -950,7 +950,7 @@ _CATEGORIES: list[tuple[str, str, str, list[str]]] = [
[
"tourism/gallery",
# tourism/artwork (statues, murals, village signs) was 93% of this
# bucket and is not a visitable gallery — see DROP_CATEGORIES.
# bucket and is not a visitable gallery. See DROP_CATEGORIES.
],
),
(
@ -1420,7 +1420,7 @@ def _school_icon_category_expr() -> pl.Expr:
# GIAS phase mixes casing ("Middle deemed Primary" vs "Middle deemed
# primary") so we normalise before matching.
phase = pl.col("phase").str.to_lowercase()
# gias._format_age_range emits three shapes: "<low><high>" (em-dash),
# gias._format_age_range emits three shapes: "<low><high>" (en-dash),
# "up to <high>" (high-only) and "<low>+" (low-only). Extract the leading
# integer as low and the trailing integer as high, then suppress the wrong
# end for the one-sided shapes so they don't collapse to a single bound.
@ -1477,7 +1477,7 @@ def _load_ofsted_ratings(ofsted_path: Path) -> pl.LazyFrame:
the conventional Ofsted labels; when there is no usable graded result
(null/"Not judged", e.g. schools last seen under the post-2024 ungraded
report-card framework) we fall back to "Ungraded inspection overall outcome"
so genuinely good/outstanding schools aren't dropped mirroring
so genuinely good/outstanding schools aren't dropped, mirroring
school_catchments.classify_good_plus_schools. Remaining nulls drop out."""
grade_col = pl.col("Latest OEIF overall effectiveness")
# See school_catchments: the ungraded outcome carries "School remains Good"/
@ -1682,7 +1682,7 @@ def osm_groceries_colocated_with_geolytix(
An OSM Groceries row is a duplicate when a GEOLYTIX point lies within
``radius_m`` metres AND that point's brand tokens (its ``category``, e.g.
"Tesco", "Co-op", "M&S") are all present in the OSM row's name i.e. the
"Tesco", "Co-op", "M&S") are all present in the OSM row's name, i.e. the
same physical branded store. Short brands like "M&S" match via
_GROCERY_SHORT_BRAND_TOKENS; brands that still tokenise to set() are kept.
@ -1703,7 +1703,7 @@ def osm_groceries_colocated_with_geolytix(
osm_ids = osm_groceries["id"].to_list()
osm_name_tokens = [_significant_tokens(n) for n in osm_groceries["name"].to_list()]
# Equirectangular projection to metres around the shared mean latitude — at
# Equirectangular projection to metres around the shared mean latitude. At
# England's scale this is accurate to well under the dedup radius.
mean_lat = float(np.mean(np.concatenate([glx_lat, osm_lat])))
cos_lat = float(np.cos(np.radians(mean_lat)))
@ -1860,7 +1860,7 @@ def transform(
)
# Scope the drop to the Groceries group: a single OSM object can also
# carry a non-grocery aspect (e.g. a convenience store that is also a
# Post Office), which must survive — only its duplicate grocery row goes.
# Post Office), which must survive. Only its duplicate grocery row goes.
lf = lf.filter(
~((pl.col("group") == "Groceries") & pl.col("id").is_in(duplicate_ids))
)