Skip to content

Commit

Permalink
Merge pull request #63 from conveyal/dev
Browse files Browse the repository at this point in the history
v0.10.0
  • Loading branch information
trevorgerhardt authored Nov 29, 2016
2 parents 92c97ed + 1e49515 commit 777a6e5
Show file tree
Hide file tree
Showing 14 changed files with 719 additions and 580 deletions.
231 changes: 118 additions & 113 deletions example.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ var LineMapFactory = React.createFactory(LineMap)
var ContourGrid = require('./test/contour-grid')
var ContourGridFactory = React.createFactory(ContourGrid)

var Browsochrone = require('./lib')
import Browsochrone from './lib'

const bc = new Browsochrone()
const bc2 = new Browsochrone()

const baseUrl = 'http://s3.amazonaws.com/analyst-static/indy-baseline-v4'
const gridUrl = 'http://s3.amazonaws.com/analyst-static/indy-baseline-z9/intgrids'
const baseUrl = 'https://dz69bcpxxuhn6.cloudfront.net/indy-baseline-v6'
const gridUrl = 'https://dz69bcpxxuhn6.cloudfront.net/indy-baseline-z9/intgrids'

const map = L.mapbox
const map = window.map = L.mapbox
.map('map', 'conveyal.hml987j0', {
accessToken: 'pk.eyJ1IjoiY29udmV5YWwiLCJhIjoiY2lndnI5cms4MHJ4Mnd3bTB4MzYycDc4NiJ9.C40M0KSYXGSX_IbbqN53Eg',
tileLayer: {
Expand All @@ -41,26 +41,28 @@ Promise
fetch(baseUrl + '/query.json').then(function (res) { return res.json() }),
fetch(baseUrl + '/stop_trees.dat').then(function (res) { return res.arrayBuffer() }),
fetch(gridUrl + '/Jobs_total.grid').then(function (res) { return res.arrayBuffer() }),
fetch(gridUrl + '/Workers_total.grid').then(function (res) { return res.arrayBuffer() }),
fetch(baseUrl + '/transitive.json').then(function (res) { return res.json() })
fetch(gridUrl + '/Workers_total.grid').then(function (res) { return res.arrayBuffer() })
])
.then(function (res) {
.then(async function (res) {
console.log('fetched all')
bc.setQuery(res[0])
bc.setStopTrees(res[1].slice(0))
bc.putGrid('jobs', res[2].slice(0))
bc.putGrid('workers', res[3].slice(0))
bc.setTransitiveNetwork(res[4])

bc2.setQuery(res[0])
bc2.setStopTrees(res[1].slice(0))
bc2.putGrid('jobs', res[2].slice(0))
bc2.putGrid('workers', res[3].slice(0))
bc2.setTransitiveNetwork(res[4])
await bc.setQuery(res[0])
await bc.setStopTrees(res[1].slice(0))
await bc.putGrid('jobs', res[2].slice(0))
await bc.putGrid('workers', res[3].slice(0))
await bc.setTransitiveNetwork(res[0].transitiveData)

await bc2.setQuery(res[0])
await bc2.setStopTrees(res[1].slice(0))
await bc2.putGrid('jobs', res[2].slice(0))
await bc2.putGrid('workers', res[3].slice(0))
await bc2.setTransitiveNetwork(res[0].transitiveData)

console.log('loaded')
var query = res[0]
map.setView(map.unproject([query.west + query.width / 2, query.north + query.height / 2], query.zoom), 11)
const center = map.unproject([query.west + query.width / 2, query.north + query.height / 2], query.zoom)
console.log('setting center to ', center)
map.setView(center, 11)
map.fire('click', {latlng: {lat: 39.77424175134454, lng: -86.15478515625001}})
})
.catch(function (e) {
console.error(e)
Expand Down Expand Up @@ -101,109 +103,112 @@ async function updateIsoLayer () {
console.timeEnd('workforce access')
}

let clickCount = 0
map.on('click', async function (e) {
if (bc.isReady()) {
// get the pixel coordinates
var point = bc.pixelToOriginPoint(map.project(e.latlng), map.getZoom())
document.getElementById('location').value = (point.x | 0) + '/' + (point.y | 0)

if (!bc.pointInQueryBounds(point)) {
if (surfaceLayer) {
map.removeLayer(surfaceLayer)
surfaceLayer = null
if (clickCount % 2 === 0) {
// get the pixel coordinates
console.log('projecting', e.latlng)
var point = bc.pixelToOriginPoint(map.project(e.latlng), map.getZoom())
document.getElementById('location').value = (point.x | 0) + '/' + (point.y | 0)

if (!bc.pointInQueryBounds(point)) {
if (surfaceLayer) {
map.removeLayer(surfaceLayer)
surfaceLayer = null
}

if (isoLayer) {
map.removeLayer(isoLayer)
isoLayer = null
}

return
}

if (isoLayer) {
map.removeLayer(isoLayer)
isoLayer = null
console.time('fetching origin')
try {
const response = await fetch(baseUrl + '/' + (point.x | 0) + '/' + (point.y | 0) + '.dat')
const data = await response.arrayBuffer()
console.timeEnd('fetching origin')
await bc.setOrigin(data.slice(0), point)
await bc2.setOrigin(data.slice(0), point)

console.time('generating surface')
console.time('generating both surfaces')
await bc.generateSurface()
console.timeEnd('generating surface')
await bc2.generateSurface()
console.timeEnd('generating both surfaces')

if (surfaceLayer) map.removeLayer(surfaceLayer)
if (isoLayer) map.removeLayer(isoLayer)

surfaceLayer = new window.L.GridLayer()
console.log('tile size', surfaceLayer.getTileSize())
surfaceLayer.createTile = bc.createTile
surfaceLayer.addTo(map)

updateIsoLayer()
} catch (err) {
if (surfaceLayer) {
map.removeLayer(surfaceLayer)
surfaceLayer = null
}

console.error(err)
console.error(err.stack)
}

return
}

console.time('fetching origin')
try {
const response = await fetch(baseUrl + '/' + (point.x | 0) + '/' + (point.y | 0) + '.dat')
const data = await response.arrayBuffer()
console.timeEnd('fetching origin')
await bc.setOrigin(data.slice(0), point)
await bc2.setOrigin(data.slice(0), point)

console.time('generating surface')
console.time('generating both surfaces')
await bc.generateSurface()
console.timeEnd('generating surface')
await bc2.generateSurface()
console.timeEnd('generating both surfaces')

if (surfaceLayer) map.removeLayer(surfaceLayer)
if (isoLayer) map.removeLayer(isoLayer)

surfaceLayer = window.L.tileLayer.canvas()
surfaceLayer.drawTile = bc.drawTile.bind(bc)
surfaceLayer.addTo(map)

updateIsoLayer()
} catch (err) {
if (surfaceLayer) {
map.removeLayer(surfaceLayer)
surfaceLayer = null
} else {
const point = bc.pixelToOriginPoint(map.project(e.latlng), map.getZoom())

console.time('transitive data')
try {
const data = await bc.generateDestinationData(point)
console.log(data)
const transitiveData = data.transitive
const transitive = new Transitive({data: transitiveData})
console.timeEnd('transitive data')

console.log(transitiveData.journeys.length + ' unique paths')

if (transitiveLayer !== null) {
map.removeLayer(transitiveLayer)
}

transitiveLayer = new L.TransitiveLayer(transitive)
map.addLayer(transitiveLayer)
// see leaflet.transitivelayer issue #2
transitiveLayer._refresh()

let { paths, times } = data.paths

// they come out of r5 backwards
reverse(times)
reverse(paths)

// clear the ones that are the same and arrive at the same time
for (let p = 0; p < paths.length - 1; p++) {
// + 1: time is offset one minute (wait one minute and take the trip at the next minute)
if (times[p] === times[p + 1] + 1 && paths[p][0] === paths[p + 1][0] && paths[p][1] === paths[p + 1][1]) paths[p] = undefined
}

paths = await Promise.all(paths.filter(p => !!p).map(path => bc.getPath(path)))

// set up Marey plot
const marey = MareyFactory({dest: point, paths, times, transitiveData})
ReactDOM.render(marey, document.getElementById('marey'))

// and schematic line map
const lineMap = LineMapFactory({data: transitiveData})
ReactDOM.render(lineMap, document.getElementById('lineMap'))
} catch (e) {
console.error(e)
}

console.error(err)
console.error(err.stack)
}
}
})

map.on('mousemove', async function (e) {
if (bc.isLoaded()) {
const point = bc.pixelToOriginPoint(map.project(e.latlng), map.getZoom())

console.time('transitive data')
try {
const data = await bc.generateDestinationData(point)
console.log(data)
const transitiveData = data.transitive
const transitive = new Transitive({data: transitiveData})
console.timeEnd('transitive data')

console.log(transitiveData.journeys.length + ' unique paths')

if (transitiveLayer !== null) {
map.removeLayer(transitiveLayer)
}

transitiveLayer = new L.TransitiveLayer(transitive)
map.addLayer(transitiveLayer)
// see leaflet.transitivelayer issue #2
transitiveLayer._refresh()

let { paths, times } = data.paths

// they come out of r5 backwards
reverse(times)
reverse(paths)

// clear the ones that are the same and arrive at the same time
for (let p = 0; p < paths.length - 1; p++) {
// + 1: time is offset one minute (wait one minute and take the trip at the next minute)
if (times[p] === times[p + 1] + 1 && paths[p][0] === paths[p + 1][0] && paths[p][1] === paths[p + 1][1]) paths[p] = undefined
}

paths = await Promise.all(paths.filter(p => !!p).map(path => bc.getPath(path)))

// set up Marey plot
const marey = MareyFactory({dest: point, paths, times, transitiveData})
ReactDOM.render(marey, document.getElementById('marey'))

// and schematic line map
const lineMap = LineMapFactory({data: transitiveData})
ReactDOM.render(lineMap, document.getElementById('lineMap'))
} catch (e) {
console.error(e)
}
}
// clickCount++ TODO: Get transitive working again
})

document.getElementById('show-isochrone').addEventListener('click', async function () {
Expand Down
29 changes: 16 additions & 13 deletions lib/accessibility-for-grid.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,37 @@
* cutoff is not feasible (the data become too large), so we only store it for a single cutoff during surface generation. However, calculating accessibility
* for additional grids should only take milliseconds.
*
* @param {Number} cutoff
* @param {Object} surface
* @param {Object} grid
* @returns {Number} accessibility
*/

export default function accessibilityForGrid ({cutoff = 60, surface, grid}) {
let query = surface.query
let accessibility = 0
export default function accessibilityForGrid ({
cutoff = 60,
grid,
surface
}) {
const query = surface.query

let accessibility = 0
for (let y = 0, pixel = 0; y < query.height; y++) {
for (let x = 0; x < query.width; x++, pixel++) {
let travelTime = surface.surface[pixel]
const travelTime = surface.surface[pixel]

// ignore unreached locations
// TODO in OTP/R5 we have a sigmoidal cutoff here to avoid "echoes" of high density locations
// at 60 minutes travel time from their origins.
// But maybe also we just want to not represent these things as hard edges since accessibility
// is a continuous phenomenon. No one is saying "ah, rats, it takes 60 minutes and 10 seconds
// to get work, I have to find a job that's 20 meters closer to home..."
if (travelTime > cutoff) continue

let gridx = x + query.west - grid.west
let gridy = y + query.north - grid.north

// if condition below fails we're off the grid, value is zero, don't bother with calculations
if (gridx >= 0 && gridx < grid.width && gridy >= 0 && gridy < grid.height) {
// get value of this pixel from grid
accessibility += grid.data[gridy * grid.width + gridx]
if (travelTime <= cutoff) {
const gridx = x + query.west - grid.west
const gridy = y + query.north - grid.north
if (grid.contains(gridx, gridy)) {
// get value of this pixel from grid
accessibility += grid.data[gridy * grid.width + gridx]
}
}
}
}
Expand Down
26 changes: 12 additions & 14 deletions lib/get-spectrogram-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,20 @@ export default function getSpectrogramData ({origin, query, stopTreeCache, grid}
x,
y
}) => {
let gridx = x + query.west - grid.west
let gridy = y + query.north - grid.north
const gridx = x + query.west - grid.west
const gridy = y + query.north - grid.north

// off the grid, return
if (gridx < 0 || gridy < 0 || gridx >= grid.width || gridy >= grid.height) return

const val = grid.data[gridy * grid.width + gridx]

for (let i = 0; i < travelTimesForDest.length; i++) {
const time = travelTimesForDest[i]
if (time === 255) continue // unreachable

if (time < MAX_TRIP_LENGTH) {
// time - 1 so areas reachable in 1 minute will be included in output[i][0]
// TODO audit all the places we're flooring things
output[i][time - 1] += val
if (grid.contains(gridx, gridy)) {
const val = grid.data[gridy * grid.width + gridx]

for (let i = 0; i < travelTimesForDest.length; i++) {
const time = travelTimesForDest[i]
if (time !== 255 && time < MAX_TRIP_LENGTH) {
// time - 1 so areas reachable in 1 minute will be included in output[i][0]
// TODO audit all the places we're flooring things
output[i][time - 1] += val
}
}
}
}})
Expand Down
2 changes: 1 addition & 1 deletion lib/get-surface.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export function computeMedianPixelValue (travelTimes) {
// odd number, find the middle, keeping in mind the fencepost problem
return travelTimes[Math.floor(travelTimes.length / 2)]
} else {
let pos = travelTimes.length / 2
const pos = travelTimes.length / 2
// -1 because off-by-one
return (travelTimes[pos] + travelTimes[pos - 1]) / 2
}
Expand Down
Loading

0 comments on commit 777a6e5

Please sign in to comment.