Skip to content

Commit ba6005c

Browse files
author
David Emory
committed
feat(map): Add toggle-able map vs. network view
1 parent 4f51f1d commit ba6005c

File tree

7 files changed

+408
-166
lines changed

7 files changed

+408
-166
lines changed

lib/components/map/base-map.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,14 @@ class BaseMap extends Component {
1313
static propTypes = {
1414
config: PropTypes.object,
1515
mapClick: PropTypes.func,
16-
setLocation: PropTypes.func // TODO: rename from action name to avoid namespace conflict?
16+
setLocation: PropTypes.func, // TODO: rename from action name to avoid namespace conflict?
17+
toggleName: PropTypes.string
1718
}
19+
20+
static defaultProps = {
21+
toggleName: 'Base2'
22+
}
23+
1824
_onClick = (e) => {
1925
const location = constructLocation(e.latlng)
2026
const reverseGeocode = true

lib/components/map/stylized-map.js

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import React, { Component, PropTypes } from 'react'
2+
import { connect } from 'react-redux'
3+
4+
import Transitive from 'transitive-js'
5+
6+
import { getActiveSearch } from '../../util/state'
7+
import { isBikeshareStation, itineraryToTransitive } from '../../util/map'
8+
9+
const extendedStyles = {
10+
segments: {
11+
// override the default stroke width
12+
'stroke-width': (display, segment, index, utils) => {
13+
switch (segment.type) {
14+
case 'CAR':
15+
return utils.pixels(display.zoom.scale(), 2, 4, 6) + 'px'
16+
case 'WALK':
17+
return '5px'
18+
case 'BICYCLE':
19+
case 'BICYCLE_RENT':
20+
return '4px'
21+
case 'TRANSIT':
22+
// bus segments:
23+
if (segment.mode === 3) {
24+
return utils.pixels(display.zoom.scale(), 4, 8, 12) + 'px'
25+
}
26+
// all others:
27+
return utils.pixels(display.zoom.scale(), 6, 12, 18) + 'px'
28+
}
29+
}
30+
},
31+
/*places: {
32+
display: function (d, data) {
33+
const place = data.owner
34+
if (
35+
place.getId() !== 'from' &&
36+
place.getId() !== 'to' &&
37+
!isBikeshareStation(place)
38+
) {
39+
return 'none'
40+
}
41+
}
42+
},*/
43+
places_icon: {
44+
display: function (display, data) {
45+
const place = data.owner
46+
if (
47+
place.getId() !== 'from' &&
48+
place.getId() !== 'to' &&
49+
!isBikeshareStation(place)
50+
) {
51+
return 'none'
52+
}
53+
}
54+
}
55+
}
56+
57+
// extend common transitive styles for stylized map view
58+
function mergeTransitiveStyles (base, extended) {
59+
const styles = Object.assign({}, base)
60+
for (const key in extended) {
61+
if (key in base) styles[key] = Object.assign({}, styles[key], extended[key])
62+
else styles[key] = extended[key]
63+
}
64+
return styles
65+
}
66+
67+
class StylizedMap extends Component {
68+
static defaultProps = {
69+
toggleName: 'Stylized'
70+
}
71+
72+
componentDidMount () {
73+
this._transitive = new Transitive({
74+
el: document.getElementById('trn-canvas'),
75+
styles: mergeTransitiveStyles(
76+
require('./transitive-styles'),
77+
extendedStyles
78+
),
79+
drawGrid: true,
80+
gridCellSize: 200,
81+
zoomFactors: [
82+
{
83+
minScale: 0,
84+
gridCellSize: 300,
85+
internalVertexFactor: 1000000,
86+
angleConstraint: 45,
87+
mergeVertexThreshold: 200
88+
}
89+
]
90+
})
91+
this._transitive.render()
92+
}
93+
94+
componentWillReceiveProps (nextProps) {
95+
if (nextProps.transitiveData !== this.props.transitiveData) {
96+
this._transitive.updateData(nextProps.transitiveData)
97+
this._transitive.render()
98+
}
99+
100+
if (nextProps.activeItinerary !== this.props.activeItinerary) {
101+
if (nextProps.activeItinerary == null) {
102+
// no option selected; clear focus
103+
this._transitive.focusJourney(null)
104+
this._transitive.refresh()
105+
} else if (nextProps.transitiveData) {
106+
this._transitive.focusJourney(
107+
nextProps.transitiveData.journeys[nextProps.activeItinerary]
108+
.journey_id
109+
)
110+
this._transitive.refresh()
111+
}
112+
}
113+
}
114+
115+
render () {
116+
return (
117+
<div
118+
id="trn-canvas"
119+
style={{ position: 'absolute', top: 0, bottom: 0, left: 0, right: 0 }}
120+
/>
121+
)
122+
}
123+
}
124+
125+
// connect to the redux store
126+
const mapStateToProps = (state, ownProps) => {
127+
const activeSearch = getActiveSearch(state.otp)
128+
let transitiveData = null
129+
if (
130+
activeSearch &&
131+
activeSearch.query.routingType === 'ITINERARY' &&
132+
activeSearch.response &&
133+
activeSearch.response.plan
134+
) {
135+
transitiveData = itineraryToTransitive(
136+
activeSearch.response.plan.itineraries[activeSearch.activeItinerary]
137+
)
138+
} else if (
139+
activeSearch &&
140+
activeSearch.response &&
141+
activeSearch.response.otp
142+
) {
143+
transitiveData = activeSearch.response.otp
144+
}
145+
146+
return {
147+
transitiveData,
148+
activeItinerary: activeSearch && activeSearch.activeItinerary
149+
}
150+
}
151+
152+
const mapDispatchToProps = {}
153+
154+
export default connect(mapStateToProps, mapDispatchToProps)(StylizedMap)

lib/components/map/toggle-map.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import React, { Component, PropTypes } from 'react'
2+
3+
import { ButtonGroup, Button } from 'react-bootstrap'
4+
5+
export default class ToggleMap extends Component {
6+
7+
constructor () {
8+
super()
9+
this.state = {
10+
visibleChild: 0
11+
}
12+
}
13+
14+
render () {
15+
return (
16+
<div className='map-container'>
17+
{this.props.children.map((child, i) => {
18+
return (
19+
<div className='map-container' style={{ visibility: i === this.state.visibleChild ? 'visible' : 'hidden' }}>
20+
{this.props.children[i]}
21+
</div>
22+
)
23+
})}
24+
<div style={{ position: 'absolute', bottom: 12, left: 12, zIndex: 100000 }}>
25+
<ButtonGroup>
26+
{this.props.children.map((child, i) => {
27+
return (
28+
<Button
29+
bsSize='xsmall'
30+
bsStyle={ i === this.state.visibleChild ? 'success' : 'default' }
31+
style={{ padding: '3px 6px' }}
32+
onClick={() => { this.setState({ visibleChild: i }) }}
33+
>
34+
{child.props.toggleName}
35+
</Button>
36+
)
37+
})}
38+
</ButtonGroup>
39+
</div>
40+
</div>
41+
)
42+
}
43+
}

lib/components/map/transitive-overlay.js

Lines changed: 1 addition & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { connect } from 'react-redux'
66
import * as d3 from 'd3'
77

88
import { getActiveSearch } from '../../util/state'
9-
import { isTransit } from '../../util/itinerary'
9+
import { itineraryToTransitive } from '../../util/map'
1010

1111
const zoomFactors = [{
1212
minScale: 0,
@@ -124,140 +124,6 @@ class TransitiveOverlay extends MapLayer {
124124
}
125125
}
126126

127-
function itineraryToTransitive (itin) {
128-
// console.log('itineraryToTransitive', itin);
129-
const tdata = {
130-
journeys: [],
131-
streetEdges: [],
132-
places: [],
133-
patterns: [],
134-
routes: [],
135-
stops: []
136-
}
137-
const routes = {}
138-
const stops = {}
139-
let streetEdgeId = 0
140-
let patternId = 0
141-
142-
const journey = {
143-
journey_id: 'itin',
144-
journey_name: 'Iterarary-derived Journey',
145-
segments: []
146-
}
147-
itin.legs.forEach(leg => {
148-
if (leg.mode === 'WALK' || leg.mode === 'BICYCLE') {
149-
const fromPlaceId = `itin_street_${streetEdgeId}_from`
150-
const toPlaceId = `itin_street_${streetEdgeId}_to`
151-
journey.segments.push({
152-
type: leg.mode,
153-
streetEdges: [streetEdgeId],
154-
from: { type: 'PLACE', place_id: fromPlaceId },
155-
to: { type: 'PLACE', place_id: toPlaceId }
156-
})
157-
tdata.streetEdges.push({
158-
edge_id: streetEdgeId,
159-
geometry: leg.legGeometry
160-
})
161-
tdata.places.push({
162-
place_id: fromPlaceId,
163-
place_lat: leg.from.lat,
164-
place_lon: leg.from.lon
165-
})
166-
tdata.places.push({
167-
place_id: toPlaceId,
168-
place_lat: leg.to.lat,
169-
place_lon: leg.to.lon
170-
})
171-
streetEdgeId++
172-
}
173-
if (isTransit(leg.mode)) {
174-
// determine if we have valid inter-stop geometry
175-
const hasInterStopGeometry =
176-
leg.interStopGeometry &&
177-
leg.interStopGeometry.length === leg.intermediateStops.length + 1
178-
179-
// create leg-specific pattern
180-
const ptnId = 'ptn_' + patternId
181-
let pattern = {
182-
pattern_id: ptnId,
183-
pattern_name: 'Pattern ' + patternId,
184-
route_id: leg.routeId,
185-
stops: []
186-
}
187-
188-
// add 'from' stop to stops dictionary and pattern object
189-
stops[leg.from.stopId] = {
190-
stop_id: leg.from.stopId,
191-
stop_name: leg.from.name,
192-
stop_lat: leg.from.lat,
193-
stop_lon: leg.from.lon
194-
}
195-
pattern.stops.push({ stop_id: leg.from.stopId })
196-
197-
// add intermediate stops to stops dictionary and pattern object
198-
//leg.intermediateStops.forEach(stop => {
199-
for (const [i, stop] of leg.intermediateStops.entries()) {
200-
stops[stop.stopId] = {
201-
stop_id: stop.stopId,
202-
stop_name: stop.name,
203-
stop_lat: stop.lat,
204-
stop_lon: stop.lon
205-
}
206-
pattern.stops.push({
207-
stop_id: stop.stopId,
208-
geometry: hasInterStopGeometry && leg.interStopGeometry[i].points
209-
})
210-
}
211-
212-
// add 'to' stop to stops dictionary and pattern object
213-
stops[leg.to.stopId] = {
214-
stop_id: leg.to.stopId,
215-
stop_name: leg.to.name,
216-
stop_lat: leg.to.lat,
217-
stop_lon: leg.to.lon
218-
}
219-
pattern.stops.push({
220-
stop_id: leg.to.stopId,
221-
geometry: hasInterStopGeometry && leg.interStopGeometry[leg.interStopGeometry.length - 1].points
222-
})
223-
224-
// add route to the route dictionary
225-
routes[leg.routeId] = {
226-
agency_id: leg.agencyId,
227-
route_id: leg.routeId,
228-
route_short_name: leg.routeShortName || '',
229-
route_long_name: leg.routeLongName || '',
230-
route_type: leg.routeType
231-
}
232-
233-
// add the pattern to the tdata patterns array
234-
tdata.patterns.push(pattern)
235-
236-
// add the pattern refrerence to the journey object
237-
journey.segments.push({
238-
type: 'TRANSIT',
239-
patterns: [{
240-
pattern_id: ptnId,
241-
from_stop_index: 0,
242-
to_stop_index: (leg.intermediateStops.length + 2) - 1
243-
}]
244-
})
245-
246-
patternId++
247-
}
248-
})
249-
250-
// add the routes and stops to the tdata arrays
251-
for (let k in routes) tdata.routes.push(routes[k])
252-
for (let k in stops) tdata.stops.push(stops[k])
253-
254-
// add the journey to the tdata journeys array
255-
tdata.journeys.push(journey)
256-
257-
// console.log('derived tdata', tdata);
258-
return tdata
259-
}
260-
261127
// connect to the redux store
262128

263129
const mapStateToProps = (state, ownProps) => {

0 commit comments

Comments
 (0)