From 321193985a29768a217066768acf8bc991fba6b5 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:08:08 -0500 Subject: [PATCH] Adding hover and click data to clusters for maps. --- src/plots/map/map.js | 122 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/src/plots/map/map.js b/src/plots/map/map.js index 9a397743482..adaa7cbe5e0 100644 --- a/src/plots/map/map.js +++ b/src/plots/map/map.js @@ -695,14 +695,128 @@ proto.getMapLayers = function() { return this.map.getStyle().layers; }; +async function getClusterData (e, _map, gd, self, curveNumber, click) { + const features = _map.queryRenderedFeatures(e.point, { + layers: [e.features[0].layer.id] + }); + + const evt = e + evt.target = _map + + var xy = [ + evt.originalEvent.offsetX, + evt.originalEvent.offsetY + ]; + + evt.target.getBoundingClientRect = function() { return bb; }; + + self.xaxis.p2c = function() { return _map.unproject(xy).lng; }; + self.yaxis.p2c = function() { return _map.unproject(xy).lat; }; + const x0 = _map.project(e.lngLat).x; + const y0 = _map.project(e.lngLat).y; + + var bb = { + x0, + x1: x0 + 2, + y0, + y1: y0 + 2, + }; + + const clusterId = features[0].properties.cluster_id; + const clusterSource = await _map.getSource(e.features[0].source); + const point_count = features[0].properties.point_count + + // Get all points under a cluster + const points = await clusterSource.getClusterLeaves(clusterId, point_count, 0) + + // Assuming 'points' contains the data you want to use for the hover event + // You need to map this data to the corresponding data points in your Plotly graph + // For example, let's assume you have a mapping function `mapToPlotlyData` that does this + + const plotlyData = mapToPlotlyData(points, curveNumber); + + // Prepare the hover event data + const hoverData = plotlyData.map(point => { + // Convert coordinates to strings for comparison + const latStr = point.y.toFixed(4); + const lonStr = point.x.toFixed(4); + + // Access the original data for the current curve + const originalData = gd.data[point.curveNumber]; + + // Find the matching text from the original data + const text = findMatchingText(latStr, lonStr, originalData); + + return { + lon: lonStr, + lat: latStr, + curveNumber: point.curveNumber, + pointNumber: point.pointNumber, + pointIndex: point.pointNumber, + data: gd.data[point.curveNumber], + fullData: gd._fullData[point.curveNumber], + bbox: bb, + x: point.x, + y: point.y, + text: text // Include the matched text + }; + }); + + gd._hoverdata = hoverData; + + if (!click) { + // Emit the Plotly hover event + gd.emit('plotly_hover', { + event: evt, // Pass the original event if needed + points: hoverData, + xaxes: [self.xaxis], // Array of x-axes involved + yaxes: [self.yaxis], // Array of y-axes involved + xvals: hoverData.map(d => d.x), // Array of x values + yvals: hoverData.map(d => d.y), // Array of y values + }); + } else { + // Emit the Plotly click event + gd.emit('plotly_click', { + event: evt.originalEvent, // Pass the original event if needed + points: hoverData, + }); + } +} + +// Function to find matching text for a given lat/lon in the original data +function findMatchingText(lat, lon, originalData) { + const matchIndex = originalData.lat.findIndex((latVal, index) => + latVal === lat && originalData.lon[index] === lon + ); + return matchIndex !== -1 ? originalData.text[matchIndex] : ''; +} + +// Example function to map cluster points to Plotly data +function mapToPlotlyData(points, curveNumber= 0) { + // Implement your logic to map cluster points to Plotly data + // This is a placeholder function + return points.map((point, index) => ({ + x: point.geometry.coordinates[0], + y: point.geometry.coordinates[1], + curveNumber, // Replace with the actual curve number if needed + pointNumber: index // + // Add any other necessary data mapping + })); +} + // convenience wrapper that first check in 'below' references // a layer that exist and then add the layer to the map, proto.addLayer = function(opts, below) { var map = this.map; + var gd = this.gd if(typeof below === 'string') { if(below === '') { map.addLayer(opts, below); + if (opts.id.includes('cluster')) { + map.on('mouseover', opts.id, async (e) => {await getClusterData(e, map, gd, this)}) + map.on('click', opts.id, async (e) => {await getClusterData(e, map, gd, this, 0, true)}) + } return; } @@ -710,6 +824,10 @@ proto.addLayer = function(opts, below) { for(var i = 0; i < mapLayers.length; i++) { if(below === mapLayers[i].id) { map.addLayer(opts, below); + if (opts.id.includes('cluster')) { + map.on('mouseover', opts.id, async (e) => {await getClusterData(e, map, gd, this, i)}) + map.on('click', opts.id, async (e) => {await getClusterData(e, map, gd, this, i, true)}) + } return; } } @@ -723,6 +841,10 @@ proto.addLayer = function(opts, below) { } map.addLayer(opts); + if (opts.id.includes('cluster')) { + map.on('mouseover', opts.id, async (e) => {await getClusterData(e, map, gd, this)}) + map.on('click', opts.id, async (e) => {await getClusterData(e, map, gd, this, 0, true)}) + } }; // convenience method to project a [lon, lat] array to pixel coords