Feature/handle cluster select from timeline (#175)

* Updating some styles for cover; updating copy

* Wrote up getSelectedClusters function; testing

* Working on select with clusters being highlighted; rendering highlight around both clusters and individual points, so there's a little overlap

* Removed extraneous props being passed down to cluster

Co-authored-by: efarooqui <efarooqui@pandora.com>
This commit is contained in:
Ebrahem Farooqui
2020-10-28 23:10:56 -07:00
committed by GitHub
parent e25b437f75
commit 9175057015
8 changed files with 86 additions and 19 deletions

View File

@@ -126,10 +126,9 @@
"filters": "Filters",
"filters_label": "Filters",
"explore_by_filter__title": "Explore by filter",
"explore_by_filter__description": "Selecting a filter will show you only those events that are annotated with the filter. If you select nothing, as well as everything, all data will be displayed.",
"explore_by_filter__description": "Filters refer to the types of incident. Select multiple filters to introduce colour-coding, up to a maximum of six filters. If no filters are selected, all datapoints are displayed.",
"explore_by_category__title": "Explore events by category",
"explore_by_category__description": ""
"explore_by_category__description": "Categories refer to the victims of a given incident. If no categories are selected, all datapoints are displayed."
},
"timeline": {
"zooms": [

View File

@@ -283,6 +283,15 @@ export function calcClusterSize (pointCount, totalPoints) {
return Math.min(maxSize, 10 + (pointCount / totalPoints) * 150)
}
export function calculateTotalClusterPoints (clusters) {
return clusters.reduce((total, cl) => {
if (cl && cl.properties && cl.properties.cluster) {
total += cl.properties.point_count
}
return total
}, 0)
}
export function isLatitude (lat) {
return !!lat && isFinite(lat) && Math.abs(lat) <= 90
}

View File

@@ -261,7 +261,7 @@ class Dashboard extends React.Component {
}
const popupStyles = {
fontSize: 24,
fontSize: 20,
height: `calc(100vh - ${app.timeline.dimensions.height}px)`,
width: '40vw',
bottom: app.timeline.dimensions.height

View File

@@ -17,7 +17,7 @@ import Narratives from './presentational/Map/Narratives'
import DefsMarkers from './presentational/Map/DefsMarkers.jsx'
import LoadingOverlay from '../components/Overlay/Loading'
import { mapClustersToLocations, isIdentical, isLatitude, isLongitude } from '../common/utilities'
import { mapClustersToLocations, isIdentical, isLatitude, isLongitude, calculateTotalClusterPoints, calcClusterSize } from '../common/utilities'
// NB: important constants for map, TODO: make statics
const supportedMapboxMap = ['streets', 'satellite']
@@ -183,6 +183,29 @@ class Map extends React.Component {
return []
}
getSelectedClusters () {
const { selected } = this.props.app
const selectedIds = selected.map(sl => sl.id)
if (this.state.clusters && this.state.clusters.length > 0) {
return this.state.clusters.reduce((acc, cl) => {
if (cl.properties.cluster) {
const children = this.getClusterChildren(cl.properties.cluster_id)
if (children && children.length > 0) {
children.forEach(child => {
const clusterPresent = acc.findIndex(item => item.id === cl.id) >= 0
if (selectedIds.includes(child.id) && !clusterPresent) {
acc.push(cl)
}
})
}
}
return acc
}, [])
}
return []
}
alignLayers () {
const mapNode = document.querySelector('.leaflet-map-pane')
if (mapNode === null) return { transformX: 0, transformY: 0 }
@@ -341,10 +364,35 @@ class Map extends React.Component {
}
renderSelected () {
const selectedClusters = this.getSelectedClusters()
const totalMarkers = []
this.props.app.selected.forEach(s => {
const { latitude, longitude } = s
totalMarkers.push({
latitude,
longitude,
radius: this.props.ui.eventRadius
})
})
const totalClusterPoints = calculateTotalClusterPoints(this.state.clusters)
selectedClusters.forEach(cl => {
if (cl.properties.cluster) {
const { coordinates } = cl.geometry
totalMarkers.push({
latitude: String(coordinates[1]),
longitude: String(coordinates[0]),
radius: calcClusterSize(cl.properties.point_count, totalClusterPoints)
})
}
})
return (
<SelectedEvents
svg={this.svgRef.current}
selected={this.props.app.selected}
selected={totalMarkers}
projectPoint={this.projectPoint}
styles={this.props.ui.mapSelectedEvents}
/>

View File

@@ -8,7 +8,8 @@ import {
isLatitude,
isLongitude,
calculateColorPercentages,
zipColorsToPercentages } from '../../../common/utilities'
zipColorsToPercentages,
calculateTotalClusterPoints } from '../../../common/utilities'
const DefsClusters = () => (
<defs>
@@ -74,14 +75,10 @@ function ClusterEvents ({
isRadial,
svg,
clusters,
filterColors
filterColors,
selected
}) {
const totalPoints = clusters.reduce((total, cl) => {
if (cl && cl.properties) {
total += cl.properties.point_count
}
return total
}, 0)
const totalPoints = calculateTotalClusterPoints(clusters)
const styles = {
fill: isRadial ? "url('#clusterGradient')" : colors.fallbackEventColor,

View File

@@ -3,10 +3,10 @@ import { Portal } from 'react-portal'
import colors from '../../../common/global.js'
class MapSelectedEvents extends React.Component {
renderMarker (event) {
const { x, y } = this.props.projectPoint([event.latitude, event.longitude])
renderMarker (marker) {
const { x, y } = this.props.projectPoint([marker.latitude, marker.longitude])
const styles = this.props.styles
const r = styles ? styles.r : 24
const r = marker.radius ? marker.radius + 5 : 24
return (
<g
className='location-marker'

View File

@@ -145,8 +145,7 @@
.hero {
min-width: 100%;
max-width: 100%;
min-height: 150px;
min-height: 80px;
margin: auto;
display: flex;
flex-direction: column;
@@ -217,6 +216,20 @@
h5 { margin-top: -15px; }
.md-container {
width: 100%;
overflow-wrap: break-word;
// white-space: pre-line;
ul {
list-style: none;
}
li::before {
content: "* ";
}
}
// mobile styles, remove overlay buttons
@media only screen and (max-width: 1200px) {
font-size: 22pt !important;

View File

@@ -173,6 +173,7 @@ export const selectLocations = createSelector(
activeLocations[location] = {
label: location,
events: [event],
id: event.id,
latitude: event.latitude,
longitude: event.longitude
}