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 style from '../gpx/styles';
|
||||
import { DepartureBoard } from '@suid/icons-material';
|
||||
import deTileVectorSource from '../../lib/de-tile-vector-source';
|
||||
import ClusterableVectorTileSource from '../../lib/ClusterableVectorTileSource';
|
||||
|
||||
const [getState, setState] = createSignal({
|
||||
lon: 0,
|
||||
|
@ -238,8 +240,7 @@ const Map: Component = () => {
|
|||
zIndex: Infinity,
|
||||
});
|
||||
|
||||
const vectorTileSource = new VectorTileSource({
|
||||
format: new MVT({ featureClass: Feature }),
|
||||
const clusterableVectorTileSource = new ClusterableVectorTileSource({
|
||||
url: 'https://geo.dyomedea.com/services/spain/tiles/{z}/{x}/{y}.pbf',
|
||||
maxZoom: 14,
|
||||
});
|
||||
|
@ -247,11 +248,11 @@ const Map: Component = () => {
|
|||
console.log({
|
||||
caller: 'Map / projections',
|
||||
vector: vectorLayer.getSource()?.getProjection(),
|
||||
vectorTile: vectorTileSource.getProjection(),
|
||||
vectorTile: clusterableVectorTileSource.getProjection(),
|
||||
});
|
||||
|
||||
const vectorTileLayer = new VectorTileLayer({
|
||||
source: vectorTileSource,
|
||||
source: clusterableVectorTileSource,
|
||||
style: [],
|
||||
declutter: false,
|
||||
});
|
||||
|
@ -262,7 +263,7 @@ const Map: Component = () => {
|
|||
zoom: +getState().zoom,
|
||||
rotation: +getState().rotation,
|
||||
}),
|
||||
layers: [tileLayer, vectorTileLayer, vectorLayer],
|
||||
layers: [tileLayer, vectorLayer, vectorTileLayer],
|
||||
target: target,
|
||||
controls: new Collection<Control>([
|
||||
new Attribution({ collapsible: true }),
|
||||
|
@ -285,114 +286,123 @@ const Map: Component = () => {
|
|||
olMap.on(['moveend'], changeListener);
|
||||
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();
|
||||
let featuresForZ: Feature[] = [];
|
||||
let viewZ = vectorTileLayer
|
||||
.getSource()
|
||||
.getTileGrid()
|
||||
.getZForResolution(olMap.getView().getResolution());
|
||||
// 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));
|
||||
// const vectorMirrorRefresh = () => {
|
||||
// console.log({
|
||||
// caller: 'Map / Cluster / on tileloadend / new feature',
|
||||
// feature,
|
||||
// center,
|
||||
// centerLonLat,
|
||||
// newFeature,
|
||||
// caller: 'Map / Cluster / vectorMirrorRefresh',
|
||||
// olMap,
|
||||
// vectorMirrorLayer,
|
||||
// viewZ,
|
||||
// featuresForZ,
|
||||
// });
|
||||
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,
|
||||
});
|
||||
});
|
||||
// vectorTileMirrorSource.clear();
|
||||
// if (featuresForZ[viewZ]) {
|
||||
// vectorTileMirrorSource.addFeatures(featuresForZ[viewZ]);
|
||||
// // vectorMirrorLayer.getSource()?.refresh();
|
||||
// }
|
||||
// //vectorMirrorLayer.changed();
|
||||
// };
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
// 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,
|
||||
// });
|
||||
// });
|
||||
|
||||
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: new Cluster({
|
||||
source: vectorTileMirrorSource,
|
||||
// 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 feature.getGeometry();
|
||||
// }
|
||||
// return null;
|
||||
// },
|
||||
// source: deTileVectorSource(vectorLayer.getSource()),
|
||||
source: clusterableVectorTileSource,
|
||||
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',
|
||||
|
@ -403,15 +413,21 @@ const Map: Component = () => {
|
|||
//return features[0];
|
||||
|
||||
return new Feature({
|
||||
geometry: point,
|
||||
geometry: new Point(
|
||||
toLonLat(
|
||||
olExtent.getCenter(point.getExtent()),
|
||||
olMap.getView().getProjection()
|
||||
)
|
||||
),
|
||||
features: features,
|
||||
type: 'cluster',
|
||||
});
|
||||
},
|
||||
distance: 100,
|
||||
minDistance: 10,
|
||||
distance: 100000,
|
||||
minDistance: 0,
|
||||
}),
|
||||
zIndex: Infinity,
|
||||
style,
|
||||
// style,
|
||||
style: function (feature) {
|
||||
// console.log({
|
||||
// caller: 'Map / Cluster / style',
|
||||
|
@ -431,19 +447,25 @@ const Map: Component = () => {
|
|||
// }),
|
||||
// }),
|
||||
});
|
||||
olMap.addLayer(vectorMirrorLayer);
|
||||
olMap.addLayer(clusterLayer);
|
||||
|
||||
|
||||
|
||||
setMap(olMap);
|
||||
|
||||
console.log({
|
||||
caller: 'Map / projections',
|
||||
olMap,
|
||||
projections: {
|
||||
map: olMap.getView().getProjection(),
|
||||
vectorSource: vectorLayer.getSource()?.getProjection(),
|
||||
vectorTileMirrorSource: vectorTileMirrorSource.getProjection(),
|
||||
vectorTileSource: vectorTileSource.getProjection(),
|
||||
clusterSource: vectorMirrorLayer.getSource()?.getProjection(),
|
||||
clusterSource: clusterLayer.getSource().getProjection(),
|
||||
clusterSourceSource: clusterLayer
|
||||
.getSource()
|
||||
?.getSource()
|
||||
.getProjection(),
|
||||
},
|
||||
});
|
||||
|
||||
setMap(olMap);
|
||||
});
|
||||
|
||||
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