Back to the proxy approach...
This commit is contained in:
parent
0722fca584
commit
982cb722f7
|
@ -52,6 +52,7 @@ import { DepartureBoard } from '@suid/icons-material';
|
|||
import deTileVectorSource from '../../lib/de-tile-vector-source';
|
||||
import ClusterableVectorTileSource from '../../lib/ClusterableVectorTileSource';
|
||||
import clusterableVectorTileSourceProxy from '../../lib/ClusterableVectorTileSourceProxy';
|
||||
import ClusterableVectorTileSourceProxy from '../../lib/ClusterableVectorTileSourceProxy';
|
||||
|
||||
const [getState, setState] = createSignal({
|
||||
lon: 0,
|
||||
|
@ -278,96 +279,9 @@ const Map: Component = () => {
|
|||
|
||||
// // cf https://stackoverflow.com/questions/55161380/openlayers-cluster-with-mvt-vectortilesource-impossible
|
||||
|
||||
// const vectorTileMirrorSource = new VectorSource();
|
||||
// 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({
|
||||
const vectorTileSource = new VectorTileSource({
|
||||
url: 'https://geo.dyomedea.com/services/spain/tiles/{z}/{x}/{y}.pbf',
|
||||
format: new MVT({ featureClass: Feature }),
|
||||
maxZoom: 14,
|
||||
});
|
||||
|
||||
|
@ -377,65 +291,55 @@ const Map: Component = () => {
|
|||
declutter: false,
|
||||
});
|
||||
|
||||
// const clusterableVectorTileSource =
|
||||
// clusterableVectorTileSourceProxy(vectorTileSource);
|
||||
const clusterableVectorTileSource = new ClusterableVectorTileSourceProxy({
|
||||
source: vectorTileSource,
|
||||
});
|
||||
|
||||
// clusterableVectorTileSource.init({
|
||||
// featureFilter: (feature: Feature, resolution?: number) =>
|
||||
// feature.get('type') === 'poi',
|
||||
// });
|
||||
const clusterSource = new Cluster({
|
||||
source: clusterableVectorTileSource,
|
||||
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({
|
||||
// source: vectorTileMirrorSource,
|
||||
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,
|
||||
}),
|
||||
source: clusterSource,
|
||||
zIndex: Infinity,
|
||||
// style,
|
||||
style: function (feature) {
|
||||
|
@ -445,17 +349,11 @@ const Map: Component = () => {
|
|||
// });
|
||||
return new Style({
|
||||
image: new Circle({
|
||||
radius: feature.get('features').length * 5,
|
||||
radius: 20,
|
||||
fill: new Fill({ color: 'black' }),
|
||||
}),
|
||||
});
|
||||
},
|
||||
// style: new Style({
|
||||
// image: new Circle({
|
||||
// radius: 20,
|
||||
// fill: new Fill({ color: 'black' }),
|
||||
// }),
|
||||
// }),
|
||||
});
|
||||
|
||||
olMap.addLayer(vectorTileLayer);
|
||||
|
|
|
@ -1,142 +1,106 @@
|
|||
import { cloneDeep } from 'lodash';
|
||||
import { Feature } from 'ol';
|
||||
import { equals, Extent } from 'ol/extent';
|
||||
import { fromLonLat, Projection } from 'ol/proj';
|
||||
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 {
|
||||
featureFilter?: FeatureFilter;
|
||||
source: VectorTileSource;
|
||||
}
|
||||
|
||||
const initialProps = {
|
||||
extent: null,
|
||||
extentProjected: null,
|
||||
featuresSent: false,
|
||||
featureFilter: passAllFeatureFilter,
|
||||
};
|
||||
class ClusterableVectorTileSourceProxy {
|
||||
extent?: Extent;
|
||||
resolution?: number;
|
||||
extentProjected?: Extent;
|
||||
featuresSent: boolean = false;
|
||||
proxy?: typeof Proxy;
|
||||
|
||||
const getProps = (vectorTileSource: VectorTileSource) =>
|
||||
vectorTileSource.get(CLUSTER_PROPS) as Props;
|
||||
|
||||
const setProps = (vectorTileSource: VectorTileSource, props: Props) =>
|
||||
vectorTileSource.set(CLUSTER_PROPS, props);
|
||||
|
||||
const overloads = {
|
||||
init: (target: VectorTileSource) => {
|
||||
return (options?: Options) => {
|
||||
let props = cloneDeep(initialProps);
|
||||
if (options) {
|
||||
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));
|
||||
overloads = {
|
||||
loadFeatures: (target: VectorTileSource) => {
|
||||
return (extent: Extent, resolution: number, projection: Projection) => {
|
||||
this.resolution = resolution;
|
||||
if (this.extent === undefined || !equals(this.extent, extent)) {
|
||||
this.extent = extent;
|
||||
if (target.getProjection() != null) {
|
||||
this.extentProjected = fromLonLat(
|
||||
extent.slice(0, 2),
|
||||
target.getProjection()
|
||||
).concat(fromLonLat(extent.slice(2), this.projection));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!props.featuresSent) {
|
||||
target.dispatchEvent('change');
|
||||
}
|
||||
props.featuresSent = false;
|
||||
setProps(target, props);
|
||||
// console.log({
|
||||
// caller: 'clusterableVectorTileSourceProxy',
|
||||
// overload: 'loadFeatures',
|
||||
// extent,
|
||||
// resolution,
|
||||
// projection,
|
||||
// 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;
|
||||
if (!this.featuresSent) {
|
||||
target.dispatchEvent('change');
|
||||
}
|
||||
this.featuresSent = false;
|
||||
// console.log({
|
||||
// caller: 'clusterableVectorTileSourceProxy',
|
||||
// overload: 'loadFeatures',
|
||||
// extent,
|
||||
// resolution,
|
||||
// projection,
|
||||
// target: target,
|
||||
// this,
|
||||
// });
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
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;
|
||||
|
|
Loading…
Reference in New Issue