From 4d259b92092b9ac2fc1d5ccff443fc99a339c434 Mon Sep 17 00:00:00 2001 From: Nate-Wessel Date: Wed, 17 Jan 2024 17:04:17 +0000 Subject: [PATCH 1/7] DRY out CSV formatting --- frontend/src/Sidebar/index.jsx | 2 +- frontend/src/travelTimeQuery.js | 41 ++++++++++++++------------------- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/frontend/src/Sidebar/index.jsx b/frontend/src/Sidebar/index.jsx index 0674382..b18a9ec 100644 --- a/frontend/src/Sidebar/index.jsx +++ b/frontend/src/Sidebar/index.jsx @@ -67,7 +67,7 @@ function Results(){ r.resultsRecord('csv')).join('\n'))}`} + href={`data:text/plain;charset=utf-8,${encodeURIComponent([...results[0].resultsRecord('').keys()].join(',') + '\n' + results.map(r=>r.resultsRecord('csv')).join('\n'))}`} > Download results as CSV diff --git a/frontend/src/travelTimeQuery.js b/frontend/src/travelTimeQuery.js index 4794e4d..4517e00 100644 --- a/frontend/src/travelTimeQuery.js +++ b/frontend/src/travelTimeQuery.js @@ -53,33 +53,26 @@ export class TravelTimeQuery { return hoursPerDay * numDays } resultsRecord(type='json'){ - const record = { - URI: this.URI, - corridor: this.corridor.name, - timeRange: this.timeRange.name, - dateRange: this.dateRange.name, - daysOfWeek: this.days.name, - holidaysIncluded: this.#holidayOption.holidaysIncluded, - hoursInRange: this.hoursInRange, - estimatedVehicleCount: this.#estimatedSample, - mean_travel_time_minutes: this.#travelTime - } + // map used instead of object to preserve insertion order + const record = new Map() + record.set('URI',this.URI) + record.set('corridor',this.corridor.name) + record.set('timeRange',this.timeRange.name) + record.set('dateRange',this.dateRange.name) + record.set('daysOfWeek', this.days.name) + record.set('holidaysIncluded', this.#holidayOption.holidaysIncluded) + record.set('hoursInRange', this.hoursInRange) + record.set('estimatedVehicleCount', this.#estimatedSample) + record.set('mean_travel_time_minutes', this.#travelTime) if(type=='json'){ - return record + // can't JSONify maps + return Object.fromEntries(record) }else if(type=='csv'){ - return Object.values(record) - .map( value => { - - if(typeof value == 'string'){ - return `"${value}"` - } - return value - } ) + return [...record.values()] + // add double quotes to strings and concatenate + .map( value => typeof value == 'string' ? `"${value}"` : value ) .join(',') } - return 'invalid type requested' - } - static csvHeader(){ - return 'URI,corridor,timeRange,dateRange,daysOfWeek,holidaysIncluded,hoursPossible,estimatedSample,mean_travel_time_minutes' + return record } } \ No newline at end of file From 4ff4ef2c0137270c327b8d30555ca0df03e372c9 Mon Sep 17 00:00:00 2001 From: Nate-Wessel Date: Wed, 17 Jan 2024 17:10:53 +0000 Subject: [PATCH 2/7] formatting --- frontend/src/travelTimeQuery.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/src/travelTimeQuery.js b/frontend/src/travelTimeQuery.js index 4517e00..61cfda9 100644 --- a/frontend/src/travelTimeQuery.js +++ b/frontend/src/travelTimeQuery.js @@ -64,15 +64,16 @@ export class TravelTimeQuery { record.set('hoursInRange', this.hoursInRange) record.set('estimatedVehicleCount', this.#estimatedSample) record.set('mean_travel_time_minutes', this.#travelTime) + if(type=='json'){ - // can't JSONify maps - return Object.fromEntries(record) + return Object.fromEntries(record) // can't JSONify maps }else if(type=='csv'){ + // add double quotes to strings and concatenate return [...record.values()] - // add double quotes to strings and concatenate .map( value => typeof value == 'string' ? `"${value}"` : value ) .join(',') } + // the keys of a map record are used to create the CSV header return record } } \ No newline at end of file From bf0932237c8d35f9289ffe09928ea8db9cf60dc8 Mon Sep 17 00:00:00 2001 From: Nate-Wessel Date: Wed, 17 Jan 2024 19:22:55 +0000 Subject: [PATCH 3/7] split the output fields --- frontend/src/corridor.js | 47 +++++++++++++++++++++++---------- frontend/src/travelTimeQuery.js | 5 +++- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/frontend/src/corridor.js b/frontend/src/corridor.js index a610e3e..a31c039 100644 --- a/frontend/src/corridor.js +++ b/frontend/src/corridor.js @@ -42,21 +42,38 @@ export class Corridor extends Factor { get viaStreets(){ return new Set( this.links.map( link => link.name ) ) } + get viaStreetsString(){ + return [...this.viaStreets].join(' & ') + } + get startCrossStreets(){ + try { return difference(this.#intersections[0].streetNames,this.viaStreets) } + catch (e) { return new Set() } + } + get startCrossStreetsString(){ + if(this.startCrossStreets.size > 0){ + return [...this.startCrossStreets].join(' & ') + } + try{ return this.#intersections[0].displayCoords } + catch { } + } + get endCrossStreets(){ + try { return difference(this.#intersections[1].streetNames,this.viaStreets) } + catch (e) { return new Set() } + } + get endCrossStreetsString(){ + if(this.endCrossStreets.size > 0){ + return [...this.endCrossStreets].join(' & ') + } + try{ return this.#intersections[1].displayCoords } + catch { } + } get name(){ if(this.#intersections.length == 1){ - return `Incomplete corridor starting from ${this.intersections[0].description}` + return `Incomplete corridor starting from ${this.startCrossStreetsString}` }else if(this.#intersections.length == 2 && this.viaStreets.size > 0){ - // routing should be done - let start = difference(this.intersections[0].streetNames,this.viaStreets) - let end = difference(this.intersections[1].streetNames,this.viaStreets) - // no cross-street of a different name - if(start.size == 0){ start.add(this.intersections[0].displayCoords) } - if(end.size == 0){ end.add(this.intersections[1].displayCoords) } - return `${[...this.viaStreets].join(' & ')} from ${[...start].join(' & ')} to ${[...end].join(' & ')}` + return `${this.viaStreetsString} from ${this.startCrossStreetsString} to ${this.endCrossStreetsString}` }else if(this.#intersections.length == 2){ // but no via streets (yet?) - let start = [...this.intersections[0].streetNames].join(' & ') - let end = [...this.intersections[1].streetNames].join(' & ') - return `from ${start} to ${end}` + return `from ${this.startCrossStreetsString} to ${this.endCrossStreetsString}` } return 'New Corridor' } @@ -68,14 +85,16 @@ export class Corridor extends Factor { function CorridorElement({corridor}){ return (
-
{corridor.name}
+
+ {corridor.name} +
{corridor.isActive && <>
{corridor.intersections.length == 0 && - <>Click on the map to identify the starting point + 'Click on the map to identify the starting point' } {corridor.intersections.length == 1 && - <>Click on the map to identify the end point + 'Click on the map to identify the end point' }
} diff --git a/frontend/src/travelTimeQuery.js b/frontend/src/travelTimeQuery.js index 61cfda9..0197439 100644 --- a/frontend/src/travelTimeQuery.js +++ b/frontend/src/travelTimeQuery.js @@ -56,7 +56,9 @@ export class TravelTimeQuery { // map used instead of object to preserve insertion order const record = new Map() record.set('URI',this.URI) - record.set('corridor',this.corridor.name) + record.set('routeStreets',this.corridor.viaStreetsString) + record.set('startCrossStreets',this.corridor.startCrossStreetsString) + record.set('endCrossStreets',this.corridor.endCrossStreetsString) record.set('timeRange',this.timeRange.name) record.set('dateRange',this.dateRange.name) record.set('daysOfWeek', this.days.name) @@ -64,6 +66,7 @@ export class TravelTimeQuery { record.set('hoursInRange', this.hoursInRange) record.set('estimatedVehicleCount', this.#estimatedSample) record.set('mean_travel_time_minutes', this.#travelTime) + record.set('mean_travel_time_seconds', 60* this.#travelTime) if(type=='json'){ return Object.fromEntries(record) // can't JSONify maps From c533fbb29acb0b9862c410fdb960a959db7d7493 Mon Sep 17 00:00:00 2001 From: Nate-Wessel Date: Wed, 17 Jan 2024 19:43:12 +0000 Subject: [PATCH 4/7] update readme with new fields --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 758cb52..9e478ec 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,6 @@ If you have any trouble using the app, please send an email to Nate Wessel (nate ## Outputs The app can return results in either CSV or JSON format. The fields in either case are the same. There will be one column for each of the input parameters: -* corridor * time range * date range * days of week @@ -32,10 +31,14 @@ The app can return results in either CSV or JSON format. The fields in either ca The other fields may require some explanation: -| Field | Description | +| Field | Description | |----|----| +| `routeStreets` | The name(s) of the streets along the corridor. I.e. the path taken. | +| `startCrossStreets` | The names of any cross-street(s) at the start of the corridor. If the corridor starts mid-block then coordinates of that point will be returned instead. | +| `endCrossStreets` | The names of any cross-street(s) at the end of the corridor. If the corridor ends mid-block then coordinates of that point will be returned instead. | | `mean_travel_time_minutes` | The mean travel time in minutes is given as a floating point number rounded to two decimal places. Where insufficient data was available to complete the request, the value will be null, and in cases where the request was impossible a value of -999 will be assigned. (See `hoursInRange` below). | -| `URI` | The URI is the API endpoint that corresponds to this exect request. It may be of little use to some end users but may help us to reproduce the request and verify data quality. It can also serve as a unique ID. | +| `mean_travel_time_seconds` | Same as above, but measured in seconds. | +| `URI` | The URI is the API endpoint that corresponds to this exect request. It may be of little use to some end users but can help us to reproduce the request and verify data quality. It can also serve as a unique ID for the record. | | `hoursInRange` | The total number of hours that are theoretically within the scope of this request. This does not imply that data is/was available at all times. It's possible to construct requests with zero hours in range such as e.g `2023-01-01` to `2023-01-02`, Mondays only (There's only one Sunday in that range). Impossible combinations are included in the output for clarity and completeness but are not actually executed against the API and should return an error. | | `estimatedVehicleCount` | A very rough estimate of the number of actual vehicles traversing the corridor within the given temporal bounds. Includes partial trips through the corridor and may be subject to additional caveats which we are still exploring. | From 53ab378f7f99a6fccd6025d0e1e644335422769c Mon Sep 17 00:00:00 2001 From: Nate-Wessel Date: Wed, 17 Jan 2024 19:46:01 +0000 Subject: [PATCH 5/7] lint --- frontend/src/Sidebar/index.jsx | 1 - frontend/src/corridor.js | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/frontend/src/Sidebar/index.jsx b/frontend/src/Sidebar/index.jsx index b18a9ec..642a6d4 100644 --- a/frontend/src/Sidebar/index.jsx +++ b/frontend/src/Sidebar/index.jsx @@ -4,7 +4,6 @@ import { DataContext } from '../Layout' import FactorContainer from './FactorContainer' import BigButton from './BigButton' import FactorList from './FactorList' -import { TravelTimeQuery } from '../travelTimeQuery.js' import { restoreStateFromFile } from './restoreStateFromFile.js' import './sidebar.css' diff --git a/frontend/src/corridor.js b/frontend/src/corridor.js index a31c039..25384f4 100644 --- a/frontend/src/corridor.js +++ b/frontend/src/corridor.js @@ -54,7 +54,7 @@ export class Corridor extends Factor { return [...this.startCrossStreets].join(' & ') } try{ return this.#intersections[0].displayCoords } - catch { } + catch { return } } get endCrossStreets(){ try { return difference(this.#intersections[1].streetNames,this.viaStreets) } @@ -65,7 +65,7 @@ export class Corridor extends Factor { return [...this.endCrossStreets].join(' & ') } try{ return this.#intersections[1].displayCoords } - catch { } + catch { return } } get name(){ if(this.#intersections.length == 1){ From 26e3c448ed6e2d4903cc0a8b4558493eb6940773 Mon Sep 17 00:00:00 2001 From: Nate-Wessel Date: Wed, 17 Jan 2024 19:59:05 +0000 Subject: [PATCH 6/7] llintt --- frontend/src/corridor.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/src/corridor.js b/frontend/src/corridor.js index 25384f4..8b1caf4 100644 --- a/frontend/src/corridor.js +++ b/frontend/src/corridor.js @@ -52,9 +52,10 @@ export class Corridor extends Factor { get startCrossStreetsString(){ if(this.startCrossStreets.size > 0){ return [...this.startCrossStreets].join(' & ') + }else if(this.#intersections.length > 0){ + return this.#intersections[0].displayCoords } - try{ return this.#intersections[0].displayCoords } - catch { return } + return } get endCrossStreets(){ try { return difference(this.#intersections[1].streetNames,this.viaStreets) } @@ -63,9 +64,10 @@ export class Corridor extends Factor { get endCrossStreetsString(){ if(this.endCrossStreets.size > 0){ return [...this.endCrossStreets].join(' & ') + }else if(this.#intersections.length > 1){ + return this.#intersections[1].displayCoords } - try{ return this.#intersections[1].displayCoords } - catch { return } + return } get name(){ if(this.#intersections.length == 1){ From bb1e5f56981554ccd9286eda801c9b91682a7ad6 Mon Sep 17 00:00:00 2001 From: Nate-Wessel Date: Wed, 17 Jan 2024 20:03:19 +0000 Subject: [PATCH 7/7] linttt --- frontend/src/corridor.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/corridor.js b/frontend/src/corridor.js index 8b1caf4..8643b9e 100644 --- a/frontend/src/corridor.js +++ b/frontend/src/corridor.js @@ -55,7 +55,7 @@ export class Corridor extends Factor { }else if(this.#intersections.length > 0){ return this.#intersections[0].displayCoords } - return + return '' } get endCrossStreets(){ try { return difference(this.#intersections[1].streetNames,this.viaStreets) } @@ -67,7 +67,7 @@ export class Corridor extends Factor { }else if(this.#intersections.length > 1){ return this.#intersections[1].displayCoords } - return + return '' } get name(){ if(this.#intersections.length == 1){