Still working on clustering.
This commit is contained in:
parent
fce0b54574
commit
66fc287e5c
10
cSpell.json
10
cSpell.json
|
@ -1,3 +1,11 @@
|
||||||
{
|
{
|
||||||
"overrides": [{ "filename": "**/fr.*", "language": "fr" }]
|
"overrides": [
|
||||||
|
{
|
||||||
|
"filename": "**/fr.*",
|
||||||
|
"language": "fr"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"words": [
|
||||||
|
"Clusterable"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,8 @@ 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 { DepartureBoard } from '@suid/icons-material';
|
import { DepartureBoard } from '@suid/icons-material';
|
||||||
|
import deTileVectorSource from '../../lib/de-tile-vector-source';
|
||||||
|
import ClusterableVectorTileSource from '../../lib/ClusterableVectorTileSource';
|
||||||
|
|
||||||
const [getState, setState] = createSignal({
|
const [getState, setState] = createSignal({
|
||||||
lon: 0,
|
lon: 0,
|
||||||
|
@ -238,8 +240,7 @@ const Map: Component = () => {
|
||||||
zIndex: Infinity,
|
zIndex: Infinity,
|
||||||
});
|
});
|
||||||
|
|
||||||
const vectorTileSource = new VectorTileSource({
|
const clusterableVectorTileSource = new ClusterableVectorTileSource({
|
||||||
format: new MVT({ featureClass: Feature }),
|
|
||||||
url: 'https://geo.dyomedea.com/services/spain/tiles/{z}/{x}/{y}.pbf',
|
url: 'https://geo.dyomedea.com/services/spain/tiles/{z}/{x}/{y}.pbf',
|
||||||
maxZoom: 14,
|
maxZoom: 14,
|
||||||
});
|
});
|
||||||
|
@ -247,11 +248,11 @@ const Map: Component = () => {
|
||||||
console.log({
|
console.log({
|
||||||
caller: 'Map / projections',
|
caller: 'Map / projections',
|
||||||
vector: vectorLayer.getSource()?.getProjection(),
|
vector: vectorLayer.getSource()?.getProjection(),
|
||||||
vectorTile: vectorTileSource.getProjection(),
|
vectorTile: clusterableVectorTileSource.getProjection(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const vectorTileLayer = new VectorTileLayer({
|
const vectorTileLayer = new VectorTileLayer({
|
||||||
source: vectorTileSource,
|
source: clusterableVectorTileSource,
|
||||||
style: [],
|
style: [],
|
||||||
declutter: false,
|
declutter: false,
|
||||||
});
|
});
|
||||||
|
@ -262,7 +263,7 @@ const Map: Component = () => {
|
||||||
zoom: +getState().zoom,
|
zoom: +getState().zoom,
|
||||||
rotation: +getState().rotation,
|
rotation: +getState().rotation,
|
||||||
}),
|
}),
|
||||||
layers: [tileLayer, vectorTileLayer, vectorLayer],
|
layers: [tileLayer, vectorLayer, vectorTileLayer],
|
||||||
target: target,
|
target: target,
|
||||||
controls: new Collection<Control>([
|
controls: new Collection<Control>([
|
||||||
new Attribution({ collapsible: true }),
|
new Attribution({ collapsible: true }),
|
||||||
|
@ -285,114 +286,123 @@ const Map: Component = () => {
|
||||||
olMap.on(['moveend'], changeListener);
|
olMap.on(['moveend'], changeListener);
|
||||||
olMap.on(['singleclick'], clickHandler);
|
olMap.on(['singleclick'], clickHandler);
|
||||||
|
|
||||||
// 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 vectorTileMirrorSource = new VectorSource();
|
||||||
let featuresForZ: Feature[] = [];
|
// let featuresForZ: Feature[] = [];
|
||||||
let viewZ = vectorTileLayer
|
// let viewZ = vectorTileLayer
|
||||||
.getSource()
|
// .getSource()
|
||||||
.getTileGrid()
|
// .getTileGrid()
|
||||||
.getZForResolution(olMap.getView().getResolution());
|
// .getZForResolution(olMap.getView().getResolution());
|
||||||
|
|
||||||
const vectorMirrorRefresh = () => {
|
// 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({
|
// console.log({
|
||||||
// caller: 'Map / Cluster / on tileloadend / new feature',
|
// caller: 'Map / Cluster / vectorMirrorRefresh',
|
||||||
// feature,
|
// olMap,
|
||||||
// center,
|
// vectorMirrorLayer,
|
||||||
// centerLonLat,
|
// viewZ,
|
||||||
// newFeature,
|
// featuresForZ,
|
||||||
// });
|
// });
|
||||||
return newFeature;
|
// vectorTileMirrorSource.clear();
|
||||||
});
|
// if (featuresForZ[viewZ]) {
|
||||||
if (!Array.isArray(featuresForZ[z])) {
|
// vectorTileMirrorSource.addFeatures(featuresForZ[viewZ]);
|
||||||
featuresForZ[z] = [];
|
// // vectorMirrorLayer.getSource()?.refresh();
|
||||||
}
|
// }
|
||||||
featuresForZ[z] = featuresForZ[z].concat(features);
|
// //vectorMirrorLayer.changed();
|
||||||
// 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 () {
|
// vectorTileLayer.getSource()?.on('tileloadend', (evt) => {
|
||||||
// use VT features from the tile z level corresponding to view resolution
|
// const z = evt.tile.getTileCoord()[0];
|
||||||
const newZ = vectorTileLayer
|
// // const features = evt.tile.getFeatures();
|
||||||
.getSource()
|
// // features.forEach((feature: Feature) => {
|
||||||
.getTileGrid()
|
// // feature.setId(undefined);
|
||||||
.getZForResolution(olMap.getView().getResolution());
|
// // });
|
||||||
console.log({
|
// const features = evt.tile
|
||||||
caller: 'Map / Cluster / on change:resolution',
|
// .getFeatures()
|
||||||
olMap,
|
// .filter((feature: Feature) => feature.get('type') === 'poi')
|
||||||
newZ,
|
// .map((feature: Feature) => {
|
||||||
viewZ,
|
// const center = olExtent.getCenter(feature.getGeometry().getExtent());
|
||||||
vectorMirrorLayer,
|
// const centerLonLat = toLonLat(
|
||||||
featuresForZ,
|
// center,
|
||||||
});
|
// olMap.getView().getProjection()
|
||||||
if (newZ !== viewZ) {
|
// );
|
||||||
viewZ = newZ;
|
// const newFeature = feature.clone();
|
||||||
vectorMirrorRefresh();
|
// 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,
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
let vectorMirrorLayer = new VectorLayer({
|
// 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();
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
let clusterLayer = new VectorLayer({
|
||||||
// source: vectorTileMirrorSource,
|
// source: vectorTileMirrorSource,
|
||||||
source: new Cluster({
|
source: new Cluster({
|
||||||
source: vectorTileMirrorSource,
|
// source: deTileVectorSource(vectorLayer.getSource()),
|
||||||
// geometryFunction: (feature: Feature) => {
|
source: clusterableVectorTileSource,
|
||||||
// // console.log({
|
geometryFunction: (feature: Feature) => {
|
||||||
// // caller: 'Map / Cluster / geometryFunction',
|
// console.log({
|
||||||
// // feature,
|
// caller: 'Map / Cluster / geometryFunction',
|
||||||
// // });
|
// feature,
|
||||||
// // test data is linestrings
|
// });
|
||||||
// // return new Point(
|
// test data is linestrings
|
||||||
// // olExtent.getCenter(feature.getGeometry().getExtent())
|
// return new Point(
|
||||||
// // );
|
// olExtent.getCenter(feature.getGeometry().getExtent())
|
||||||
// if (feature.get('type') === 'poi') {
|
// );
|
||||||
// return feature.getGeometry();
|
if (feature.get('type') === 'poi') {
|
||||||
// }
|
return new Point(
|
||||||
// return null;
|
olExtent.getCenter(feature.getGeometry().getExtent())
|
||||||
// },
|
);
|
||||||
|
// return new Point(
|
||||||
|
// toLonLat(
|
||||||
|
// olExtent.getCenter(feature.getGeometry().getExtent()),
|
||||||
|
// olMap.getView().getProjection()
|
||||||
|
// )
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
createCluster: (point: Point, features: Feature[]) => {
|
createCluster: (point: Point, features: Feature[]) => {
|
||||||
// console.log({
|
// console.log({
|
||||||
// caller: 'Map / Cluster / createCluster',
|
// caller: 'Map / Cluster / createCluster',
|
||||||
|
@ -403,15 +413,21 @@ const Map: Component = () => {
|
||||||
//return features[0];
|
//return features[0];
|
||||||
|
|
||||||
return new Feature({
|
return new Feature({
|
||||||
geometry: point,
|
geometry: new Point(
|
||||||
|
toLonLat(
|
||||||
|
olExtent.getCenter(point.getExtent()),
|
||||||
|
olMap.getView().getProjection()
|
||||||
|
)
|
||||||
|
),
|
||||||
features: features,
|
features: features,
|
||||||
|
type: 'cluster',
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
distance: 100,
|
distance: 100000,
|
||||||
minDistance: 10,
|
minDistance: 0,
|
||||||
}),
|
}),
|
||||||
zIndex: Infinity,
|
zIndex: Infinity,
|
||||||
style,
|
// style,
|
||||||
style: function (feature) {
|
style: function (feature) {
|
||||||
// console.log({
|
// console.log({
|
||||||
// caller: 'Map / Cluster / style',
|
// caller: 'Map / Cluster / style',
|
||||||
|
@ -431,19 +447,25 @@ const Map: Component = () => {
|
||||||
// }),
|
// }),
|
||||||
// }),
|
// }),
|
||||||
});
|
});
|
||||||
olMap.addLayer(vectorMirrorLayer);
|
olMap.addLayer(clusterLayer);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setMap(olMap);
|
||||||
|
|
||||||
console.log({
|
console.log({
|
||||||
caller: 'Map / projections',
|
caller: 'Map / projections',
|
||||||
olMap,
|
olMap,
|
||||||
|
projections: {
|
||||||
map: olMap.getView().getProjection(),
|
map: olMap.getView().getProjection(),
|
||||||
vectorSource: vectorLayer.getSource()?.getProjection(),
|
vectorSource: vectorLayer.getSource()?.getProjection(),
|
||||||
vectorTileMirrorSource: vectorTileMirrorSource.getProjection(),
|
clusterSource: clusterLayer.getSource().getProjection(),
|
||||||
vectorTileSource: vectorTileSource.getProjection(),
|
clusterSourceSource: clusterLayer
|
||||||
clusterSource: vectorMirrorLayer.getSource()?.getProjection(),
|
.getSource()
|
||||||
|
?.getSource()
|
||||||
|
.getProjection(),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
setMap(olMap);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
import { Feature } from 'ol';
|
||||||
|
import { equals, Extent } from 'ol/extent';
|
||||||
|
import MVT from 'ol/format/MVT';
|
||||||
|
import { fromLonLat, Projection } from 'ol/proj';
|
||||||
|
import VectorTileSource from 'ol/source/VectorTile.js';
|
||||||
|
|
||||||
|
class ClusterableVectorTileSource extends VectorTileSource {
|
||||||
|
extent: Extent | null = null;
|
||||||
|
extentProjected: Extent | null = null;
|
||||||
|
featuresSent: boolean = false;
|
||||||
|
|
||||||
|
changeHandler = () => {
|
||||||
|
this.featuresSent = false;
|
||||||
|
console.log({
|
||||||
|
caller: 'ClusterableVectorTileSource',
|
||||||
|
method: 'changeHandler',
|
||||||
|
this: this,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(properties: any) {
|
||||||
|
properties.format = new MVT({ featureClass: Feature });
|
||||||
|
super(properties);
|
||||||
|
this.addEventListener('change', this.changeHandler);
|
||||||
|
console.log({
|
||||||
|
caller: 'ClusterableVectorTileSource',
|
||||||
|
method: 'constructor',
|
||||||
|
this: this,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadFeatures = (
|
||||||
|
extent: Extent,
|
||||||
|
resolution: number,
|
||||||
|
projection: Projection
|
||||||
|
) => {
|
||||||
|
console.log({
|
||||||
|
caller: 'ClusterableVectorTileSource',
|
||||||
|
method: 'loadFeatures',
|
||||||
|
extent,
|
||||||
|
resolution,
|
||||||
|
projection,
|
||||||
|
this: this,
|
||||||
|
});
|
||||||
|
if (this.extent === null || !equals(this.extent, extent)) {
|
||||||
|
this.extent = extent;
|
||||||
|
if (this.projection != null) {
|
||||||
|
this.extentProjected = fromLonLat(
|
||||||
|
extent.slice(0, 2),
|
||||||
|
this.projection
|
||||||
|
).concat(fromLonLat(extent.slice(2), this.projection));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this.featuresSent) {
|
||||||
|
super.dispatchEvent('change');
|
||||||
|
}
|
||||||
|
this.featuresSent = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
getFeatures = () => {
|
||||||
|
const result =
|
||||||
|
this.extentProjected !== null
|
||||||
|
? super.getFeaturesInExtent(this.extentProjected)
|
||||||
|
: [];
|
||||||
|
console.log({
|
||||||
|
caller: 'ClusterableVectorTileSource',
|
||||||
|
method: 'getFeatures',
|
||||||
|
result,
|
||||||
|
this: this,
|
||||||
|
});
|
||||||
|
//console.trace();
|
||||||
|
this.featuresSent = true;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
getFeaturesInExtent = (extent: Extent) => {
|
||||||
|
const features = super.getFeaturesInExtent(extent);
|
||||||
|
console.log({
|
||||||
|
caller: 'ClusterableVectorTileSource',
|
||||||
|
method: 'getFeaturesInExtent',
|
||||||
|
extent,
|
||||||
|
features,
|
||||||
|
this: this,
|
||||||
|
});
|
||||||
|
return features;
|
||||||
|
// if (this.projection === null) {
|
||||||
|
// return super.getFeaturesInExtent(extent);
|
||||||
|
// }
|
||||||
|
// return super.getFeaturesInExtent(
|
||||||
|
// (this.extentProjected = fromLonLat(
|
||||||
|
// extent.slice(0, 2),
|
||||||
|
// this.projection
|
||||||
|
// ).concat(fromLonLat(extent.slice(2), this.projection)))
|
||||||
|
// );
|
||||||
|
};
|
||||||
|
|
||||||
|
addEventListener = (type: string, listener: any) => {
|
||||||
|
console.log({
|
||||||
|
caller: 'ClusterableVectorTileSource',
|
||||||
|
method: 'addEventListener',
|
||||||
|
type,
|
||||||
|
listener,
|
||||||
|
this: this,
|
||||||
|
});
|
||||||
|
super.addEventListener(type, listener);
|
||||||
|
if (type === 'change') {
|
||||||
|
super.addEventListener('tileloadend', listener);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ClusterableVectorTileSource;
|
Loading…
Reference in New Issue