Skip to content

Commit 842779c

Browse files
authored
Merge pull request #167 from opentraffic/kdiluca-barCharts
Barcharts for Reference Speed Comparison
2 parents b363a3d + 1fb9798 commit 842779c

File tree

8 files changed

+199
-891
lines changed

8 files changed

+199
-891
lines changed

package-lock.json

Lines changed: 65 additions & 858 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app/processing.js

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,21 @@ export function addSpeedToMapGeometry (tiles, date, segment, geometry) {
1818
const state = store.getState()
1919
const days = state.date.dayFilter || [0, 7]
2020
const hours = state.date.hourFilter || [0, 24]
21-
22-
const refspeed = tiles.reference[segment.level][segment.tileIdx].referenceSpeeds80[segment.segmentIdx]
21+
const refSpeed = tiles.reference[segment.level][segment.tileIdx].referenceSpeeds80[segment.segmentIdx]
2322
const subtiles = tiles.historic[date.year][date.week][segment.level][segment.tileIdx]
2423

2524
const subtile = getSubtileForSegmentIdx(segment.segmentIdx, subtiles)
26-
if (subtile) {
27-
// Append the speed to the geometry to render later
25+
if (subtile) { // Append the speed to the geometry to render later
2826
const speeds = getValuesFromSubtile(segment.segmentIdx, subtile, days, hours, 'speeds')
2927
geometry.speedByHour = addSpeedByHour(speeds, days, hours)
3028
geometry.speed = getMeanSpeed(segment.segmentIdx, subtile, days, hours)
3129
// calculate percentage difference between weekly/historical speed and reference speed
3230
if (geometry.speed === null || geometry.speed === 0 || typeof geometry.speed === 'undefined') {
33-
geometry.percentDiff = null
31+
geometry.percentDiff = 0
3432
} else {
35-
geometry.percentDiff = ((refspeed - geometry.speed) / mathjs.mean(refspeed, geometry.speed)) * 100
33+
geometry.percentDiff = ((refSpeed - geometry.speed) / mathjs.mean(refSpeed, geometry.speed)) * 100
3634
}
35+
subtile.percentDiffs.push(geometry.percentDiff)
3736
}
3837
} catch (e) {}
3938
}
@@ -60,37 +59,76 @@ function addSpeedByHour (speedArray, days, hours) {
6059
}
6160

6261
/**
63-
* Collect speeds from the entire week for use in bar chart
62+
* Collect speeds and percent differences from the entire week for use in bar chart
6463
* @param {*} tiles
6564
* @param {*} date
6665
* @param {*} segment
6766
*/
68-
export function prepareSpeedsForBarChart (tiles, date, segment) {
67+
export function prepareDataForBarChart (tiles, date, segment) {
6968
// not all levels and tiles are available yet, so try()
7069
// skips it if it doesn't work
7170
try {
71+
const refSpeed = tiles.reference[segment.level][segment.tileIdx].referenceSpeeds80[segment.segmentIdx]
7272
const subtiles = tiles.historic[date.year][date.week][segment.level][segment.tileIdx]
7373
const subtile = getSubtileForSegmentIdx(segment.segmentIdx, subtiles)
7474
if (subtile) {
75+
var percentDiffsByDayAndHourArray = mathjs.zeros(7, 24)
7576
var speedsByDayAndHourArray = mathjs.zeros(7, 24)
7677
var nonZeroSpeedCountByDayAndHourArray = mathjs.zeros(7, 24)
7778
var speeds = getValuesFromSubtile(segment.segmentIdx, subtile, [0, 7], [0, 24], 'speeds')
7879
chunk(speeds, 24).forEach((speedsForThisDay, dayIndex) => {
7980
speedsForThisDay.forEach((speedForThisHour, hourIndex) => {
8081
if (speedForThisHour > 0) {
82+
const percentDiffForThisHour = ((refSpeed - speedForThisHour) / mathjs.mean(refSpeed, speedForThisHour)) * 100
83+
percentDiffsByDayAndHourArray.set([dayIndex, hourIndex], Number(percentDiffForThisHour.toFixed(2)))
8184
speedsByDayAndHourArray.set([dayIndex, hourIndex], speedForThisHour)
8285
nonZeroSpeedCountByDayAndHourArray.set([dayIndex, hourIndex], 1)
8386
}
8487
})
8588
})
8689
return {
8790
speeds: speedsByDayAndHourArray,
91+
percentDiff: percentDiffsByDayAndHourArray,
8892
counts: nonZeroSpeedCountByDayAndHourArray
8993
}
9094
}
9195
} catch (e) {}
9296
}
9397

98+
/**
99+
* Collect percentDiffs from the entire week for use in bar chart
100+
* @param {*} tiles
101+
* @param {*} date
102+
* @param {*} segment
103+
*/
104+
export function preparePercentDiffsForBarChart (tiles, date, segment) {
105+
// not all levels and tiles are available yet, so try()
106+
// skips it if it doesn't work
107+
try {
108+
const refSpeed = tiles.reference[segment.level][segment.tileIdx].referenceSpeeds80[segment.segmentIdx]
109+
const subtiles = tiles.historic[date.year][date.week][segment.level][segment.tileIdx]
110+
const subtile = getSubtileForSegmentIdx(segment.segmentIdx, subtiles)
111+
if (subtile) {
112+
var percentDiffsByDayAndHourArray = mathjs.zeros(7, 24)
113+
var nonZeroDiffCountByDayAndHourArray = mathjs.zeros(7, 24)
114+
var speeds = getValuesFromSubtile(segment.segmentIdx, subtile, [0, 7], [0, 24], 'speeds')
115+
chunk(speeds, 24).forEach((speedsForThisDay, dayIndex) => {
116+
speedsForThisDay.forEach((speedForThisHour, hourIndex) => {
117+
if (speedForThisHour > 0) {
118+
const percDiff = ((refSpeed - speedForThisHour) / mathjs.mean(refSpeed, speedForThisHour)) * 100
119+
percentDiffsByDayAndHourArray.set([dayIndex, hourIndex], percDiff)
120+
nonZeroDiffCountByDayAndHourArray.set([dayIndex, hourIndex], 1)
121+
}
122+
})
123+
})
124+
return {
125+
percentDiffs: percentDiffsByDayAndHourArray,
126+
diffCounts: nonZeroDiffCountByDayAndHourArray
127+
}
128+
}
129+
} catch (e) {}
130+
}
131+
94132
/**
95133
* Given a segment index, its historic data subtile, and the range of days
96134
* and hours selected, calculate the mean traffic speed on that segment.
@@ -143,7 +181,7 @@ export function getValuesFromSubtile (segmentIdx, subtile, days, hours, prop) {
143181
// filter down to the requested range of days
144182
x => x.slice(...days),
145183
// filter down to the requested range of hours
146-
x => x.map(speedsForGivenDay => speedsForGivenDay.slice(...hours)),
184+
x => x.map(forGivenDay => forGivenDay.slice(...hours)),
147185
// back to just an array of hours
148186
flatten
149187
])(subtile[prop])
@@ -167,6 +205,8 @@ export function getSubtileForSegmentIdx (segmentIdx, subtiles) {
167205

168206
for (let i = 0, j = indices.length; i < j; i++) {
169207
const subtile = subtiles[indices[i]]
208+
// need to initialize the percentDiffs for the barcharts here
209+
subtile.percentDiffs = []
170210

171211
const lowerBounds = subtile.startSegmentIndex
172212
const upperBounds = subtile.startSegmentIndex + subtile.subtileSegments

src/app/region.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { getTilesForBbox, getTileUrlSuffix, parseSegmentId } from '../lib/tiles'
33
import { merge } from '../lib/geojson'
44
import { getTangramLayer, setDataSource, getCurrentConfig, setCurrentConfig } from '../lib/tangram'
55
import { fetchDataTiles } from './data'
6-
import { addSpeedToMapGeometry, prepareSpeedsForBarChart } from './processing'
6+
import { addSpeedToMapGeometry, prepareDataForBarChart } from './processing'
77
import store from '../store'
88
import { setGeoJSON } from '../store/actions/view'
99
import { startLoading, stopLoading, hideLoading } from '../store/actions/loading'
10-
import { clearBarchart, setBarchartSpeeds } from '../store/actions/barchart'
10+
import { clearBarchart, setBarchartData } from '../store/actions/barchart'
1111
import { setRouteError } from '../store/actions/route'
1212
import { displayRegionInfo } from './route-info'
1313
import mathjs from 'mathjs'
@@ -134,17 +134,20 @@ export function showRegion (bounds) {
134134
fetchDataTiles(parsedIds, date)
135135
.then((tiles) => {
136136
if (date.year && date.week) {
137+
let totalPercentDiffArray = mathjs.zeros(7, 24)
137138
let totalSpeedArray = mathjs.zeros(7, 24)
138139
let totalCountArray = mathjs.zeros(7, 24)
140+
139141
parsedIds.forEach((id, index) => {
140142
addSpeedToMapGeometry(tiles, date, id, features[index].properties)
141-
let speedsFromThisSegment = prepareSpeedsForBarChart(tiles, date, id)
142-
if (speedsFromThisSegment) {
143-
totalSpeedArray = mathjs.add(totalSpeedArray, speedsFromThisSegment.speeds)
144-
totalCountArray = mathjs.add(totalCountArray, speedsFromThisSegment.counts)
143+
let dataFromThisSegment = prepareDataForBarChart(tiles, date, id)
144+
if (dataFromThisSegment) {
145+
totalPercentDiffArray = mathjs.add(totalPercentDiffArray, dataFromThisSegment.percentDiff)
146+
totalSpeedArray = mathjs.add(totalSpeedArray, dataFromThisSegment.speeds)
147+
totalCountArray = mathjs.add(totalCountArray, dataFromThisSegment.counts)
145148
}
146149
})
147-
store.dispatch(setBarchartSpeeds(totalSpeedArray, totalCountArray))
150+
store.dispatch(setBarchartData(totalSpeedArray, totalPercentDiffArray, totalCountArray))
148151
}
149152
setDataSource('routes', { type: 'GeoJSON', data: results })
150153
results.properties = {

src/app/route.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import polyline from '@mapbox/polyline'
33
import { getRoute, getTraceAttributes, valhallaResponseToPolylineCoordinates } from '../lib/valhalla'
44
import { parseSegmentId } from '../lib/tiles'
55
import { fetchDataTiles } from './data'
6-
import { addSpeedToMapGeometry, prepareSpeedsForBarChart } from './processing'
6+
import { addSpeedToMapGeometry, prepareDataForBarChart } from './processing'
77
import { getRouteTime } from './route-time'
88
import { startLoading, stopLoading, hideLoading } from '../store/actions/loading'
9-
import { clearBarchart, setBarchartSpeeds } from '../store/actions/barchart'
9+
import { clearBarchart, setBarchartData } from '../store/actions/barchart'
1010
import { setGeoJSON } from '../store/actions/view'
1111
import {
1212
clearRouteSegments,
@@ -162,16 +162,19 @@ export function showRoute (waypoints) {
162162
// TODO: when year and week aren't specified, we should also
163163
// skip the step of trying to fetch data tiles
164164
if (date.year && date.week) {
165+
let totalPercentDiffArray = mathjs.zeros(7, 24)
165166
let totalSpeedArray = mathjs.zeros(7, 24)
166167
let totalCountArray = mathjs.zeros(7, 24)
168+
167169
parsedIds.forEach((id) => {
168170
// Will add either meaured or reference speed
169171
addSpeedToMapGeometry(tiles, date, id, id)
170-
let speedsFromThisSegment = prepareSpeedsForBarChart(tiles, date, id)
171-
totalSpeedArray = mathjs.add(totalSpeedArray, speedsFromThisSegment.speeds)
172-
totalCountArray = mathjs.add(totalCountArray, speedsFromThisSegment.counts)
172+
let dataFromThisSegment = prepareDataForBarChart(tiles, date, id)
173+
totalPercentDiffArray = mathjs.add(totalPercentDiffArray, dataFromThisSegment.percentDiff)
174+
totalSpeedArray = mathjs.add(totalSpeedArray, dataFromThisSegment.speeds)
175+
totalCountArray = mathjs.add(totalCountArray, dataFromThisSegment.counts)
173176
})
174-
store.dispatch(setBarchartSpeeds(totalSpeedArray, totalCountArray))
177+
store.dispatch(setBarchartData(totalSpeedArray, totalPercentDiffArray, totalCountArray))
175178
const routeTime = getRouteTime(response)
176179
store.dispatch(setTrafficRouteTime(routeTime))
177180
}

src/components/Sidebar/TimeFilters/chart.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import 'dc/dc.css'
88
// Callback for when data is added to the current filter results
99
const reduceAdd = function (p, v) {
1010
p.count++
11-
p.sum += v.meanSpeedThisHour
11+
p.sum += v.value // value can be mean speed this hour OR percent diff this hour
1212
if (p.count > 0) {
1313
p.avg = (p.sum / p.count)
1414
} else {
@@ -21,7 +21,7 @@ const reduceAdd = function (p, v) {
2121
const reduceRemove = function (p, v) {
2222
// return p - p.
2323
p.count--
24-
p.sum -= v.meanSpeedThisHour
24+
p.sum -= v.value // value can be mean speed this hour OR percent diff this hour
2525
if (p.count > 0) {
2626
p.avg = (p.sum / p.count)
2727
} else {
@@ -33,7 +33,7 @@ const reduceRemove = function (p, v) {
3333
// Initialize `p`
3434
const reduceInitial = function () {
3535
return {
36-
meanSpeedThisHour: 0,
36+
value: 0,
3737
count: 0,
3838
sum: 0,
3939
avg: 0

src/components/Sidebar/TimeFilters/index.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ export class TimeFilters extends React.Component {
1919
dayFilter: PropTypes.arrayOf(PropTypes.number),
2020
hourFilter: PropTypes.arrayOf(PropTypes.number),
2121
speedsBinnedByHour: PropTypes.array,
22+
percentDiffsBinnedByHour: PropTypes.array,
2223
refSpeedComparisonEnabled: PropTypes.bool
2324
}
2425

25-
componentDidUpdate () {
26-
const chartData = crossfilter(this.props.speedsBinnedByHour)
26+
componentDidUpdate (prevProps) {
27+
const { refSpeedComparisonEnabled, percentDiffsBinnedByHour, speedsBinnedByHour } = this.props
28+
const chartData = (refSpeedComparisonEnabled) ? crossfilter(percentDiffsBinnedByHour) : crossfilter(speedsBinnedByHour)
2729

2830
this.makeDailyChart(chartData)
2931
this.makeHourlyChart(chartData)
@@ -49,6 +51,7 @@ export class TimeFilters extends React.Component {
4951
shouldComponentUpdate (nextProps) {
5052
return (
5153
!isEqual(nextProps.speedsBinnedByHour, this.props.speedsBinnedByHour) ||
54+
!isEqual(nextProps.percentDiffsBinnedByHour, this.props.percentDiffsBinnedByHour) ||
5255
!isEqual(nextProps.refSpeedComparisonEnabled, this.props.refSpeedComparisonEnabled)
5356
)
5457
}
@@ -134,6 +137,7 @@ function mapStateToProps (state) {
134137
dayFilter: state.date.dayFilter,
135138
hourFilter: state.date.hourFilter,
136139
speedsBinnedByHour: state.barchart.speedsBinnedByHour,
140+
percentDiffsBinnedByHour: state.barchart.percentDiffsBinnedByHour,
137141
refSpeedComparisonEnabled: state.app.refSpeedComparisonEnabled
138142
}
139143
}

src/store/actions/barchart.js

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ export function clearBarchart () {
66
}
77
}
88

9-
export function setBarchartSpeeds (speedsArray, countArray) {
9+
export function setBarchartData (speedsArray, diffsArray, countArray) {
1010
let meanSpeedArray
11-
if (countArray) {
11+
let percentDiffArray
12+
if (countArray && diffsArray) {
1213
meanSpeedArray = speedsArray.map((value, index, matrix) => {
1314
let count = countArray.get(index)
1415
if (count && count > 0) {
@@ -17,19 +18,66 @@ export function setBarchartSpeeds (speedsArray, countArray) {
1718
return 0
1819
}
1920
})
21+
percentDiffArray = diffsArray.map((value, index, matrix) => {
22+
let count = countArray.get(index)
23+
if (count && count > 0) {
24+
return (value / count)
25+
} else {
26+
return 0
27+
}
28+
})
2029
} else {
2130
meanSpeedArray = speedsArray
31+
percentDiffArray = diffsArray
2232
}
33+
2334
let hoursForCrossFilter = []
35+
let hoursForDiffCrossFilter = []
2436
meanSpeedArray.forEach((speed, index) => {
2537
hoursForCrossFilter.push({
2638
'dayOfWeek': index[0] + 1,
2739
'hourOfDay': index[1] + 1,
28-
'meanSpeedThisHour': speed
40+
'value': speed
41+
})
42+
})
43+
percentDiffArray.forEach((diff, index) => {
44+
hoursForDiffCrossFilter.push({
45+
'dayOfWeek': index[0] + 1,
46+
'hourOfDay': index[1] + 1,
47+
'value': diff
48+
})
49+
})
50+
return {
51+
type: ADD_SEGMENTS_TO_BARCHART,
52+
speedsBinnedByHour: hoursForCrossFilter,
53+
percentDiffsBinnedByHour: hoursForDiffCrossFilter
54+
}
55+
}
56+
57+
export function setBarchartPercentDiffs (diffsArray, countArray) {
58+
let percentDiffArray
59+
if (countArray) {
60+
percentDiffArray = diffsArray.map((value, index, matrix) => {
61+
let count = countArray.get(index)
62+
if (count && count > 0) {
63+
return (value / count)
64+
} else {
65+
return 0
66+
}
67+
})
68+
} else {
69+
percentDiffArray = diffsArray
70+
}
71+
let hoursForCrossFilter = []
72+
percentDiffArray.forEach((percentDiff, index) => {
73+
hoursForCrossFilter.push({
74+
'dayOfWeek': index[0] + 1,
75+
'hourOfDay': index[1] + 1,
76+
'percentDiffThisHour': percentDiff
2977
})
3078
})
3179
return {
3280
type: ADD_SEGMENTS_TO_BARCHART,
33-
speedsBinnedByHour: hoursForCrossFilter
81+
percentDiffsBinnedByHour: hoursForCrossFilter
3482
}
3583
}

src/store/reducers/barchart.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { CLEAR_BARCHART, ADD_SEGMENTS_TO_BARCHART } from '../actions'
22

33
const initialState = {
4+
percentDiffsBinnedByHour: [],
45
speedsBinnedByHour: []
56
}
67

@@ -9,12 +10,14 @@ const barchart = (state = initialState, action) => {
910
case CLEAR_BARCHART:
1011
return {
1112
...state,
12-
speedsBinnedByHour: []
13+
speedsBinnedByHour: [],
14+
percentDiffsBinnedByHour: []
1315
}
1416
case ADD_SEGMENTS_TO_BARCHART:
1517
return {
1618
...state,
17-
speedsBinnedByHour: action.speedsBinnedByHour
19+
speedsBinnedByHour: action.speedsBinnedByHour,
20+
percentDiffsBinnedByHour: action.percentDiffsBinnedByHour
1821
}
1922
default:
2023
return state

0 commit comments

Comments
 (0)