Skip to content

Commit

Permalink
feat(flowmap): FlowMap based on FlowmapBlue (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
billyc committed Nov 1, 2021
1 parent 99e166b commit f667347
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 26 deletions.
44 changes: 33 additions & 11 deletions src/charts/flowmap.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<template lang="pug">
.map-layout
flow-map-layer.choro-map(v-if="centroids.length" :props="mapProps")
flow-map-layer.choro-map(v-if="centroids.length"
:viewId="viewId"
:props="mapProps"
)

//- .config-bar
//- img.img-button(@click="useCircles=false"
Expand All @@ -21,9 +24,10 @@ import { Vue, Component, Watch, Prop } from 'vue-property-decorator'
import { Worker, spawn, Thread } from 'threads'
import * as turf from '@turf/turf'
import { FileSystemConfig } from '@/Globals'
import { FileSystemConfig, REACT_VIEW_HANDLES } from '@/Globals'
import FlowMapLayer from '@/layers/FlowMapLayer'
import HTTPFileSystem from '@/js/HTTPFileSystem'
// import globalStore from '@/store'
import { VuePlugin } from 'vuera'
Vue.use(VuePlugin)
Expand All @@ -44,6 +48,10 @@ export default class VueComponent extends Vue {
private sliderOpacity = 80
// private globalState = globalStore.state
private viewId = Math.random()
private get mapProps() {
return {
locations: this.centroids,
Expand All @@ -53,6 +61,11 @@ export default class VueComponent extends Vue {
}
}
@Watch('$store.state.viewState') viewMoved() {
if (!REACT_VIEW_HANDLES[this.viewId]) return
REACT_VIEW_HANDLES[this.viewId]()
}
private async mounted() {
this.fileApi = new HTTPFileSystem(this.fileSystemConfig)
Expand All @@ -69,6 +82,13 @@ export default class VueComponent extends Vue {
this.animate()
}
private beforeDestroy() {
if (this.animator) window.cancelAnimationFrame(this.animator)
// MUST delete the React view handle to prevent gigantic memory leak!
delete REACT_VIEW_HANDLES[this.viewId]
}
private startTime = Date.now()
private elapsed = 0
private animator: any = null
Expand All @@ -80,10 +100,6 @@ export default class VueComponent extends Vue {
}, 33)
}
private beforeDestroy() {
if (this.animator) window.cancelAnimationFrame(this.animator)
}
private async loadBoundaries() {
if (!this.config.boundaries) return
Expand Down Expand Up @@ -113,7 +129,7 @@ export default class VueComponent extends Vue {
centroid.properties.id = feature.properties[this.config.boundariesJoinCol]
this.centroids.push({
id: centroid.properties.id,
id: `${centroid.properties.id}`,
lon: centroid.geometry.coordinates[0],
lat: centroid.geometry.coordinates[1],
})
Expand All @@ -128,15 +144,21 @@ export default class VueComponent extends Vue {
this.thread = await spawn(new Worker('../workers/DataFetcher.thread'))
try {
const data = await this.thread.fetchData({
const data = (await this.thread.fetchData({
fileSystemConfig: this.fileSystemConfig,
subfolder: this.subfolder,
files: this.files,
config: this.config,
})) as any[]
// assumes flow data has "origin,destination,count" columns
this.flows = data.map((row: any) => {
return {
o: `${row.origin}`,
d: `${row.destination}`,
v: row.count,
}
})
// assumes flow data has "origin,destination,trips" columns
this.flows = data
} catch (e) {
const message = '' + e
console.log(message)
Expand Down
66 changes: 51 additions & 15 deletions src/layers/FlowMapLayer.tsx
Original file line number Diff line number Diff line change
@@ -1,48 +1,84 @@
import React, { useState, useMemo, useEffect } from 'react'
import { StaticMap } from 'react-map-gl'
import React, { useState } from 'react'
import DeckGL from '@deck.gl/react'
import { StaticMap } from 'react-map-gl'
import FlowMapLayer from '@flowmap.gl/core'
import * as ReactDOM from 'react-dom'

import { MAPBOX_TOKEN, MAP_STYLES } from '@/Globals'
import { MAPBOX_TOKEN, MAP_STYLES, REACT_VIEW_HANDLES } from '@/Globals'
import globalStore from '@/store'

function ZLayer({
export default function Layer({
props = {} as any,
viewId = 0,
initialView = { latitude: 37.76, longitude: -122.45, zoom: 11, pitch: 0 } as any,
}) {
const { locations, flows, dark, elapsed } = props

const [viewState, setViewState] = useState(globalStore.state.viewState)
const [hoverInfo, setHoverInfo] = useState({})

// // register setViewState in global view updater so we can respond to map motion
REACT_VIEW_HANDLES[viewId] = () => {
setViewState(globalStore.state.viewState)
}

function handleClick() {
console.log('click!')
}

function handleHover(event: any) {
console.log(event)
}

function handleViewState(view: any) {
setViewState(view)
view.center = [view.longitude, view.latitude]
globalStore.commit('setMapCamera', view)
}

const layer = new FlowMapLayer({
id: 'my-flowmap-layer',
locations,
flows,
getFlowMagnitude: flow => flow.count || null,
getFlowOriginId: flow => flow.origin,
getFlowDestId: flow => flow.destination,
getFlowOriginId: flow => flow.o,
getFlowDestId: flow => flow.d,
getFlowMagnitude: flow => flow.v || null,
getLocationId: (location: any) => location.id,
getLocationCentroid: (location: any) => [location.lon, location.lat],
onHover: setHoverInfo,
// animate: true,
// animationCurrentTime: elapsed,
// showTotals: true,
// selectedLocationIds: ['10'], // '5', '10', '20'],
pickable: true,
animate: true,
animationCurrentTime: elapsed,
showTotals: true,
showOnlyTopFlows: 3000,
maxFlowThickness: 15,
maxLocationCircleSize: 20,
// opacity: 0.5,
outlineThickness: -1,
showOnlyTopFlows: 2000,
})

return (
/*
//@ts-ignore */
<DeckGL controller={true} initialViewState={initialView} layers={[layer]}>
<DeckGL
layers={[layer]}
controller={true}
viewState={viewState}
pickingRadius={5}
getCursor={() => 'pointer'}
onClick={handleClick}
onViewStateChange={(e: any) => handleViewState(e.viewState)}
>
{
/*
// @ts-ignore */
<StaticMap
reuseMaps
mapStyle={MAP_STYLES.light}
mapStyle={dark ? MAP_STYLES.dark : MAP_STYLES.light}
preventStyleDiffing={true}
mapboxApiAccessToken={MAPBOX_TOKEN}
/>
}
</DeckGL>
)
}
export default ZLayer

0 comments on commit f667347

Please sign in to comment.