Skip to content

Commit c3b80eb

Browse files
Merge pull request #91 from conveyal/dev
v0.5.0
2 parents 30b8dad + ee84511 commit c3b80eb

File tree

15 files changed

+390
-333
lines changed

15 files changed

+390
-333
lines changed

README.md

+11
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@ After cloning the repository, run:
2323
- `npm install`
2424
- `npm start`
2525

26+
## Deploy
27+
28+
Once you see that TAUI is working properly with your static site data, deploy it to the S3 bucket specified in settings.yml with the following command (replacing the --config switch to point to default or wherever):
29+
30+
`mastarm deploy --config configurations/marseilles --minify --env production`
31+
32+
The AWS SDK for JS will detect and use the same AWS credentials you should have set up for AWSCLI. The deployment copies JS and CSS files into a sub-bucket called assets, but does not upload `index.html`. You need to edit `index.html` in the root of the TAUI repository to customize the page name, then upload it with `aws s3 cp index.html s3://mamp-static/index.html`.
33+
34+
`mastarm deploy` will not create the bucket or set its contents public. You need to ensure the bucket exists before running `mastarm deploy` and set all its contents public afterward using the S3 web console or CLI. You don’t need to set any permissions on the bucket itself.
35+
36+
If S3 reports “access denied” when you try to fetch a page over HTTP in your browser, this is often because an object you are requesting does not exist.
2637

2738
[npm-image]: https://img.shields.io/npm/v/@conveyal/taui.svg?maxAge=2592000&style=flat-square
2839
[npm-url]: https://www.npmjs.com/package/@conveyal/taui

configurations/default/messages.yml

+4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
Systems:
2+
AccessTitle: Access to
23
BaseTitle: Proposed Transit
34
ComparisonTitle: Current Transit
45
TripsTitle: Example trips
56
TripsEmpty: No trips found!
67
BestTripTitle: Fastest trip
78
AlternateTripsTitle: Alternates
9+
Waiting: waiting included
810
Faster: faster
911
NewTrip: New Trip
1012
Geocoding:
@@ -13,6 +15,8 @@ Geocoding:
1315
EndTitle: Traveling to
1416
EndPlaceholder: End
1517
PromptText: Type to search for an address
18+
Log:
19+
Title: Log
1620
Map:
1721
SetLocationPopup:
1822
SetStart: Set start

configurations/default/settings.yml

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ cloudfront: EJKZ46R7IC1BB
22
entries:
33
- src/index.js:assets/index.js
44
- src/containers/indianapolis/style.css:assets/index.css
5+
env: development
56
flyle: true
67
s3bucket: taui
78
serve: true

package.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
"description": "Isochrone & accessibility analysis",
44
"main": "src/index.js",
55
"scripts": {
6+
"deploy": "mastarm deploy --env production --minify",
67
"prestart": "yarn",
78
"semantic-release": "semantic-release pre && npm publish && semantic-release post",
8-
"start": "mastarm build --serve src/index.js:assets/index.js src/containers/indianapolis/style.css:assets/index.css --flyle",
9+
"start": "mastarm build",
910
"test": "mastarm lint"
1011
},
1112
"repository": {
@@ -28,7 +29,7 @@
2829
},
2930
"dependencies": {
3031
"@conveyal/woonerf": "^0.2.1",
31-
"browsochrones": "^0.9.0",
32+
"browsochrones": "^0.9.1",
3233
"color": "^0.11.1",
3334
"debug": "^2.2.0",
3435
"font-awesome": "^4.6.3",
@@ -54,7 +55,7 @@
5455
"react-select-geocoder": "^0.2.1",
5556
"redux": "^3.6.0",
5657
"redux-actions": "^0.12.0",
57-
"transitive-js": "^0.9.1"
58+
"transitive-js": "^0.9.2"
5859
},
5960
"standard": {
6061
"parser": "babel-eslint"

src/actions/index.js

+50-22
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ export const setOrigin = createAction('set origin', (origin) => {
3535
return origin
3636
})
3737

38+
export const setDestinationLabel = createAction('set destination label', (label) => {
39+
setKeyTo('end', label)
40+
return label
41+
})
42+
43+
export const setOriginLabel = createAction('set origin label', (label) => {
44+
setKeyTo('start', label)
45+
return label
46+
})
47+
3848
export const clearEnd = createAction('clear end', () => {
3949
setKeyTo('end', null)
4050
})
@@ -94,14 +104,15 @@ export function updateOrigin ({browsochrones, destinationLatlng, latlng, label,
94104
)
95105
} else {
96106
actions.push(
107+
setOrigin({latlng}),
97108
addActionLogItem(`Finding start address for ${lonlng(latlng).toString()}`),
98109
reverseGeocode({latlng})
99110
.then(({features}) => {
100111
if (!features || features.length < 1) return
101112
const label = featureToLabel(features[0])
102113
return [
103114
addActionLogItem(`Set start address to: ${label}`),
104-
setOrigin({label, latlng: lonlng(features[0].geometry.coordinates)})
115+
setOriginLabel(label)
105116
]
106117
})
107118
)
@@ -111,24 +122,24 @@ export function updateOrigin ({browsochrones, destinationLatlng, latlng, label,
111122

112123
const point = browsochrones.base.pixelToOriginPoint(Leaflet.CRS.EPSG3857.latLngToPoint(latlng, zoom), zoom)
113124
if (browsochrones.base.pointInQueryBounds(point)) {
114-
actions.push(
115-
fetchBrowsochronesFor({
116-
browsochrones: browsochrones.base,
117-
destinationLatlng,
118-
latlng,
119-
name: 'base',
120-
timeCutoff,
121-
zoom
122-
}),
123-
fetchBrowsochronesFor({
125+
actions.push(fetchBrowsochronesFor({
126+
browsochrones: browsochrones.base,
127+
destinationLatlng,
128+
latlng,
129+
name: 'base',
130+
timeCutoff,
131+
zoom
132+
}))
133+
if (browsochrones.comparison) {
134+
actions.push(fetchBrowsochronesFor({
124135
browsochrones: browsochrones.comparison,
125136
destinationLatlng,
126137
latlng,
127138
name: 'comparison',
128139
timeCutoff,
129140
zoom
130-
})
131-
)
141+
}))
142+
}
132143
} else {
133144
console.log('point out of bounds') // TODO: Handle
134145
}
@@ -156,7 +167,7 @@ function fetchBrowsochronesFor ({
156167

157168
return [
158169
decrementWork(),
159-
generateAccessiblityFor({browsochrones, name, timeCutoff}),
170+
generateAccessiblityFor({browsochrones, latlng, name, timeCutoff}),
160171
generateIsochroneFor({browsochrones, latlng, name, timeCutoff}),
161172
destinationLatlng && generateDestinationDataFor({
162173
browsochrones,
@@ -171,14 +182,19 @@ function fetchBrowsochronesFor ({
171182
]
172183
}
173184

174-
function generateAccessiblityFor ({browsochrones, name, timeCutoff}) {
185+
const storedAccessibility = {}
186+
const storedIsochrones = {}
187+
188+
function generateAccessiblityFor ({browsochrones, latlng, name, timeCutoff}) {
175189
return [
176190
incrementWork(),
177191
addActionLogItem(`Generating accessibility surface for ${name}`),
178192
(async () => {
179193
const accessibility = {}
180194
for (const grid of browsochrones.grids) {
181-
accessibility[grid] = await browsochrones.getAccessibilityForGrid(grid, timeCutoff)
195+
const key = `${name}-${lonlng.toString(latlng)}-${timeCutoff}-${grid}`
196+
accessibility[grid] = storedAccessibility[key] || await browsochrones.getAccessibilityForGrid(grid, timeCutoff)
197+
storedAccessibility[key] = accessibility[grid]
182198
}
183199
return [
184200
setAccessibilityFor({accessibility, name}),
@@ -193,8 +209,10 @@ function generateIsochroneFor ({browsochrones, latlng, name, timeCutoff}) {
193209
incrementWork(),
194210
addActionLogItem(`Generating travel time isochrone for ${name}`),
195211
(async () => {
196-
const isochrone = await browsochrones.getIsochrone(timeCutoff)
197-
isochrone.key = `${name}-${lonlng.toString(latlng)}-${timeCutoff}`
212+
const key = `${name}-${lonlng.toString(latlng)}-${timeCutoff}`
213+
const isochrone = storedIsochrones[key] || await browsochrones.getIsochrone(timeCutoff)
214+
isochrone.key = key
215+
storedIsochrones[key] = isochrone
198216

199217
return [
200218
setIsochroneFor({isochrone, name}),
@@ -212,7 +230,10 @@ function generateDestinationDataFor ({browsochrones, fromLatlng, toLatlng, name,
212230
const destinationPoint = browsochrones.pixelToOriginPoint(Leaflet.CRS.EPSG3857.latLngToPoint(toLatlng, zoom), zoom)
213231
const data = await browsochrones.generateDestinationData({
214232
from: fromLatlng || null,
215-
to: destinationPoint
233+
to: {
234+
...toLatlng,
235+
...destinationPoint
236+
}
216237
})
217238
data.transitive.key = `${name}-${lonlng.toString(toLatlng)}`
218239
return [
@@ -230,9 +251,12 @@ export function updateSelectedTimeCutoff ({browsochrones, latlng, timeCutoff}) {
230251

231252
if (browsochrones.base && browsochrones.base.isLoaded()) {
232253
actions.push(generateIsochroneFor({browsochrones: browsochrones.base, latlng, name: 'base', timeCutoff}))
254+
actions.push(generateAccessiblityFor({browsochrones: browsochrones.base, latlng, name: 'base', timeCutoff}))
255+
}
256+
257+
if (browsochrones.comparison && browsochrones.comparison.isLoaded()) {
233258
actions.push(generateIsochroneFor({browsochrones: browsochrones.comparison, latlng, name: 'comparison', timeCutoff}))
234-
actions.push(generateAccessiblityFor({browsochrones: browsochrones.base, name: 'base', timeCutoff}))
235-
actions.push(generateAccessiblityFor({browsochrones: browsochrones.comparison, name: 'comparison', timeCutoff}))
259+
actions.push(generateAccessiblityFor({browsochrones: browsochrones.comparison, latlng, name: 'comparison', timeCutoff}))
236260
}
237261

238262
return actions
@@ -266,13 +290,17 @@ export function updateDestination ({
266290
actions.push(setDestination({label, latlng}))
267291
} else {
268292
actions.push(
293+
setDestination({latlng}),
269294
reverseGeocode({latlng})
270-
.then(({features}) => setDestination({label: featureToLabel(features[0]), latlng: lonlng(features[0].geometry.coordinates)}))
295+
.then(({features}) => setDestinationLabel(featureToLabel(features[0])))
271296
)
272297
}
273298

274299
if (browsochrones.base && browsochrones.base.isLoaded()) {
275300
actions.push(generateDestinationDataFor({browsochrones: browsochrones.base, fromLatlng, toLatlng: latlng, name: 'base', zoom}))
301+
}
302+
303+
if (browsochrones.comparison && browsochrones.comparison.isLoaded()) {
276304
actions.push(generateDestinationDataFor({browsochrones: browsochrones.comparison, fromLatlng, toLatlng: latlng, name: 'comparison', zoom}))
277305
}
278306

src/components/log-item/style.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@
1111
.LogItem-createdAt {
1212
opacity: 0.75;
1313
margin-right: 0.5rem;
14-
vertical-align: middle;;
14+
vertical-align: middle;
1515
}

src/components/route-card.js

+11-11
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ function TripDiff ({
6767
: oldTravelTime - travelTime
6868
const diff = parseInt((nume / oldTravelTime * 100).toFixed(1))
6969

70-
if (oldTravelTime === 255) return <span className='increase'>{messages.NewTrip} <Icon type='star' /></span>
71-
else if (actualDiff > 0) return <span className='pull-right decrease'><strong>{diff}</strong>%<Icon type='level-up' /></span>
72-
else return <span className='pull-right increase'><strong>{diff * -1}</strong>%<Icon className='fa-rotate-180' type='level-up' /></span>
70+
if (oldTravelTime === 255) return <span className='increase'>{messages.NewTrip} <Icon type='star' /><br /></span>
71+
else if (actualDiff > 0) return <span className='pull-right decrease'><strong>{diff}</strong>%<Icon type='level-up' /><br /></span>
72+
else return <span className='pull-right increase'><strong>{diff * -1}</strong>%<Icon className='fa-rotate-180' type='level-up' /><br /></span>
7373
}
7474

7575
function renderJourneys ({ oldTravelTime, transitiveData, travelTime, waitTime }) {
@@ -102,8 +102,7 @@ function renderJourneys ({ oldTravelTime, transitiveData, travelTime, waitTime }
102102
const alternateTrips = alternateTripSegments.map((segments, jindex) => {
103103
return (
104104
<div className='Trip' key={`journey-${jindex}`}>
105-
<span className='CardIndex'>#{jindex + 1}</span>
106-
{segments}
105+
<span className='CardIndex'>{jindex + 1}.</span>{segments}
107106
</div>
108107
)
109108
})
@@ -117,8 +116,8 @@ function renderJourneys ({ oldTravelTime, transitiveData, travelTime, waitTime }
117116
<TripDiff
118117
oldTravelTime={oldTravelTime}
119118
travelTime={travelTime}
120-
/>}<br />
121-
includes <strong>{waitTime}</strong> {messages.Units.Mins} waiting <br />
119+
/>}
120+
<strong>{waitTime}</strong> {messages.Units.Mins} {messages.Systems.Waiting}<br />
122121
</div>
123122
</div>
124123
{alternateTrips.length > 0 &&
@@ -146,7 +145,7 @@ function extractRelevantTransitiveInfo ({
146145
const pid = s.pattern_id || s.patterns[0].pattern_id
147146
const seg = {}
148147
const route = findRouteForPattern({id: pid, patterns, routes})
149-
const color = route.route_color ? Color(`#${route.route_color}`) : Color(s.color)
148+
const color = route.route_color ? Color(`#${route.route_color}`) : Color('#0b2b40')
150149
seg.name = toCapitalCase(route.route_short_name)
151150

152151
if (s.patterns && s.patterns.length > 0) {
@@ -180,13 +179,14 @@ function MetricIcon ({
180179
}) {
181180
const lc = name.toLowerCase()
182181
if (lc.indexOf('job') !== -1) return <Icon type='building' />
183-
if (lc.indexOf('worker') !== -1) return <Icon type='child' />
182+
if (lc.indexOf('worker') !== -1 || lc.indexOf('population') !== -1) return <Icon type='child' />
183+
return <span />
184184
}
185185

186186
function showAccess (keys, base) {
187187
return (
188188
<div className='CardAccess'>
189-
<div className='heading'>Access to</div>
189+
<div className='heading'>{messages.Systems.AccessTitle}</div>
190190
{keys.map((k, i) => <div className='Metric' key={k}><MetricIcon name={k} /><strong> {(base[k] | 0).toLocaleString()} </strong> {toSpaceCase(k)}</div>)}
191191
</div>
192192
)
@@ -209,7 +209,7 @@ function AccessDiffPercentage ({
209209
function showDiff (keys, base, comparison) {
210210
return (
211211
<div className='CardAccess'>
212-
<div className='heading'>Access to</div>
212+
<div className='heading'>{messages.Systems.AccessTitle}</div>
213213
{keys.map((key, i) => {
214214
return (
215215
<div className='Metric' key={key}>

src/containers/indianapolis/form.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export default class Form extends DeepEqual {
5858
type='range'
5959
min={10}
6060
max={120}
61-
step={10}
61+
step={5}
6262
/>
6363
</div>
6464
</div>

src/containers/indianapolis/index.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class Indianapolis extends Component {
4343
timeCutoff: PropTypes.shape({
4444
selected: PropTypes.number
4545
}),
46-
ui: PropTypes.object,
46+
ui: PropTypes.object.isRequired,
4747
zoom: PropTypes.number
4848
}
4949

@@ -231,9 +231,9 @@ class Indianapolis extends Component {
231231
{messages.Systems.ComparisonTitle}
232232
</RouteCard>
233233
}
234-
{actionLog && actionLog.length > 0 &&
234+
{ui.showLog && actionLog && actionLog.length > 0 &&
235235
<div className='Card'>
236-
<div className='CardTitle'>Log</div>
236+
<div className='CardTitle'>{messages.Log.Title}</div>
237237
<Log
238238
items={this.props.actionLog}
239239
/>

src/containers/indianapolis/map.js

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
11
import {Browser} from 'leaflet'
2+
import {mapbox} from 'mapbox.js'
23
import React, {PropTypes} from 'react'
3-
import {GeoJson, Map as LeafletMap, Marker, Popup, TileLayer} from 'react-leaflet'
4+
import {GeoJson, Map as LeafletMap, Marker, Popup, TileLayer, ZoomControl} from 'react-leaflet'
45

56
import DeepEqual from '../../components/deep-equal'
67
import Icon from '../../components/icon'
78
import messages from '../../utils/messages'
89
import TransitiveLayer from '../../components/transitive-map-layer'
910
import transitiveStyle from './transitive-style'
1011

12+
const startIcon = mapbox.marker.icon({
13+
'marker-size': 'large',
14+
'marker-symbol': 'star',
15+
'marker-color': '#4269a4'
16+
})
17+
const endIcon = mapbox.marker.icon({
18+
'marker-color': '#ff8c00'
19+
})
20+
1121
export default class Map extends DeepEqual {
1222
static propTypes = {
1323
centerCoordinates: PropTypes.arrayOf(PropTypes.number),
@@ -87,7 +97,10 @@ export default class Map extends DeepEqual {
8797
zoom={zoom}
8898
onClick={this._onMapClick}
8999
onZoom={onZoom}
100+
preferCanvas
101+
zoomControl={false}
90102
>
103+
<ZoomControl position='topright' />
91104
<TileLayer
92105
url={Browser.retina && process.env.LEAFLET_RETINA_URL ? process.env.LEAFLET_RETINA_URL : process.env.LEAFLET_TILE_URL}
93106
attribution={process.env.LEAFLET_ATTRIBUTION}
@@ -97,6 +110,7 @@ export default class Map extends DeepEqual {
97110
return (
98111
<Marker
99112
draggable
113+
icon={index === 0 ? startIcon : endIcon}
100114
key={`marker-${index}`}
101115
{...m}
102116
>

0 commit comments

Comments
 (0)