Skip to content

Commit

Permalink
Refactor cluster styles and fix shadowed clusters issue
Browse files Browse the repository at this point in the history
  • Loading branch information
igoroctaviano committed May 10, 2024
1 parent 6e977d4 commit b5cfdd7
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 89 deletions.
140 changes: 51 additions & 89 deletions src/clusterStyles.js
Original file line number Diff line number Diff line change
@@ -1,120 +1,82 @@
import Feature from "ol/Feature.js"
import { Circle as CircleStyle, Fill, Stroke, Style, Text } from "ol/style.js"
import { createEmpty, extend, getHeight, getWidth } from 'ol/extent.js';
import Feature from 'ol/Feature.js'
import { Circle as CircleStyle, Fill, Stroke, Style, Text } from 'ol/style.js'
import { createEmpty, extend, getHeight, getWidth } from 'ol/extent.js'

const circleDistanceMultiplier = 1
const circleFootSeparation = 28
const circleStartAngle = Math.PI / 2
const textFill = new Fill({
color: "#fff",
})
const textFill = new Fill({ color: '#fff' })
const textStroke = new Stroke({
color: "rgba(0, 0, 0, 0.6)",
color: 'rgba(0, 0, 0, 0.6)',
width: 3,
})
const CIRCLE_RADIUS = 14

/**
* Single feature style, users for clusters with 1 feature and cluster circles.
* @param {Feature} clusterMember A feature from a cluster.
* @return {Style} An icon style for the cluster member's location.
*/
export function clusterMemberStyle(clusterMember) {
return new Style({
geometry: clusterMember.getGeometry(),
})
return new Style({ geometry: clusterMember.getGeometry() })
}

/**
* From
* https://github.com/Leaflet/Leaflet.markercluster/blob/31360f2/src/MarkerCluster.Spiderfier.js#L55-L72
* Arranges points in a circle around the cluster center, with a line pointing from the center to
* each point.
* @param {number} count Number of cluster members.
* @param {Array<number>} clusterCenter Center coordinate of the cluster.
* @param {number} resolution Current view resolution.
* @return {Array<Array<number>>} An array of coordinates representing the cluster members.
*/
export function generatePointsCircle(count, clusterCenter, resolution) {
const circumference =
circleDistanceMultiplier * circleFootSeparation * (2 + count)
let legLength = circumference / (Math.PI * 2) //radius from circumference
const angleStep = (Math.PI * 2) / count
const res = []
let angle

legLength = Math.max(legLength, 35) * resolution // Minimum distance to get outside the cluster icon.

for (let i = 0; i < count; ++i) {
// Clockwise, like spiral.
angle = circleStartAngle + i * angleStep
res.push([
clusterCenter[0] + legLength * Math.cos(angle),
clusterCenter[1] + legLength * Math.sin(angle),
])
}

return res
}
const outerCircleFill = new Fill()
const innerCircleFill = new Fill()
const innerCircle = new CircleStyle({
radius: CIRCLE_RADIUS,
fill: innerCircleFill,
})
const outerCircle = new CircleStyle({ fill: outerCircleFill })
const outerCircleStyle = new Style({ image: outerCircle })
const textStyle = new Text({
fill: textFill,
stroke: textStroke,
})
const innerCircleStyle = new Style({
image: innerCircle,
text: textStyle,
})

export function getClusterStyleFunc({ color, opacity }, clusterSource) {
const outerCircleFill = new Fill({
color: `rgba(${color[0]}, ${color[1]}, ${color[2]}, 0.3)`,
})
const innerCircleFill = new Fill({
color: `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${opacity})`,
})

let maxFeatureCount, currentResolution;
outerCircleFill.setColor(`rgba(${color[0]}, ${color[1]}, ${color[2]}, 0.3)`)
innerCircleFill.setColor(`rgba(${color[0]}, ${color[1]}, ${color[2]}, ${opacity})`)
innerCircle.setFill(innerCircleFill)

const calculateClusterInfo = function (resolution) {
maxFeatureCount = 0;
const updateClustersRadius = (resolution) => {
const features = clusterSource.getFeatures();
let feature, radius;

for (let i = features.length - 1; i >= 0; --i) {
feature = features[i];
const feature = features[i]
const originalFeatures = feature.get('features');
const extent = createEmpty();
let j, jj;
const extent = createEmpty()

let j, jj
for (j = 0, jj = originalFeatures.length; j < jj; ++j) {
extend(extent, originalFeatures[j].getGeometry().getExtent());
extend(extent, originalFeatures[j].getGeometry().getExtent())
}
maxFeatureCount = Math.max(maxFeatureCount, jj);
radius = (0.15 * (getWidth(extent) + getHeight(extent))) / resolution;
feature.set('radius', radius);

const radius = (0.15 * (getWidth(extent) + getHeight(extent))) / resolution
feature.set('radius', radius)
}
};
}

/** Style Function */
let currentResolution
return (feature, resolution) => {
if (resolution != currentResolution) {
calculateClusterInfo(resolution);
currentResolution = resolution;
const features = feature.get('features')

if (resolution !== currentResolution) {
updateClustersRadius(resolution)
currentResolution = resolution
}

const size = feature.get("features").length
const size = features.length
if (size > 0) {
const innerCircle = new CircleStyle({
radius: 14,
fill: innerCircleFill,
})
const outerCircle = new CircleStyle({
radius: feature.get('radius'),
fill: outerCircleFill,
})

return [
new Style({ image: outerCircle }),
new Style({
image: innerCircle,
text: new Text({
text: size.toString(),
fill: textFill,
stroke: textStroke,
}),
}),
]
outerCircle.setRadius(feature.get('radius'))
outerCircleStyle.setImage(outerCircle)
textStyle.setText(size.toString())
return [ outerCircleStyle, innerCircleStyle ]
}
const originalFeature = feature.get("features")[0]

const originalFeature = features[0]
return clusterMemberStyle(originalFeature)
};
}
}
4 changes: 4 additions & 0 deletions src/viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3665,13 +3665,15 @@ class VolumeImageViewer {
: new VectorLayer({
source: highResSource,
style: this.getGraphicTypeLayerStyle(annotationGroup),
extent: this[_pyramid].extent,
})
}

const highResLayer = getHighResLayer({ pointsSource, highResSource, annotationGroup })
const lowResLayer = numberOfAnnotations > 1000 ? new VectorLayer({
source: clustersSource,
style: getClusterStyleFunc(annotationGroup.style, clustersSource),
extent: this[_pyramid].extent,
}) : getHighResLayer({ pointsSource, highResSource, annotationGroup })

annotationGroup.layers = []
Expand Down Expand Up @@ -4088,6 +4090,7 @@ class VolumeImageViewer {
source: prevLayer.getSource(),
visible: prevLayer.getVisible(),
style: this.getGraphicTypeLayerStyle(annotationGroup),
extent: this[_pyramid].extent,
})
}

Expand All @@ -4100,6 +4103,7 @@ class VolumeImageViewer {
annotationGroup.style,
prevLowResLayer.getSource()
),
extent: this[_pyramid].extent,
}) : getHighResLayer({ annotationGroup, prevLayer: prevLowResLayer })
}

Expand Down

0 comments on commit b5cfdd7

Please sign in to comment.