Huge and dirty refactoring of the vector tile stuff.

This commit is contained in:
Eric van der Vlist 2023-01-24 10:37:03 +01:00
parent 36337623ca
commit 10a5f2fb18
8 changed files with 355 additions and 90 deletions

View File

@ -24,6 +24,11 @@ import osmIcons, { highlight } from './osm-icons';
import { indexOf } from 'lodash'; import { indexOf } from 'lodash';
import { getZoomInteger } from '../map/Map'; import { getZoomInteger } from '../map/Map';
import { isHighlighted } from '../map-tile-provider'; import { isHighlighted } from '../map-tile-provider';
import {
getHighlightedTagValue,
getTagValue,
} from '../map-tile-provider/MapTileProvider';
import { getCenter } from 'ol/extent';
interface StyleParameters { interface StyleParameters {
type: string; type: string;
@ -327,27 +332,35 @@ const styles = {
}, },
poi: { poi: {
getParameters: (feature: Feature) => { getParameters: (feature: Feature) => {
const klass: string = feature.get('class'); if (feature.getGeometryName() !== 'Point') {
const isHighlightedFeature = isHighlighted(feature); feature.setGeometry(
new Point(getCenter(feature.getGeometry().getExtent()))
);
}
if (getZoomInteger() < 14) {
return null;
}
const highlightedTagValue = getHighlightedTagValue(feature);
const isHighlightedFeature = !!highlightedTagValue;
if (
(isHighlightedFeature && getZoomInteger() < 14) ||
(!isHighlightedFeature && getZoomInteger() < 16)
) {
return null;
}
return { return {
name: feature.get('name'), name: feature.get('name'),
klass, poiType: isHighlightedFeature
? highlightedTagValue
: getTagValue(feature),
isHighlighted: isHighlightedFeature, isHighlighted: isHighlightedFeature,
isTextHidden: getZoomInteger() < 19, isTextHidden: getZoomInteger() < 19,
// isHidden: !isHighlighted && getZoomInteger() < 16,
isHidden:
(isHighlightedFeature && getZoomInteger() < 14) ||
(!isHighlightedFeature && getZoomInteger() < 16),
}; };
}, },
getStyle: memoize((params: any) => { getStyle: memoize((params: any) => {
// console.log({ caller: 'getStyle', params }); // console.log({ caller: 'getStyle', params });
const { isSelected, name, klass, isHidden, isTextHidden, isHighlighted } = const { name, poiType, isTextHidden, isHighlighted } = params;
params; const icon = osmIcons[poiType];
if (isHidden) {
return null;
}
const icon = osmIcons[klass];
if (icon === undefined) { if (icon === undefined) {
return undefined; return undefined;
} }
@ -389,10 +402,9 @@ const styles = {
cluster: { cluster: {
getParameters: (feature: Feature) => { getParameters: (feature: Feature) => {
const nbFeatures = feature.get('features').length; const nbFeatures = feature.get('features').length;
// console.log({ caller: 'cluster / getParameters', feature, nbFeatures }); // console.log({ caller: 'cluster / getParameters', feature, nbFeatures });
return { return {
nbFeatures, nbFeatures,
subFeature: nbFeatures === 1 ? feature.get('features')[0] : undefined,
}; };
}, },
getStyle: memoize((params: any) => { getStyle: memoize((params: any) => {
@ -427,7 +439,7 @@ export const style = (feature: Feature, resolution: number) => {
return createDefaultStyle(feature, resolution)[0]; return createDefaultStyle(feature, resolution)[0];
} }
const params = styles[type].getParameters(feature); const params = styles[type].getParameters(feature);
if (params?.isHidden) { if (params === null || params?.isHidden) {
return null; return null;
} }
// if (params.subFeature) { // if (params.subFeature) {

View File

@ -15,6 +15,12 @@ import Dialog from '../dialog';
import Tree from '../tree'; import Tree from '../tree';
import { GpxViewer } from '../gpx'; import { GpxViewer } from '../gpx';
import { Browser } from '@capacitor/browser'; import { Browser } from '@capacitor/browser';
import { isHighlighted } from '../map-tile-provider';
import {
getHighlightedTagValue,
getTagValue,
} from '../map-tile-provider/MapTileProvider';
import style from '../gpx/styles';
const Item = styled(Paper)(({ theme }) => ({ const Item = styled(Paper)(({ theme }) => ({
...theme.typography.body2, ...theme.typography.body2,
@ -198,7 +204,12 @@ const Infos: Component<{}> = (props) => {
<> <>
{console.log({ {console.log({
caller: 'Infos / vector layer feature', caller: 'Infos / vector layer feature',
highlightedTagValue:
getHighlightedTagValue(feature),
isHighlighted: isHighlighted(feature),
tagValue: getTagValue(feature),
properties: feature.getProperties(), properties: feature.getProperties(),
style: style(feature),
})} })}
</> </>
<For each={Object.keys(feature.getProperties())}> <For each={Object.keys(feature.getProperties())}>

View File

@ -24,6 +24,11 @@ import dispatch from '../../workers/dispatcher-main';
import getUri from '../../lib/ids'; import getUri from '../../lib/ids';
import { Feature } from 'ol'; import { Feature } from 'ol';
import {
overlayCategories,
overlayDefinitions,
} from '../overlays/overlay-definitions';
const id = getUri('overlays', undefined); const id = getUri('overlays', undefined);
interface TileProvider { interface TileProvider {
@ -119,49 +124,64 @@ const defaultOverlays: Overlays = {
}, },
}; };
type OverlayDefinition = Record<string, FeatureTypes>; // type OverlayDefinition = Record<string, FeatureTypes>;
type OverlayDefinitions = Record<string, OverlayDefinition>; // type OverlayDefinitions = Record<string, OverlayDefinition>;
export const overlayDefinitions: OverlayDefinitions = { // export const overlayDefinitions: OverlayDefinitions = {
none: {}, // none: {},
hiking: { // hiking: {
none: {}, // none: {},
sleeping: { // sleeping: {
tourism: '*', // tourism: [
}, // 'hotel',
drinking: { // 'alpine_hut',
amenity: ['bar', 'cafe', 'pub', 'drinking_water', 'water_point'], // 'apartment',
}, // 'camp_site',
eating: { // 'chalet',
amenity: ['fast_food', 'pub', 'restaurant'], // 'guest_house',
shop: [ // 'hostel',
'bakery', // 'motel',
'butcher', // 'wilderness_hut',
'cheese', // ],
'chocolate', // },
'convenience', // drinking: {
'dairy', // amenity: ['bar', 'cafe', 'pub', 'drinking_water', 'water_point'],
'farm', // natural: ['spring'],
'greengrocer', // },
'health_food', // eating: {
'pastry', // amenity: ['fast_food', 'pub', 'restaurant'],
'seafood', // shop: [
'department_store', // 'bakery',
'supermarket', // 'butcher',
], // 'cheese',
}, // 'chocolate',
health: { // 'convenience',
amenity: ['doctors', 'hospital', 'pharmacy'], // 'dairy',
}, // 'farm',
security: { // 'greengrocer',
amenity: ['police', 'fire_station'], // 'health_food',
}, // 'pastry',
dayToDay: { // 'seafood',
amenity: ['waste_basket', 'waste_disposal'], // 'department_store',
shop: ['laundry'], // 'supermarket',
}, // ],
}, // },
}; // health: {
// amenity: ['doctors', 'hospital', 'pharmacy'],
// },
// security: {
// amenity: ['police', 'fire_station'],
// },
// dayToday: {
// amenity: ['waste_basket', 'waste_disposal'],
// shop: ['laundry'],
// },
// naturalSites: {
// tourism: ['viewpoint'],
// natural: '*',
// },
// },
// };
const getOverlays = createCachedSignal({ const getOverlays = createCachedSignal({
id, id,
@ -177,33 +197,144 @@ const currentOverlayKey = () =>
const currentOverlay = () => const currentOverlay = () =>
getOverlays() ? getOverlays()[currentOverlayKey()] : {}; getOverlays() ? getOverlays()[currentOverlayKey()] : {};
export const currentOverlayDefinition = () => const overlayCategoriesPlusNone = { none: [], ...overlayCategories };
overlayDefinitions[currentOverlayKey()];
export const currentCategory = () =>
currentOverlayKey() === 'none'
? []
: ['none', ...overlayCategoriesPlusNone[currentOverlayKey()]];
const currentOverlayHighlightedKey = () => const currentOverlayHighlightedKey = () =>
currentOverlay() && currentOverlay().highlighted currentOverlay() && currentOverlay().highlighted
? Object.keys(currentOverlay().highlighted)[0] ? currentOverlay().highlighted
: 'none'; : 'none';
export const currentOverlayHighlightedDefinition = () => export const currentOverlayHighlightedDefinition = () =>
currentOverlayDefinition()[currentOverlayHighlightedKey()]; currentCategory()[currentOverlayHighlightedKey()];
export const highlightedTags = () => {
let result = {};
Object.keys(overlayDefinitions).forEach((tagName) => {
let tagValues = [];
const tag = overlayDefinitions[tagName];
Object.keys(tag).forEach((tagValue) => {
const catDef = tag[tagValue];
Object.keys(catDef).forEach((catName) => {
if (catName === currentOverlayKey()) {
const catValue = catDef[catName];
if (catValue[currentOverlay()?.highlighted]) {
tagValues = [...tagValues, tagValue];
}
}
});
});
if (tagValues.length > 0) {
result[tagName] = tagValues;
}
});
// console.log({
// caller: 'MapTileProviders / highlightedTags',
// result,
// currentOverlayKey: currentOverlayKey(),
// currentOverlay: currentOverlay(),
// });
return result;
};
// createEffect(() => {
// highlightedTags = {};
// Object.keys(overlayDefinitions).forEach((tagName) => {
// let tagValues = [];
// const tag = overlayDefinitions[tagName];
// Object.keys(tag).forEach((tagValue) => {
// const catDef = tag[tagValue];
// Object.keys(catDef).forEach((catName) => {
// if (catName === currentOverlayKey()) {
// const catValue = catDef[catName];
// if (catValue[currentOverlay()?.highlighted]) {
// tagValues = [...tagValues, tagValue];
// }
// }
// });
// });
// if (tagValues.length > 0) {
// highlightedTags[tagName] = tagValues;
// }
// });
// console.log({
// caller: 'MapTileProviders / createEffect',
// highlightedTags,
// currentOverlayKey: currentOverlayKey(),
// currentOverlay: currentOverlay(),
// });
// });
export const getHighlightedTagValue = (feature: Feature) => {
let result = false;
Object.keys(highlightedTags()).every((tagName) => {
const value = feature.get(tagName);
const highlightedValues = highlightedTags()[tagName];
// console.log({
// caller: 'MapTileProviders / highlightedTag / found',
// feature,
// feature_values: feature.values_,
// highlightedTags,
// tagName,
// tag_value: value,
// highlightedValues,
// });
if (value !== undefined && highlightedValues.includes(value)) {
result = value;
return false;
}
return true;
});
// console.log({
// caller: 'MapTileProviders / highlightedTag / result',
// feature,
// feature_values: feature.values_,
// highlightedTags,
// result,
// });
return result;
};
export const isHighlighted = (feature: Feature) => { export const isHighlighted = (feature: Feature) => {
const type = feature.get('sub-type'); return !!getHighlightedTagValue(feature);
const subType = feature.get('class'); };
const highlightedType = currentOverlayHighlightedDefinition()[type];
if (!highlightedType) { export const getTagValue = (feature: Feature) => {
return false; let result = false;
} Object.keys(overlayDefinitions).every((tagName) => {
const value = feature.get(tagName);
const tagValues = overlayDefinitions[tagName];
if (value !== undefined && Object.keys(tagValues).includes(value)) {
// console.log({
// caller: 'MapTileProviders / tagValue / found',
// feature,
// feature_values: feature.values_,
// highlightedTags,
// tagName,
// tag_value: value,
// highlightedValues,
// });
result = value;
return false;
}
return true;
});
// console.log({ // console.log({
// caller: 'Overlays / isHighlighted', // caller: 'MapTileProviders / isHighlighted / false',
// feature, // feature,
// type, // feature_values: feature.values_,
// subType, // highlightedTags,
// currentOverlayHighlightedDefinition: currentOverlayHighlightedDefinition(),
// highlightedType,
// }); // });
return highlightedType === '*' || highlightedType.includes(subType); return result;
}; };
const MapTilesProvider: Component<{}> = (props) => { const MapTilesProvider: Component<{}> = (props) => {
@ -235,7 +366,7 @@ const MapTilesProvider: Component<{}> = (props) => {
overlays: getOverlays(), overlays: getOverlays(),
currentOverlayKey: currentOverlayKey(), currentOverlayKey: currentOverlayKey(),
currentOverlay: currentOverlay(), currentOverlay: currentOverlay(),
currentOverlayDefinition: currentOverlayDefinition(), currentOverlayDefinition: currentCategory(),
currentOverlayHighlightedKey: currentOverlayHighlightedKey(), currentOverlayHighlightedKey: currentOverlayHighlightedKey(),
}); });
}); });
@ -265,8 +396,7 @@ const MapTilesProvider: Component<{}> = (props) => {
value, value,
}); });
const newOverlays = getOverlays(); const newOverlays = getOverlays();
newOverlays[currentOverlayKey()].highlighted = {}; newOverlays[currentOverlayKey()].highlighted = value;
newOverlays[currentOverlayKey()].highlighted[value] = [];
dispatch({ dispatch({
action: 'putOverlays', action: 'putOverlays',
params: { id, overlays: newOverlays }, params: { id, overlays: newOverlays },
@ -293,21 +423,20 @@ const MapTilesProvider: Component<{}> = (props) => {
defaultValue={currentOverlayKey()} defaultValue={currentOverlayKey()}
onChange={handleOverlayChange} onChange={handleOverlayChange}
> >
<For each={Object.keys(overlayDefinitions)}> <For each={Object.keys(overlayCategoriesPlusNone)}>
{(p: string) => ( {(p: string) => (
<FormControlLabel value={p} control={<Radio />} label={p} /> <FormControlLabel value={p} control={<Radio />} label={p} />
)} )}
</For> </For>
</RadioGroup> </RadioGroup>
<Show when={Object.keys(currentOverlayDefinition()).length > 0}> <Show when={currentCategory().length > 0}>
{' '}
<div>---</div> <div>---</div>
<div>Highlight</div> <div>Highlight</div>
<RadioGroup <RadioGroup
defaultValue={currentOverlayHighlightedKey()} defaultValue={currentOverlayHighlightedKey()}
onChange={handleOverlayHighlightChange} onChange={handleOverlayHighlightChange}
> >
<For each={Object.keys(currentOverlayDefinition())}> <For each={currentCategory()}>
{(p: string) => ( {(p: string) => (
<FormControlLabel <FormControlLabel
value={p} value={p}

View File

@ -1,7 +1,7 @@
export { export {
default, default,
mapTileProviders, mapTileProviders,
currentOverlayDefinition, currentCategory as currentOverlayDefinition,
currentOverlayHighlightedDefinition, currentOverlayHighlightedDefinition,
isHighlighted, isHighlighted,
} from './MapTileProvider'; } from './MapTileProvider';

View File

@ -49,7 +49,7 @@ import VectorTileSource from 'ol/source/VectorTile.js';
import MVT from 'ol/format/MVT.js'; import MVT from 'ol/format/MVT.js';
import style from '../gpx/styles'; import style from '../gpx/styles';
import ClusterableVectorTileSourceProxy from '../../lib/ClusterableVectorTileSourceProxy'; import ClusterableVectorTileSourceProxy from '../../lib/ClusterableVectorTileSourceProxy';
import { Overlays } from './Overlays'; import { Overlays } from '../overlays/Overlays';
const [getState, setState] = createSignal({ const [getState, setState] = createSignal({
lon: 0, lon: 0,

View File

@ -15,7 +15,8 @@ import VectorTileSource from 'ol/source/VectorTile.js';
import Cluster from 'ol/source/Cluster'; import Cluster from 'ol/source/Cluster';
import { style } from '../gpx/styles'; import { style } from '../gpx/styles';
import ClusterableVectorTileSourceProxy from '../../lib/ClusterableVectorTileSourceProxy'; import ClusterableVectorTileSourceProxy from '../../lib/ClusterableVectorTileSourceProxy';
import { getMap, getZoomInteger } from './Map'; import { getMap, getZoomInteger } from '../map/Map';
import { getCenter } from 'ol/extent';
interface Props { interface Props {
map: () => OlMap | null; map: () => OlMap | null;
@ -47,12 +48,16 @@ export const Overlays: Component<Props> = ({ map }) => {
distance: 30, distance: 30,
minDistance: 10, minDistance: 10,
geometryFunction: (feature: Feature) => { geometryFunction: (feature: Feature) => {
// console.log({ if (
// caller: 'Map / Cluster / geometryFunction', getZoomInteger() < 14 &&
// feature, feature.get('type') === 'poi' &&
// }); isHighlighted(feature)
if (getZoomInteger() < 14 && isHighlighted(feature)) { ) {
return feature.getGeometry(); // console.log({
// caller: 'Map / Cluster / geometryFunction / true',
// feature,
// });
return new Point(getCenter(feature.getGeometry().getExtent()));
} }
return null; return null;
}, },

View File

@ -0,0 +1 @@
import { overlayDefinitions, overlayCategories } from './overlay-definitions';

View File

@ -0,0 +1,107 @@
import { indexOf } from 'lodash';
export const overlayDefinitions = {
amenity: {
bar: { hiking: { drinking: true } },
fast_food: { hiking: { eating: true } },
food_court: { hiking: { eating: true } },
pub: { hiking: { eating: true } },
restaurant: { hiking: { eating: true } },
cafe: { hiking: { drinking: true } },
atm: { hiking: { money: true, dayToDay: true } },
bank: { hiking: { money: true, dayToDay: true } },
doctors: { hiking: { health: true } },
hospital: { hiking: { health: true } },
pharmacy: { hiking: { health: true } },
police: { hiking: { security: true } },
fire_station: { hiking: { security: true } },
drinking_water: { hiking: { drinking: true } },
water_point: { hiking: { drinking: true } },
waste_basket: { hiking: { dayToDay: true } },
waste_disposal: { hiking: { dayToDay: true } },
vending_machine: { hiking: { dayToDay: true } },
},
shop: {
bakery: { hiking: { eating: true } },
butcher: { hiking: { eating: true } },
cheese: { hiking: { eating: true } },
chocolate: { hiking: { eating: true } },
convenience: { hiking: { eating: true } },
dairy: { hiking: { eating: true } },
farm: { hiking: { eating: true } },
greengrocer: { hiking: { eating: true } },
health_food: { hiking: { eating: true } },
pasta: { hiking: { eating: true } },
pastry: { hiking: { eating: true } },
seafood: { hiking: { eating: true } },
water: { hiking: { drinking: true } },
department_store: { hiking: { dayToDay: true } },
general: { hiking: { dayToDay: true } },
mall: { hiking: { dayToDay: true } },
supermarket: { hiking: { eating: true } },
wholesale: { hiking: { eating: true } },
outdoor: { hiking: { material: true } },
laundry: { hiking: { dayToDay: true } },
},
tourism: {
hotel: { hiking: { sleeping: true } },
alpine_hut: { hiking: { sleeping: true } },
apartment: { hiking: { sleeping: true } },
camp_site: { hiking: { sleeping: true } },
chalet: { hiking: { sleeping: true } },
guest_house: { hiking: { sleeping: true } },
hostel: { hiking: { sleeping: true } },
motel: { hiking: { sleeping: true } },
wilderness_hut: { hiking: { sleeping: true } },
viewpoint: { hiking: { naturalSites: true } },
},
waterway: { waterfall: { hiking: { naturalSites: true } } },
natural: {
peak: { hiking: { naturalSites: true } },
cave_entrance: { hiking: { naturalSites: true } },
volcano: { hiking: { naturalSites: true } },
arch: { hiking: { naturalSites: true } },
arete: { hiking: { naturalSites: true } },
fumarole: { hiking: { naturalSites: true } },
rock: { hiking: { naturalSites: true } },
saddle: { hiking: { naturalSites: true } },
sinkhole: { hiking: { naturalSites: true } },
stone: { hiking: { naturalSites: true } },
glacier: { hiking: { naturalSites: true } },
spring: { hiking: { naturalSites: true } },
hot_spring: { hiking: { naturalSites: true } },
geyser: { hiking: { naturalSites: true } },
},
};
let _flat: any = [];
Object.values(overlayDefinitions).forEach((category) => {
Object.values(category).forEach((subCategory) => {
_flat = [..._flat, subCategory];
});
});
type Categories = { string: Set<string> };
export let overlayCategories: Categories = {};
_flat.forEach((obj: any) => {
Object.keys(obj).forEach((catName: string) => {
const catValue = obj[catName];
const previous =
overlayCategories[catName] === undefined
? []
: overlayCategories[catName];
overlayCategories[catName] = new Set([
...previous,
...Object.keys(catValue),
]);
});
});
console.log({
caller: 'overlay-definitions',
overlayDefinitions,
_flat,
overlayCategories,
});