From 3e36fd980da944c2b1c0af88e8c4b0ad86ea2128 Mon Sep 17 00:00:00 2001 From: Aditya Bhumbla Date: Thu, 5 Oct 2023 13:49:46 -0700 Subject: [PATCH] fill in PT leg in elevation graph --- package-lock.json | 9 ++ package.json | 1 + src/components/ItineraryElevationProfile.jsx | 125 +++++++++++++++---- 3 files changed, 110 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0cae95d6..f9df1f20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "bowser": "^2.11.0", "browserslist": "^4.18.1", "chart.js": "^4.4.0", + "chartjs-plugin-annotation": "^3.0.1", "classnames": "^2.3.1", "color": "^4.2.1", "eslint": "^8.3.0", @@ -5879,6 +5880,14 @@ "pnpm": ">=7" } }, + "node_modules/chartjs-plugin-annotation": { + "version": "3.0.1", + "resolved": "https://foursquaredev.jfrog.io/foursquaredev/api/npm/npm/chartjs-plugin-annotation/-/chartjs-plugin-annotation-3.0.1.tgz", + "integrity": "sha512-hlIrXXKqSDgb+ZjVYHefmlZUXK8KbkCPiynSVrTb/HjTMkT62cOInaT1NTQCKtxKKOm9oHp958DY3RTAFKtkHg==", + "peerDependencies": { + "chart.js": ">=4.0.0" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", diff --git a/package.json b/package.json index 410824a7..baca3aaf 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "bowser": "^2.11.0", "browserslist": "^4.18.1", "chart.js": "^4.4.0", + "chartjs-plugin-annotation": "^3.0.1", "classnames": "^2.3.1", "color": "^4.2.1", "eslint": "^8.3.0", diff --git a/src/components/ItineraryElevationProfile.jsx b/src/components/ItineraryElevationProfile.jsx index 60ffdd4c..531c6d28 100644 --- a/src/components/ItineraryElevationProfile.jsx +++ b/src/components/ItineraryElevationProfile.jsx @@ -4,57 +4,137 @@ import './ItineraryElevationProfile.css'; import { Chart as ChartJS, - CategoryScale, LinearScale, PointElement, LineElement, Title, - Tooltip, Filler, - Legend, } from 'chart.js'; +import AnnotationPlugin from 'chartjs-plugin-annotation'; import { Scatter } from 'react-chartjs-2'; import distance from '@turf/distance'; import * as turf from '@turf/helpers'; ChartJS.register( - CategoryScale, LinearScale, PointElement, LineElement, Title, - Tooltip, Filler, - Legend, + AnnotationPlugin, ); export default function ItineraryElevationProfile(props) { const { route } = props; const points = []; + const ptLegs = []; + let ptLegWithoutHeight = []; let currentDist = 0; let currentPoint = route.legs[0].geometry.coordinates[0]; let maxHeight = 0; - for (const leg of route.legs) { - for (const point of leg.geometry.coordinates) { - const dist = distance(turf.point(currentPoint), turf.point(point)); + let currPTLeg = { data: [] }; + console.log(route); + for (let i = 0; i < route.legs.length; i++) { + const leg = route.legs[i]; + for (let j = 0; j < leg.geometry.coordinates.length; j++) { + const point = leg.geometry.coordinates[j]; + const dist = distance(turf.point(currentPoint), turf.point(point), { + units: 'miles', + }); currentDist += dist; currentPoint = point; if (point.length === 3) { - points.push({ x: currentDist, y: currentPoint[2] }); - if (currentPoint[2] > maxHeight) { - maxHeight = currentPoint[2]; + const pointHeight = currentPoint[2] / 0.3048; + points.push({ x: currentDist, y: pointHeight }); + if (pointHeight > maxHeight) { + maxHeight = pointHeight; + } + if ( + j === leg.geometry.coordinates.length - 1 && + i < route.legs.length - 1 && + route.legs[i + 1].type === 'pt' + ) { + currPTLeg.data.push({ + x: currentDist, + y: pointHeight, + type: 'start', + }); + } + if (j === 0 && i > 0 && route.legs[i - 1].type === 'pt') { + currPTLeg.data.push({ x: currentDist, y: pointHeight, type: 'end' }); + ptLegs.push(currPTLeg); + currPTLeg = { data: [] }; + // Check if we have any transit legs without a start/end height + if (ptLegWithoutHeight.length) { + const startHeight = ptLegs[ptLegWithoutHeight[0]].data[0].y; + const heightDiff = pointHeight - startHeight; + for (let k = 0; k < ptLegWithoutHeight.length; k++) { + const frac = (k + 1) / (ptLegWithoutHeight.length + 1); + ptLegs[ptLegWithoutHeight[k]].data[1].y = + startHeight + frac * heightDiff; + ptLegs[ptLegWithoutHeight[k] + 1].data[0].y = + startHeight + frac * heightDiff; + } + ptLegWithoutHeight = []; + } } } else { + // Sometimes two transit legs are next to each other without a bike leg in between + if ( + j === leg.geometry.coordinates.length - 1 && + i < route.legs.length - 1 && + route.legs[i + 1].type === 'pt' + ) { + currPTLeg.data.push({ x: currentDist, y: null, type: 'end' }); + ptLegs.push(currPTLeg); + currPTLeg = { data: [] }; + ptLegWithoutHeight.push(ptLegs.length - 1); + } + if (j === 0 && i > 0 && route.legs[i - 1].type === 'pt') { + currPTLeg.data.push({ x: currentDist, y: null, type: 'start' }); + } points.push({ x: currentDist, y: null }); } + if (j === 0 && leg.type === 'pt') { + currPTLeg['backgroundColor'] = leg.route_color; + currPTLeg['label'] = leg.route_name; + currPTLeg['name'] = leg.trip_id; + currPTLeg['showLine'] = true; + currPTLeg['fill'] = true; + } } } - console.log(points); + const annotations = {}; + const datasets = [ + { + fill: true, + label: 'Elevation', + data: points, + borderColor: 'rgb(53, 162, 235)', + backgroundColor: 'rgba(53, 162, 235, 0.5)', + showLine: true, + }, + ]; + for (const ptLeg of ptLegs) { + annotations[ptLeg.name] = { + type: 'label', + content: ptLeg.label, + xValue: (ptLeg.data[0].x + ptLeg.data[1].x) / 2, + yValue: (ptLeg.data[0].y + ptLeg.data[1].y) / 2, + backgroundColor: 'rgba(245,245,245)', + }; + datasets.push(ptLeg); + } const options = { + elements: { + point: { + radius: 0, + }, + }, scales: { x: { min: 0, @@ -65,34 +145,29 @@ export default function ItineraryElevationProfile(props) { }, y: { min: 0, - max: maxHeight + 2, + max: maxHeight + 5, ticks: { stepSize: 10, }, }, }, plugins: { + tooltip: { + enabled: false, + }, legend: { - position: 'top', + display: false, }, title: { display: true, text: 'Elevation', }, + annotation: { annotations }, }, }; const data = { - datasets: [ - { - fill: true, - label: 'Elevation', - data: points, - borderColor: 'rgb(53, 162, 235)', - backgroundColor: 'rgba(53, 162, 235, 0.5)', - showLine: true, - }, - ], + datasets, }; return ;