Smal
This commit is contained in:
parent
d34478e13c
commit
1934a38677
5 changed files with 66 additions and 9 deletions
|
|
@ -917,7 +917,7 @@ const en = {
|
||||||
body: 'Want to see property data around you? Use your location, or start the demo at Canary Wharf.',
|
body: 'Want to see property data around you? Use your location, or start the demo at Canary Wharf.',
|
||||||
useLocation: 'Use my location',
|
useLocation: 'Use my location',
|
||||||
locating: 'Locating…',
|
locating: 'Locating…',
|
||||||
useDefault: 'Show Canary Wharf',
|
useDefault: 'Show London',
|
||||||
},
|
},
|
||||||
|
|
||||||
// ── Mobile Drawer ──────────────────────────────────
|
// ── Mobile Drawer ──────────────────────────────────
|
||||||
|
|
|
||||||
|
|
@ -196,6 +196,32 @@ describe('url-state', () => {
|
||||||
expect(state.overlays).toEqual(new Set(['noise', 'crime-hotspots']));
|
expect(state.overlays).toEqual(new Set(['noise', 'crime-hotspots']));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('enables crime + trees overlays by default on a fresh visit', () => {
|
||||||
|
const state = parseUrlState();
|
||||||
|
|
||||||
|
expect(state.overlays).toEqual(new Set(['crime-hotspots', 'trees-outside-woodlands']));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('round-trips an explicit "all overlays off" via the __none sentinel', () => {
|
||||||
|
const params = stateToParams(
|
||||||
|
null,
|
||||||
|
{},
|
||||||
|
[],
|
||||||
|
new Set(),
|
||||||
|
'area',
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
new Set()
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(params.getAll('overlay')).toEqual(['__none']);
|
||||||
|
|
||||||
|
window.history.replaceState({}, '', `/?${params.toString()}`);
|
||||||
|
const state = parseUrlState();
|
||||||
|
|
||||||
|
expect(state.overlays).toEqual(new Set());
|
||||||
|
});
|
||||||
|
|
||||||
it('round-trips satellite basemap selection', () => {
|
it('round-trips satellite basemap selection', () => {
|
||||||
const params = stateToParams(
|
const params = stateToParams(
|
||||||
null,
|
null,
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ import {
|
||||||
type PoiFilterName,
|
type PoiFilterName,
|
||||||
} from './poi-distance-filter';
|
} from './poi-distance-filter';
|
||||||
import { dedupeTravelTimeEntries } from './travel-params';
|
import { dedupeTravelTimeEntries } from './travel-params';
|
||||||
import { isOverlayId, type OverlayId } from './overlays';
|
import { isOverlayId, DEFAULT_OVERLAY_IDS, type OverlayId } from './overlays';
|
||||||
import { CRIME_TYPES, isCrimeTypeValue } from './crime-types';
|
import { CRIME_TYPES, isCrimeTypeValue } from './crime-types';
|
||||||
import { isBasemapId, type BasemapId } from './basemaps';
|
import { isBasemapId, type BasemapId } from './basemaps';
|
||||||
import {
|
import {
|
||||||
|
|
@ -59,6 +59,7 @@ import {
|
||||||
|
|
||||||
const POI_NONE_PARAM = '__none';
|
const POI_NONE_PARAM = '__none';
|
||||||
const CRIME_TYPES_NONE_PARAM = '__none';
|
const CRIME_TYPES_NONE_PARAM = '__none';
|
||||||
|
const OVERLAY_NONE_PARAM = '__none';
|
||||||
const CRIME_OVERLAY_ID: OverlayId = 'crime-hotspots';
|
const CRIME_OVERLAY_ID: OverlayId = 'crime-hotspots';
|
||||||
|
|
||||||
export interface UrlState {
|
export interface UrlState {
|
||||||
|
|
@ -227,7 +228,7 @@ export function parseUrlState(): UrlState {
|
||||||
hasExplicitView: false,
|
hasExplicitView: false,
|
||||||
filters: parseFilters(params),
|
filters: parseFilters(params),
|
||||||
poiCategories: new Set(),
|
poiCategories: new Set(),
|
||||||
overlays: new Set(),
|
overlays: new Set(DEFAULT_OVERLAY_IDS),
|
||||||
basemap: 'standard',
|
basemap: 'standard',
|
||||||
colorOpacity: DEFAULT_COLOR_OPACITY,
|
colorOpacity: DEFAULT_COLOR_OPACITY,
|
||||||
tab: 'area',
|
tab: 'area',
|
||||||
|
|
@ -266,9 +267,14 @@ export function parseUrlState(): UrlState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Overlays default to DEFAULT_OVERLAY_IDS on a fresh visit; explicit `overlay`
|
||||||
|
// params override that, and the `__none` sentinel records "all off" so it
|
||||||
|
// survives a reload instead of silently restoring the defaults.
|
||||||
const overlayParams = params.getAll('overlay');
|
const overlayParams = params.getAll('overlay');
|
||||||
if (overlayParams.length > 0) {
|
if (overlayParams.length > 0) {
|
||||||
result.overlays = new Set(overlayParams.filter(isOverlayId));
|
result.overlays = overlayParams.includes(OVERLAY_NONE_PARAM)
|
||||||
|
? new Set()
|
||||||
|
: new Set(overlayParams.filter(isOverlayId));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crime-type filter: repeated `crimeType` params, or the `__none` sentinel.
|
// Crime-type filter: repeated `crimeType` params, or the `__none` sentinel.
|
||||||
|
|
@ -451,10 +457,16 @@ export function stateToParams(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedOverlays) {
|
if (selectedOverlays) {
|
||||||
|
if (selectedOverlays.size === 0) {
|
||||||
|
// Distinguish a deliberate "all overlays off" from a fresh visit, which
|
||||||
|
// would otherwise re-apply the defaults on reload.
|
||||||
|
params.append('overlay', OVERLAY_NONE_PARAM);
|
||||||
|
} else {
|
||||||
for (const overlay of selectedOverlays) {
|
for (const overlay of selectedOverlays) {
|
||||||
params.append('overlay', overlay);
|
params.append('overlay', overlay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Crime-type selection only matters while the crime overlay is on. Emit it
|
// Crime-type selection only matters while the crime overlay is on. Emit it
|
||||||
// only when it deviates from the default (all types selected) to keep URLs
|
// only when it deviates from the default (all types selected) to keep URLs
|
||||||
|
|
|
||||||
|
|
@ -827,6 +827,18 @@ impl PropertyData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reclaim geometric-growth slack. `address_search_token_keys` and the
|
||||||
|
// posting lists are built by repeated `push`, so their capacity rounds up
|
||||||
|
// to the next doubling step; at full UK scale (88M row-token keys, 39M
|
||||||
|
// postings) that slack is ~250MB of permanently-resident waste. These
|
||||||
|
// tables live for the whole process, so shrinking once at build time is
|
||||||
|
// a pure win with no behaviour change.
|
||||||
|
address_search_token_keys.shrink_to_fit();
|
||||||
|
for postings in address_token_index.values_mut() {
|
||||||
|
postings.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
// Keep every distinctive token: common road words ("high", "church", "station") are
|
// Keep every distinctive token: common road words ("high", "church", "station") are
|
||||||
// exactly what people search, and dropping them made those roads unsearchable while a
|
// exactly what people search, and dropping them made those roads unsearchable while a
|
||||||
// prefix fallback surfaced the wrong street ("Highbury" for "High"). The candidate scan
|
// prefix fallback surfaced the wrong street ("Highbury" for "High"). The candidate scan
|
||||||
|
|
@ -838,7 +850,10 @@ impl PropertyData {
|
||||||
.max()
|
.max()
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let address_prefix_index = build_address_prefix_index(&address_token_index);
|
let address_prefix_index = build_address_prefix_index(&address_token_index);
|
||||||
let address_search_interner = address_search_rodeo.into_reader();
|
// Resolve-only view: scoring only ever calls `.resolve(spur)`, never
|
||||||
|
// `.get(str)`, so we drop the string->key reverse map that a RodeoReader
|
||||||
|
// would keep (~tens of MB of hashmap we never touch).
|
||||||
|
let address_search_interner = address_search_rodeo.into_resolver();
|
||||||
let address_postings_count: usize = address_token_index.values().map(Vec::len).sum();
|
let address_postings_count: usize = address_token_index.values().map(Vec::len).sum();
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
tokens = address_token_index.len(),
|
tokens = address_token_index.len(),
|
||||||
|
|
@ -861,6 +876,9 @@ impl PropertyData {
|
||||||
.or_default()
|
.or_default()
|
||||||
.push(new_row as u32);
|
.push(new_row as u32);
|
||||||
}
|
}
|
||||||
|
for rows in postcode_row_index.values_mut() {
|
||||||
|
rows.shrink_to_fit();
|
||||||
|
}
|
||||||
let postcode_interner = postcode_rodeo.into_reader();
|
let postcode_interner = postcode_rodeo.into_reader();
|
||||||
|
|
||||||
let row_to_poi_metric_idx: Vec<u32> = if poi_metrics.is_empty() {
|
let row_to_poi_metric_idx: Vec<u32> = if poi_metrics.is_empty() {
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,8 @@ pub struct PropertyData {
|
||||||
/// Prefix lookup from typed address-token prefix to indexed full address tokens.
|
/// Prefix lookup from typed address-token prefix to indexed full address tokens.
|
||||||
address_prefix_index: FxHashMap<String, Vec<String>>,
|
address_prefix_index: FxHashMap<String, Vec<String>>,
|
||||||
/// Interned normalized address-search tokens used for per-row scoring.
|
/// Interned normalized address-search tokens used for per-row scoring.
|
||||||
address_search_interner: lasso::RodeoReader,
|
/// Resolve-only (no string->key reverse map): scoring only resolves keys.
|
||||||
|
address_search_interner: lasso::RodeoResolver,
|
||||||
/// Flat per-row normalized address-search token keys.
|
/// Flat per-row normalized address-search token keys.
|
||||||
address_search_token_keys: Vec<lasso::Spur>,
|
address_search_token_keys: Vec<lasso::Spur>,
|
||||||
/// Offset into `address_search_token_keys` for each row.
|
/// Offset into `address_search_token_keys` for each row.
|
||||||
|
|
@ -243,7 +244,7 @@ impl PropertyData {
|
||||||
postcode_row_index: FxHashMap::default(),
|
postcode_row_index: FxHashMap::default(),
|
||||||
address_token_index: FxHashMap::default(),
|
address_token_index: FxHashMap::default(),
|
||||||
address_prefix_index: FxHashMap::default(),
|
address_prefix_index: FxHashMap::default(),
|
||||||
address_search_interner: lasso::Rodeo::default().into_reader(),
|
address_search_interner: lasso::Rodeo::default().into_resolver(),
|
||||||
address_search_token_keys: Vec::new(),
|
address_search_token_keys: Vec::new(),
|
||||||
address_search_token_offsets: Vec::new(),
|
address_search_token_offsets: Vec::new(),
|
||||||
address_search_token_lengths: Vec::new(),
|
address_search_token_lengths: Vec::new(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue