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

View File

@ -15,6 +15,12 @@ import Dialog from '../dialog';
import Tree from '../tree';
import { GpxViewer } from '../gpx';
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 }) => ({
...theme.typography.body2,
@ -198,7 +204,12 @@ const Infos: Component<{}> = (props) => {
<>
{console.log({
caller: 'Infos / vector layer feature',
highlightedTagValue:
getHighlightedTagValue(feature),
isHighlighted: isHighlighted(feature),
tagValue: getTagValue(feature),
properties: feature.getProperties(),
style: style(feature),
})}
</>
<For each={Object.keys(feature.getProperties())}>

View File

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

View File

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

View File

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

View File

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