Last night

This commit is contained in:
Andras Schmelczer 2026-02-08 10:21:37 +00:00
parent 2906b01734
commit 42ee2d4c51
47 changed files with 848 additions and 478 deletions

View file

@ -14,7 +14,9 @@ import type {
ViewChangeParams,
POI,
FeatureMeta,
Bounds,
} from '../../types';
import { cellToLatLng } from 'h3-js';
import {
GRADIENT,
normalizedToColor,
@ -63,6 +65,7 @@ interface MapProps {
filters?: FeatureFilters;
searchedPostcode?: SearchedPostcode | null;
onPostcodeSearched?: (postcode: SearchedPostcode | null) => void;
bounds?: Bounds | null;
}
@ -114,6 +117,7 @@ export default memo(function Map({
filters = {},
searchedPostcode,
onPostcodeSearched,
bounds: viewportBounds,
}: MapProps) {
const containerRef = useRef<HTMLDivElement>(null);
const [viewState, setViewState] = useState<ViewState>(initialViewState || INITIAL_VIEW_STATE);
@ -199,13 +203,18 @@ export default memo(function Map({
let min = Infinity;
let max = -Infinity;
for (const d of data) {
if (viewportBounds) {
const [lat, lng] = cellToLatLng(d.h3);
if (lat < viewportBounds.south || lat > viewportBounds.north || lng < viewportBounds.west || lng > viewportBounds.east) continue;
}
const c = d.count as number;
if (c < min) min = c;
if (c > max) max = c;
}
if (min === Infinity) return { min: 0, max: 1 };
if (min === max) return { min, max: min + 1 };
return { min, max };
}, [data]);
}, [data, viewportBounds]);
const colorFeatureMeta = useMemo(
() => (viewFeature ? features.find((f) => f.name === viewFeature) || null : null),
@ -259,13 +268,18 @@ export default memo(function Map({
let min = Infinity;
let max = -Infinity;
for (const d of postcodeData) {
if (viewportBounds) {
const [lng, lat] = d.properties.centroid as [number, number];
if (lat < viewportBounds.south || lat > viewportBounds.north || lng < viewportBounds.west || lng > viewportBounds.east) continue;
}
const c = d.properties.count;
if (c < min) min = c;
if (c > max) max = c;
}
if (min === Infinity) return { min: 0, max: 1 };
if (min === max) return { min, max: min + 1 };
return { min, max };
}, [postcodeData]);
}, [postcodeData, viewportBounds]);
const postcodeCountRangeRef = useRef(postcodeCountRange);
postcodeCountRangeRef.current = postcodeCountRange;
@ -324,7 +338,7 @@ export default memo(function Map({
const cfm = colorFeatureMetaRef.current;
const dark = isDarkRef.current;
if (vf && clr && cfm) {
const val = d[`min_${vf}`];
const val = d[`avg_${vf}`] ?? d[`min_${vf}`];
if (val == null) return (dark ? [80, 70, 65, 80] : [128, 128, 128, 80]) as [number, number, number, number];
if (fr) {
const minVal = d[`min_${vf}`] as number;
@ -392,7 +406,7 @@ export default memo(function Map({
const cfm = colorFeatureMetaRef.current;
const dark = isDarkRef.current;
if (vf && clr && cfm) {
const val = d[`min_${vf}`];
const val = d[`avg_${vf}`] ?? d[`min_${vf}`];
if (val == null) return (dark ? [80, 70, 65, 80] : [128, 128, 128, 80]) as [number, number, number, number];
if (fr) {
const minVal = d[`min_${vf}`] as number;
@ -402,15 +416,15 @@ export default memo(function Map({
}
}
const range = clr[1] - clr[0];
if (range === 0) return [...GRADIENT[0].color, 255] as [number, number, number, number];
if (range === 0) return [...GRADIENT[0].color, 180] as [number, number, number, number];
const t = ((val as number) - clr[0]) / range;
const rgb = normalizedToColor(Math.max(0, Math.min(1, t)));
return [...rgb, 255] as [number, number, number, number];
return [...rgb, 180] as [number, number, number, number];
}
const cr = postcodeCountRangeRef.current;
const c = d.count;
const t = (c - cr.min) / (cr.max - cr.min);
return [...countToColor(Math.max(0, Math.min(1, t)), densityGradientRef.current), 255] as [
return [...countToColor(Math.max(0, Math.min(1, t)), densityGradientRef.current), 180] as [
number,
number,
number,
@ -442,6 +456,8 @@ export default memo(function Map({
pickable: true,
onClick: handlePostcodeClick,
onHover: handlePostcodeHoverCallback,
// @ts-expect-error beforeId is a MapboxOverlay interleave prop, not typed in LayerProps
beforeId: 'landuse_park',
}),
[postcodeData, postcodeColorTrigger, handlePostcodeClick, handlePostcodeHoverCallback]
);
@ -572,22 +588,9 @@ export default memo(function Map({
) : (
<>
<PostcodeSearch onFlyTo={handleFlyTo} onPostcodeSearched={onPostcodeSearched} />
{viewSource === 'eye' && viewFeature && (
<div className="absolute top-4 left-1/2 -translate-x-1/2 z-20 flex items-center gap-3 bg-white dark:bg-warm-800 rounded-lg shadow-lg px-5 py-3">
<span className="text-lg font-semibold text-navy-950 dark:text-white">
Previewing &ldquo;{viewFeature}&rdquo;
</span>
<button
onClick={onCancelPin}
className="px-4 py-1.5 rounded-md bg-warm-200 dark:bg-warm-700 text-warm-700 dark:text-white hover:bg-warm-300 dark:hover:bg-warm-600 font-medium text-sm"
>
Cancel
</button>
</div>
)}
{viewFeature && colorRange && colorFeatureMeta ? (
<MapLegend
featureLabel={colorFeatureMeta.name}
featureLabel={viewSource === 'eye' ? `Previewing \u201c${colorFeatureMeta.name}\u201d` : colorFeatureMeta.name}
range={colorRange}
showCancel={viewSource === 'eye'}
onCancel={onCancelPin}
@ -598,7 +601,7 @@ export default memo(function Map({
) : (
<MapLegend
featureLabel="Property density"
range={[0, 0]}
range={usePostcodeView ? [postcodeCountRange.min, postcodeCountRange.max] : [countRange.min, countRange.max]}
showCancel={false}
onCancel={onCancelPin}
mode="density"