Format the map
This commit is contained in:
parent
4c258018c3
commit
0fde087c3d
3 changed files with 64 additions and 29 deletions
|
|
@ -66,7 +66,9 @@ export default function App() {
|
|||
const poiAbortControllerRef = useRef<AbortController | null>(null);
|
||||
|
||||
// Hexagon properties state
|
||||
const [selectedHexagon, setSelectedHexagon] = useState<{ h3: string; resolution: number } | null>(null);
|
||||
const [selectedHexagon, setSelectedHexagon] = useState<{ h3: string; resolution: number } | null>(
|
||||
null
|
||||
);
|
||||
const [properties, setProperties] = useState<Property[]>([]);
|
||||
const [propertiesTotal, setPropertiesTotal] = useState(0);
|
||||
const [propertiesOffset, setPropertiesOffset] = useState(0);
|
||||
|
|
@ -347,9 +349,7 @@ export default function App() {
|
|||
<div className="flex border-b border-gray-200">
|
||||
<button
|
||||
className={`flex-1 p-3 ${
|
||||
rightPaneTab === 'pois'
|
||||
? 'border-b-2 border-blue-500 font-semibold'
|
||||
: 'text-gray-600'
|
||||
rightPaneTab === 'pois' ? 'border-b-2 border-blue-500 font-semibold' : 'text-gray-600'
|
||||
}`}
|
||||
onClick={() => setRightPaneTab('pois')}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ function emojiToTwemojiUrl(emoji: string): string {
|
|||
return `${TWEMOJI_BASE}${hex}.png`;
|
||||
}
|
||||
|
||||
|
||||
const INITIAL_VIEW: ViewState = {
|
||||
longitude: -1.5,
|
||||
latitude: 53.5,
|
||||
|
|
@ -43,10 +42,10 @@ const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json
|
|||
|
||||
// Gradient stops for normalized [0,1] values
|
||||
const GRADIENT: { t: number; color: [number, number, number] }[] = [
|
||||
{ t: 0, color: [46, 204, 113] }, // Green
|
||||
{ t: 0.33, color: [241, 196, 15] }, // Yellow
|
||||
{ t: 0.66, color: [231, 76, 60] }, // Red
|
||||
{ t: 1, color: [142, 68, 173] }, // Purple
|
||||
{ t: 0, color: [46, 204, 113] }, // Green
|
||||
{ t: 0.33, color: [241, 196, 15] }, // Yellow
|
||||
{ t: 0.66, color: [231, 76, 60] }, // Red
|
||||
{ t: 1, color: [142, 68, 173] }, // Purple
|
||||
];
|
||||
|
||||
function normalizedToColor(t: number): [number, number, number] {
|
||||
|
|
@ -144,7 +143,16 @@ function countToColor(t: number): [number, number, number] {
|
|||
return [r, g, b];
|
||||
}
|
||||
|
||||
export default function Map({ data, pois, onViewChange, activeFeature, dragValue, features, selectedHexagonId, onHexagonClick }: MapProps) {
|
||||
export default function Map({
|
||||
data,
|
||||
pois,
|
||||
onViewChange,
|
||||
activeFeature,
|
||||
dragValue,
|
||||
features,
|
||||
selectedHexagonId,
|
||||
onHexagonClick,
|
||||
}: MapProps) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [viewState, setViewState] = useState<ViewState>(INITIAL_VIEW);
|
||||
const [dimensions, setDimensions] = useState<Dimensions>({ width: 0, height: 0 });
|
||||
|
|
@ -180,15 +188,18 @@ export default function Map({ data, pois, onViewChange, activeFeature, dragValue
|
|||
}, []);
|
||||
|
||||
// Make place labels more legible over the colored hexagons
|
||||
const handleMapLoad = useCallback((evt: { target: MapRef['getMap'] extends () => infer M ? M : never }) => {
|
||||
const map = evt.target;
|
||||
for (const layer of map.getStyle().layers || []) {
|
||||
if (layer.type !== 'symbol') continue;
|
||||
map.setPaintProperty(layer.id, 'text-halo-color', 'rgba(255,255,255,1)');
|
||||
map.setPaintProperty(layer.id, 'text-halo-width', 2);
|
||||
map.setPaintProperty(layer.id, 'text-color', '#222');
|
||||
}
|
||||
}, []);
|
||||
const handleMapLoad = useCallback(
|
||||
(evt: { target: MapRef['getMap'] extends () => infer M ? M : never }) => {
|
||||
const map = evt.target;
|
||||
for (const layer of map.getStyle().layers || []) {
|
||||
if (layer.type !== 'symbol') continue;
|
||||
map.setPaintProperty(layer.id, 'text-halo-color', 'rgba(255,255,255,1)');
|
||||
map.setPaintProperty(layer.id, 'text-halo-width', 2);
|
||||
map.setPaintProperty(layer.id, 'text-color', '#222');
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
// Popup state for POI hover
|
||||
const [popupInfo, setPopupInfo] = useState<{
|
||||
|
|
@ -226,7 +237,9 @@ export default function Map({ data, pois, onViewChange, activeFeature, dragValue
|
|||
}, [data]);
|
||||
|
||||
// Determine color mode
|
||||
const colorFeatureMeta = activeFeature ? features.find((f) => f.name === activeFeature) || null : null;
|
||||
const colorFeatureMeta = activeFeature
|
||||
? features.find((f) => f.name === activeFeature) || null
|
||||
: null;
|
||||
|
||||
const handleHexagonClick = useCallback(
|
||||
(info: PickingInfo<HexagonData>) => {
|
||||
|
|
@ -258,7 +271,13 @@ export default function Map({ data, pois, onViewChange, activeFeature, dragValue
|
|||
const t = (c - countRange.min) / (countRange.max - countRange.min);
|
||||
return countToColor(Math.max(0, Math.min(1, t)));
|
||||
},
|
||||
getLineColor: (d) => (d.h3 === selectedHexagonId ? [255, 255, 255, 255] : [0, 0, 0, 0]) as [number, number, number, number],
|
||||
getLineColor: (d) =>
|
||||
(d.h3 === selectedHexagonId ? [255, 255, 255, 255] : [0, 0, 0, 0]) as [
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
],
|
||||
getLineWidth: (d) => (d.h3 === selectedHexagonId ? 2 : 0),
|
||||
lineWidthUnits: 'pixels',
|
||||
updateTriggers: {
|
||||
|
|
@ -290,7 +309,17 @@ export default function Map({ data, pois, onViewChange, activeFeature, dragValue
|
|||
onHover: handlePoiHover,
|
||||
}),
|
||||
],
|
||||
[data, pois, handlePoiHover, handleHexagonClick, activeFeature, dragValue, countRange, colorFeatureMeta, selectedHexagonId]
|
||||
[
|
||||
data,
|
||||
pois,
|
||||
handlePoiHover,
|
||||
handleHexagonClick,
|
||||
activeFeature,
|
||||
dragValue,
|
||||
countRange,
|
||||
colorFeatureMeta,
|
||||
selectedHexagonId,
|
||||
]
|
||||
);
|
||||
|
||||
const getTooltip = useCallback(
|
||||
|
|
@ -305,8 +334,14 @@ export default function Map({ data, pois, onViewChange, activeFeature, dragValue
|
|||
const minVal = hex[`min_${f.name}`];
|
||||
const maxVal = hex[`max_${f.name}`];
|
||||
if (minVal != null && maxVal != null) {
|
||||
const minStr = typeof minVal === 'number' ? minVal.toLocaleString(undefined, { maximumFractionDigits: 1 }) : String(minVal);
|
||||
const maxStr = typeof maxVal === 'number' ? maxVal.toLocaleString(undefined, { maximumFractionDigits: 1 }) : String(maxVal);
|
||||
const minStr =
|
||||
typeof minVal === 'number'
|
||||
? minVal.toLocaleString(undefined, { maximumFractionDigits: 1 })
|
||||
: String(minVal);
|
||||
const maxStr =
|
||||
typeof maxVal === 'number'
|
||||
? maxVal.toLocaleString(undefined, { maximumFractionDigits: 1 })
|
||||
: String(maxVal);
|
||||
const highlight = f.name === activeFeature ? 'font-weight: bold;' : '';
|
||||
lines.push(`<div style="${highlight}">${f.label}: ${minStr} - ${maxStr}</div>`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,9 +31,7 @@ export function PropertiesPane({
|
|||
case 'size':
|
||||
return ((b.total_floor_area as number) || 0) - ((a.total_floor_area as number) || 0);
|
||||
case 'energy':
|
||||
return (a.current_energy_rating || 'Z').localeCompare(
|
||||
b.current_energy_rating || 'Z'
|
||||
);
|
||||
return (a.current_energy_rating || 'Z').localeCompare(b.current_energy_rating || 'Z');
|
||||
}
|
||||
});
|
||||
}, [properties, sortBy]);
|
||||
|
|
@ -142,7 +140,8 @@ function PropertyCard({ property }: { property: Property }) {
|
|||
)}
|
||||
{property.total_floor_area && (
|
||||
<div>
|
||||
<span className="text-gray-600">Area:</span> {formatNumber(property.total_floor_area as number)}m²
|
||||
<span className="text-gray-600">Area:</span>{' '}
|
||||
{formatNumber(property.total_floor_area as number)}m²
|
||||
</div>
|
||||
)}
|
||||
{property.number_habitable_rooms && (
|
||||
|
|
@ -163,7 +162,8 @@ function PropertyCard({ property }: { property: Property }) {
|
|||
)}
|
||||
{property.construction_age_band !== undefined && (
|
||||
<div>
|
||||
<span className="text-gray-600">Built (age):</span> {formatNumber(property.construction_age_band as number)}
|
||||
<span className="text-gray-600">Built (age):</span>{' '}
|
||||
{formatNumber(property.construction_age_band as number)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue