Back to the proxy approach...

This commit is contained in:
Eric van der Vlist 2023-01-20 12:58:56 +01:00
parent 0722fca584
commit 982cb722f7
2 changed files with 145 additions and 283 deletions

View File

@ -52,6 +52,7 @@ import { DepartureBoard } from '@suid/icons-material';
import deTileVectorSource from '../../lib/de-tile-vector-source'; import deTileVectorSource from '../../lib/de-tile-vector-source';
import ClusterableVectorTileSource from '../../lib/ClusterableVectorTileSource'; import ClusterableVectorTileSource from '../../lib/ClusterableVectorTileSource';
import clusterableVectorTileSourceProxy from '../../lib/ClusterableVectorTileSourceProxy'; import clusterableVectorTileSourceProxy from '../../lib/ClusterableVectorTileSourceProxy';
import ClusterableVectorTileSourceProxy from '../../lib/ClusterableVectorTileSourceProxy';
const [getState, setState] = createSignal({ const [getState, setState] = createSignal({
lon: 0, lon: 0,
@ -278,96 +279,9 @@ const Map: Component = () => {
// // cf https://stackoverflow.com/questions/55161380/openlayers-cluster-with-mvt-vectortilesource-impossible // // cf https://stackoverflow.com/questions/55161380/openlayers-cluster-with-mvt-vectortilesource-impossible
// const vectorTileMirrorSource = new VectorSource(); const vectorTileSource = new VectorTileSource({
// let featuresForZ: Feature[] = [];
// let viewZ = vectorTileLayer
// .getSource()
// .getTileGrid()
// .getZForResolution(olMap.getView().getResolution());
// const vectorMirrorRefresh = () => {
// console.log({
// caller: 'Map / Cluster / vectorMirrorRefresh',
// olMap,
// vectorMirrorLayer,
// viewZ,
// featuresForZ,
// });
// vectorTileMirrorSource.clear();
// if (featuresForZ[viewZ]) {
// vectorTileMirrorSource.addFeatures(featuresForZ[viewZ]);
// // vectorMirrorLayer.getSource()?.refresh();
// }
// //vectorMirrorLayer.changed();
// };
// vectorTileLayer.getSource()?.on('tileloadend', (evt) => {
// const z = evt.tile.getTileCoord()[0];
// // const features = evt.tile.getFeatures();
// // features.forEach((feature: Feature) => {
// // feature.setId(undefined);
// // });
// const features = evt.tile
// .getFeatures()
// .filter((feature: Feature) => feature.get('type') === 'poi')
// .map((feature: Feature) => {
// const center = olExtent.getCenter(feature.getGeometry().getExtent());
// const centerLonLat = toLonLat(
// center,
// olMap.getView().getProjection()
// );
// const newFeature = feature.clone();
// newFeature.setGeometry(new Point(centerLonLat));
// // console.log({
// // caller: 'Map / Cluster / on tileloadend / new feature',
// // feature,
// // center,
// // centerLonLat,
// // newFeature,
// // });
// return newFeature;
// });
// if (!Array.isArray(featuresForZ[z])) {
// featuresForZ[z] = [];
// }
// featuresForZ[z] = featuresForZ[z].concat(features);
// // evt.tile.setFeatures([]);
// if (z === viewZ) {
// vectorMirrorRefresh();
// }
// console.log({
// caller: 'Map / Cluster / on tileloadend',
// olMap,
// z,
// viewZ,
// features,
// vectorMirrorLayer,
// featuresForZ,
// });
// });
// olMap.getView().on('change:resolution', function () {
// // use VT features from the tile z level corresponding to view resolution
// const newZ = vectorTileLayer
// .getSource()
// .getTileGrid()
// .getZForResolution(olMap.getView().getResolution());
// console.log({
// caller: 'Map / Cluster / on change:resolution',
// olMap,
// newZ,
// viewZ,
// vectorMirrorLayer,
// featuresForZ,
// });
// if (newZ !== viewZ) {
// viewZ = newZ;
// vectorMirrorRefresh();
// }
// });
const vectorTileSource = new ClusterableVectorTileSource({
url: 'https://geo.dyomedea.com/services/spain/tiles/{z}/{x}/{y}.pbf', url: 'https://geo.dyomedea.com/services/spain/tiles/{z}/{x}/{y}.pbf',
format: new MVT({ featureClass: Feature }),
maxZoom: 14, maxZoom: 14,
}); });
@ -377,65 +291,55 @@ const Map: Component = () => {
declutter: false, declutter: false,
}); });
// const clusterableVectorTileSource = const clusterableVectorTileSource = new ClusterableVectorTileSourceProxy({
// clusterableVectorTileSourceProxy(vectorTileSource); source: vectorTileSource,
});
// clusterableVectorTileSource.init({ const clusterSource = new Cluster({
// featureFilter: (feature: Feature, resolution?: number) => source: clusterableVectorTileSource,
// feature.get('type') === 'poi', distance: 4000000,
// }); //minDistance: 200,
geometryFunction: (feature: Feature) => {
// console.log({
// caller: 'Map / Cluster / geometryFunction',
// feature,
// });
if (feature.get('sub-type') === 'tourism') {
return new Point(
olExtent.getCenter(feature.getGeometry().getExtent())
);
// return new Point(
// toLonLat(
// olExtent.getCenter(feature.getGeometry().getExtent()),
// olMap.getView().getProjection()
// )
// );
}
return null;
},
createCluster: (point: Point, features: Feature[]) => {
// console.log({
// caller: 'Map / Cluster / createCluster',
// point,
// features,
// });
return new Feature({
geometry: new Point(
toLonLat(
olExtent.getCenter(point.getExtent()),
olMap.getView().getProjection()
)
),
// point,
features: features,
type: 'cluster',
});
},
});
let clusterLayer = new VectorLayer({ let clusterLayer = new VectorLayer({
// source: vectorTileMirrorSource, source: clusterSource,
source: new Cluster({
// source: deTileVectorSource(vectorLayer.getSource()),
source: vectorTileSource,
geometryFunction: (feature: Feature) => {
// console.log({
// caller: 'Map / Cluster / geometryFunction',
// feature,
// });
// test data is linestrings
// return new Point(
// olExtent.getCenter(feature.getGeometry().getExtent())
// );
if (feature.get('type') === 'poi') {
return new Point(
olExtent.getCenter(feature.getGeometry().getExtent())
);
// return new Point(
// toLonLat(
// olExtent.getCenter(feature.getGeometry().getExtent()),
// olMap.getView().getProjection()
// )
// );
}
return null;
},
createCluster: (point: Point, features: Feature[]) => {
// console.log({
// caller: 'Map / Cluster / createCluster',
// point,
// features,
// });
//return features[0];
return new Feature({
geometry: new Point(
toLonLat(
olExtent.getCenter(point.getExtent()),
olMap.getView().getProjection()
)
),
// point,
features: features,
type: 'cluster',
});
},
// distance: 100000,
// minDistance: 0,
}),
zIndex: Infinity, zIndex: Infinity,
// style, // style,
style: function (feature) { style: function (feature) {
@ -445,17 +349,11 @@ const Map: Component = () => {
// }); // });
return new Style({ return new Style({
image: new Circle({ image: new Circle({
radius: feature.get('features').length * 5, radius: 20,
fill: new Fill({ color: 'black' }), fill: new Fill({ color: 'black' }),
}), }),
}); });
}, },
// style: new Style({
// image: new Circle({
// radius: 20,
// fill: new Fill({ color: 'black' }),
// }),
// }),
}); });
olMap.addLayer(vectorTileLayer); olMap.addLayer(vectorTileLayer);

View File

@ -1,142 +1,106 @@
import { cloneDeep } from 'lodash';
import { Feature } from 'ol';
import { equals, Extent } from 'ol/extent'; import { equals, Extent } from 'ol/extent';
import { fromLonLat, Projection } from 'ol/proj'; import { fromLonLat, Projection } from 'ol/proj';
import VectorTileSource from 'ol/source/VectorTile.js'; import VectorTileSource from 'ol/source/VectorTile.js';
const CLUSTER_PROPS = '[cluster props]';
type FeatureFilter = (
feature: Feature,
resolution: number | undefined
) => boolean;
const passAllFeatureFilter = (feature: Feature, resolution?: number) => true;
interface Props {
extent: Extent | null;
resolution?: number;
extentProjected: Extent | null;
featuresSent: boolean;
featureFilter: FeatureFilter;
}
interface Options { interface Options {
featureFilter?: FeatureFilter; source: VectorTileSource;
} }
const initialProps = { class ClusterableVectorTileSourceProxy {
extent: null, extent?: Extent;
extentProjected: null, resolution?: number;
featuresSent: false, extentProjected?: Extent;
featureFilter: passAllFeatureFilter, featuresSent: boolean = false;
}; proxy?: typeof Proxy;
const getProps = (vectorTileSource: VectorTileSource) => overloads = {
vectorTileSource.get(CLUSTER_PROPS) as Props; loadFeatures: (target: VectorTileSource) => {
return (extent: Extent, resolution: number, projection: Projection) => {
const setProps = (vectorTileSource: VectorTileSource, props: Props) => this.resolution = resolution;
vectorTileSource.set(CLUSTER_PROPS, props); if (this.extent === undefined || !equals(this.extent, extent)) {
this.extent = extent;
const overloads = { if (target.getProjection() != null) {
init: (target: VectorTileSource) => { this.extentProjected = fromLonLat(
return (options?: Options) => { extent.slice(0, 2),
let props = cloneDeep(initialProps); target.getProjection()
if (options) { ).concat(fromLonLat(extent.slice(2), this.projection));
props = { ...props, ...options }; }
}
setProps(target, props);
};
},
loadFeatures: (target: VectorTileSource) => {
return (extent: Extent, resolution: number, projection: Projection) => {
let props = getProps(target);
props.resolution = resolution;
if (props.extent === null || !equals(props.extent, extent)) {
props.extent = extent;
if (target.getProjection() != null) {
props.extentProjected = fromLonLat(
extent.slice(0, 2),
target.getProjection()
).concat(fromLonLat(extent.slice(2), props.projection));
} }
} if (!this.featuresSent) {
if (!props.featuresSent) { target.dispatchEvent('change');
target.dispatchEvent('change'); }
} this.featuresSent = false;
props.featuresSent = false; // console.log({
setProps(target, props); // caller: 'clusterableVectorTileSourceProxy',
// console.log({ // overload: 'loadFeatures',
// caller: 'clusterableVectorTileSourceProxy', // extent,
// overload: 'loadFeatures', // resolution,
// extent, // projection,
// resolution, // target: target,
// projection, // this,
// target: target, // });
// props, };
// });
};
},
getFeatures: (target: VectorTileSource) => {
return () => {
let props = getProps(target);
const result = (
props.extentProjected !== null
? target.getFeaturesInExtent(props.extentProjected)
: []
).filter((feature) => props.featureFilter(feature, props.resolution));
//console.trace();
props.featuresSent = true;
setProps(target, props);
// console.log({
// caller: 'clusterableVectorTileSourceProxy',
// overload: 'getFeatures',
// result,
// props,
// target,
// });
return result;
};
},
addEventListener: (target: VectorTileSource) => {
return (type: string, listener: any) => {
console.log({
caller: 'clusterableVectorTileSourceProxy',
overload: 'addEventListener',
type,
listener,
target,
});
target.addEventListener(type, listener);
if (type === 'change') {
target.addEventListener('tileloadend', listener);
}
};
},
};
const clusterableVectorTileSourceProxy = (vectorTileSource: VectorTileSource) =>
new Proxy(vectorTileSource, {
get: (target, prop, receiver) => {
let result;
if (prop in overloads) {
result = overloads[prop as keyof typeof overloads](target);
} else {
result = Reflect.get(target, prop, receiver);
}
// console.log({
// caller: 'clusterableVectorTileSourceProxy',
// trap: 'get',
// target,
// prop,
// receiver,
// result,
// });
return result;
}, },
});
export default clusterableVectorTileSourceProxy; getFeatures: (target: VectorTileSource) => {
return () => {
const result =
this.extentProjected !== undefined
? target.getFeaturesInExtent(this.extentProjected)
: [];
//console.trace();
this.featuresSent = true;
// console.log({
// caller: 'clusterableVectorTileSourceProxy',
// overload: 'getFeatures',
// result,
// this,
// target,
// });
return result;
};
},
addEventListener: (target: VectorTileSource) => {
return (type: string, listener: any) => {
console.log({
caller: 'clusterableVectorTileSourceProxy',
overload: 'addEventListener',
type,
listener,
target,
});
target.addEventListener(type, listener);
if (type === 'change') {
target.addEventListener('tileloadend', listener);
}
};
},
};
constructor(options: Options) {
const { source } = options;
this.proxy = new Proxy(source, {
get: (target, prop, receiver) => {
let result;
if (prop in this.overloads) {
result = this.overloads[prop as keyof typeof this.overloads](target);
} else {
result = Reflect.get(target, prop, receiver);
}
// console.log({
// caller: 'clusterableVectorTileSourceProxy',
// trap: 'get',
// target,
// prop,
// receiver,
// result,
// });
return result;
},
});
return this.proxy;
}
}
export default ClusterableVectorTileSourceProxy;