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 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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue