This commit is contained in:
Andras Schmelczer 2026-02-02 21:56:35 +00:00
parent 2c613dc0d1
commit a677b9331f
28 changed files with 1647 additions and 1498 deletions

View file

@ -0,0 +1,113 @@
import type { FeatureMeta, FeatureFilters, ViewState } from '../types';
export const DEFAULT_VIEW: ViewState = {
longitude: -1.5,
latitude: 53.5,
zoom: 6,
pitch: 0,
};
export function parseUrlState(): {
viewState?: ViewState;
filters?: FeatureFilters;
poiCategories?: Set<string>;
tab?: 'pois' | 'properties' | 'area';
} {
const params = new URLSearchParams(window.location.search);
const result: ReturnType<typeof parseUrlState> = {};
const v = params.get('v');
if (v) {
const parts = v.split(',').map(Number);
if (parts.length === 3 && parts.every((n) => !isNaN(n))) {
result.viewState = {
latitude: parts[0],
longitude: parts[1],
zoom: parts[2],
pitch: 0,
};
}
}
const f = params.get('f');
if (f) {
const filters: FeatureFilters = {};
for (const segment of f.split(',')) {
const colonIdx = segment.indexOf(':');
if (colonIdx === -1) continue;
const name = segment.substring(0, colonIdx);
const rest = segment.substring(colonIdx + 1);
if (rest.includes(':')) {
const [minStr, maxStr] = rest.split(':');
const min = Number(minStr);
const max = Number(maxStr);
if (!isNaN(min) && !isNaN(max)) {
filters[name] = [min, max];
}
} else if (rest.includes('|')) {
filters[name] = rest.split('|');
} else {
filters[name] = [rest];
}
}
if (Object.keys(filters).length > 0) {
result.filters = filters;
}
}
const poi = params.get('poi');
if (poi) {
result.poiCategories = new Set(poi.split(',').filter(Boolean));
}
const tab = params.get('tab');
if (tab === 'p') result.tab = 'properties';
else if (tab === 'o') result.tab = 'pois';
else if (tab === 'a') result.tab = 'area';
return result;
}
export function stateToParams(
viewState: { latitude: number; longitude: number; zoom: number } | null,
filters: FeatureFilters,
features: FeatureMeta[],
selectedPOICategories: Set<string>,
rightPaneTab: 'pois' | 'properties' | 'area'
): URLSearchParams {
const params = new URLSearchParams();
if (viewState) {
params.set(
'v',
`${viewState.latitude.toFixed(4)},${viewState.longitude.toFixed(4)},${viewState.zoom.toFixed(1)}`
);
}
const filterEntries = Object.entries(filters);
if (filterEntries.length > 0) {
const filtersStr = filterEntries
.map(([name, value]) => {
const meta = features.find((f) => f.name === name);
if (meta?.type === 'enum') {
return `${name}:${(value as string[]).join('|')}`;
}
const [min, max] = value as [number, number];
return `${name}:${min}:${max}`;
})
.join(',');
params.set('f', filtersStr);
}
if (selectedPOICategories.size > 0) {
params.set('poi', Array.from(selectedPOICategories).join(','));
}
if (rightPaneTab === 'properties') {
params.set('tab', 'p');
} else if (rightPaneTab === 'area') {
params.set('tab', 'a');
}
return params;
}