Published
Edited
Aug 22, 2021
Importers
7 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
selected_station.name
Insert cell
station_index = station_list.indexOf(selected_station.name)
Insert cell
mutable radius = {
var inst_radius
if (selected_station.name !== undefined){
inst_radius = 50
} else {
inst_radius = 125
}
return inst_radius
}
Insert cell
trip_z_threshold = 1.5
Insert cell
// row data
arrival_data = {
var arrival_data = []
if (station_index >=0){
arrival_data = network.mat[station_index]
.map((x, i) => ({
'value': x,
'name': station_list[i],
'position': location_dict[station_list[i]]
}))
.filter(x => x.value > trip_z_threshold)
}
return arrival_data
}
Insert cell
opacity_scale = d3.scaleLinear()
.domain([1, 5])
.range([0, 200])
Insert cell
// column data
departure_data = network.mat.map(x => x[station_index])
.map((x, i) => ({
'value': x,
'name': station_list[i],
'position': location_dict[station_list[i]]
}))
.filter(x => x.value > trip_z_threshold)
Insert cell
path_width = 100
Insert cell
departure_paths = departure_data.map(x => {
return ({
'path': [selected_station.position, x.position],
'color': [255, 0, 0, opacity_scale(x.value)],
'width': path_width,
'departure': selected_station.name,
'arrival': x.name
})
})
Insert cell
arrival_paths = arrival_data.map(x => {
return ({
'path': [selected_station.position, x.position],
'color': [0, 0, 255, opacity_scale(x.value)],
'width': path_width,
'departure': x.name,
'arrival': selected_station.name
})
})
Insert cell
station_list
Insert cell
mutable path_types = 'all'
Insert cell
all_paths
Insert cell
path_types
Insert cell
all_paths = {
var all_paths = []
if (path_types === 'all'){
all_paths = [...arrival_paths, ...departure_paths]
} else if (path_types === 'departures'){
all_paths = departure_paths
} else if (path_types === 'arrivals'){
all_paths = arrival_paths
}
else if (path_types === 'single'){
all_paths = [...arrival_paths, ...departure_paths]
.filter(x => x.arrival === filter_single)
}
return all_paths
}
Insert cell
Insert cell
station_list = network.col_nodes.map(x => x.name)
Insert cell
Insert cell
Insert cell
mutable selected_station = ({name: undefined})
Insert cell
mutable location_dict = ({})
Insert cell
umap_x_min = Math.min.apply(Math, meta.map(function(o) { return parseFloat(o['umap-x']) }))
Insert cell
umap_x_max = Math.max.apply(Math, meta.map(function(o) { return parseFloat(o['umap-x']) }))
Insert cell
umap_y_min = Math.min.apply(Math, meta.map(function(o) { return parseFloat(o['umap-y']) }))
Insert cell
umap_y_max = Math.max.apply(Math, meta.map(function(o) { return parseFloat(o['umap-y']) }))
Insert cell
long_min = Math.min.apply(Math, meta.map(function(o) { return parseFloat(o['start station longitude']) }))
Insert cell
long_max = Math.max.apply(Math, meta.map(function(o) { return parseFloat(o['start station longitude']) }))
Insert cell
lat_min = Math.min.apply(Math, meta.map(function(o) { return parseFloat(o['start station latitude']) }))
Insert cell
lat_max = Math.max.apply(Math, meta.map(function(o) { return parseFloat(o['start station latitude']) }))
Insert cell
umap_x_scale = d3.scaleLinear()
.domain([umap_x_min, umap_x_max])
.range([long_min, long_max]);
Insert cell
umap_y_scale = d3.scaleLinear()
.domain([umap_y_min, umap_y_max])
.range([lat_min, lat_max]);
Insert cell
station_data = meta.map(
x => {
if (dendro_stations.length > 0){
var inst_alpha = 25;
if (dendro_stations.includes(x[''])){
inst_alpha = 255
}
} else {
inst_alpha = 255
}

var inst_hex = cat_colors[x['Neighborhood']]
var rgb = d3.color(inst_hex)
var inst_color = [rgb.r, rgb.g, rgb.b, inst_alpha]
// var inst_long = parseFloat(x['start station longitude'])
// var inst_lat = parseFloat(x['start station latitude'])
var map_long = parseFloat(x['start station longitude'])
var map_lat = parseFloat(x['start station latitude'])
var umap_long = umap_x_scale(parseFloat(x['umap-x']))
var umap_lat = umap_y_scale(parseFloat(x['umap-y']))
var inst_long = map_long * (1 - map_morph) + umap_long * map_morph
var inst_lat = map_lat * (1 - map_morph) + umap_lat * map_morph
var inst_obj = ({
'name': x[''],
'position': [inst_long, inst_lat],
'Neighborhood': x['Neighborhood'],
'radius': 150,

color: inst_color,

'station crossing': x['station crossing'],
'cross-x': x['cross-x'],
'cross-y': x['cross-y'],
'umap': [parseFloat(x['umap-x']), parseFloat(x['umap-y'])]
})
mutable location_dict[inst_obj.name] = inst_obj.position

return inst_obj
}
)
Insert cell
station_data
Insert cell
station_data
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// station_data[0].position
Insert cell
station_data
Insert cell
scatter_layer = {
// var background_data = [{
// position: [-73.938299999998, 40.79232719999897],
// color: [255, 255, 255],
// radius: 100000
// }]
// const backgroundLayer = new deck.ScatterplotLayer({
// data:background_data,
// getPosition: d => d.position,
// getFillColor: d => d.color,
// getRadius: d => d.radius,
// opacity: map_morph,
// pickable: true,
// })

// var polygon_data = [
// {polygon: [[0, 0], [0, 100], [100, 100], [100, 0], [0, 0]]}
// ]
// var backgroundLayer = new deck.SolidPolygonLayer({
// polygon_data,
// getPolygon: d => d.contour,
// });
var backgroundLayer = new deck.GridLayer({
id: 'new-grid-layer',
data: [
{COORDINATES: [-73.75, 40.7]}
],
// pickable: true,
// extruded: true,
cellSize: 2000000,
elevationScale: 0,
getPosition: d => d.COORDINATES,
color: [255, 255, 255],
opacity: map_morph
});
const scatterLayer = new deck.ScatterplotLayer({
data: station_data,
getPosition: d => d.position,
getFillColor: d => d.color,
// getRadius: d => d.radius,
getRadius: radius,
opacity: 0.5,
pickable: true,
autoHighlight: true,
stroked: true,
lineWidthMinPixels: 0.5,
onHover: function(info, event){
if ('object' in info){
mutable inst_neighborhood = info.object.Neighborhood
}
},
onClick: function(info, event){
if ('object' in info){
if (info.object.name !== selected_station.name){
// select stations to view paths
mutable path_types = 'all'
mutable selected_station = info.object
} else {
// turn off paths
mutable selected_station = {}
}
}
}
})
const pathLayer = new deck.PathLayer({
data: all_paths,
pickable: true,
widthScale: 1,
widthMinPixels: 1,
getPath: d => d.path,
getColor: d => d.color,
getWidth: d => d.width
});
deckgl.setProps({layers: [backgroundLayer, pathLayer, scatterLayer]});
// deckgl.setProps({layers: [backgroundLayer]});

return scatterLayer
}
Insert cell
all_paths
Insert cell
station_data[0]
Insert cell
mutable inst_neighborhood = ''
Insert cell
deckgl = {
// // reference for below https://observablehq.com/@tomvantilburg/deckgl-mapbox-and-3d-tiles
// // This is an Observable hack: clear previously generated content
container.innerHTML = '';
return new deck.DeckGL({
container,
initialViewState: {
longitude: inst_lng,
latitude: inst_lat,
zoom: inst_zoom,
minZoom: 10,
maxZoom: 15,
},
controller: true,

getTooltip: ({object}) => {
return object && `${object.Neighborhood} \n ${object['cross-x']} and ${object['cross-y']}
`},
// Mapbox settings
// set `map: false` to turn off Mapbox base map
map: mapboxgl,
// This token is for demo-purpose only and rotated regularly. Get your token at https://www.mapbox.com
mapboxApiAccessToken: mapbox_token,
mapStyle: 'mapbox://styles/mapbox/light-v9',
});
}
Insert cell
// toggle_map = //mapboxgl
Insert cell
Insert cell
Insert cell
Insert cell
network = {
network_ini.matrix_colors = {}
network_ini.matrix_colors.pos = 'black'
network_ini.matrix_colors.neg = 'green'
return network_ini
}
Insert cell
function reactiveValue(container, name, value) {
return {
get() {
return value;
},
set(v) {
value = v;
container.dispatchEvent(new CustomEvent("input", {detail: {name, value}}));
return value;
}
};
}
Insert cell
'tmp'.includes('mp')
Insert cell
dendro_stations = Generators.observe(notify => {

const mousemove = () => {
if (cgm.params.tooltip.tooltip_type.includes('dendro')){
mutable selected_station = {
'name': undefined,
}
}

notify(cgm.params.dendro.selected_clust_names)
}
viewof cgm.addEventListener("click", mousemove);
notify(cgm.params.dendro.selected_clust_names);

return () => viewof cgm.removeEventListener("mousemove", mousemove);
})
Insert cell
// selected_departure =
Generators.observe(notify => {
const mouse_click = () => {

if (cgm.params.tooltip.tooltip_type === 'col-label'){
mutable selected_station = {
'name': cgm.params.int.mouseover.col.name,
'position': location_dict[cgm.params.int.mouseover.col.name]
}
mutable path_types = 'departures'

} else if (cgm.params.tooltip.tooltip_type === 'matrix-cell'){
mutable selected_station = {
'name': cgm.params.int.mouseover.col.name,
'position': location_dict[cgm.params.int.mouseover.col.name]
}
mutable path_types = 'single'
mutable filter_single = cgm.params.int.mouseover.row.name
}
notify(cgm.params.int.mouseover.col.name)
}
viewof cgm.addEventListener("click", mouse_click);
notify(cgm.params.int.mouseover.col.name);

return () => viewof cgm.removeEventListener("mousemove", mouse_click);
})
Insert cell
mutable filter_single = ''
Insert cell
// selected_destination =
Generators.observe(notify => {
const mouse_click = () => {

console.log('------>>>', cgm.params.tooltip.tooltip_type)
if (cgm.params.tooltip.tooltip_type === 'row-label'){
mutable selected_station = {
'name': cgm.params.int.mouseover.row.name,
'position': location_dict[cgm.params.int.mouseover.row.name]
}
mutable path_types = 'arrivals'
}
notify(cgm.params.int.mouseover.row.name)
}
viewof cgm.addEventListener("click", mouse_click);
notify(cgm.params.int.mouseover.row.name);

return () => viewof cgm.removeEventListener("mousemove", mouse_click);
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
mutable is_fullscreen = false
Insert cell
function fullscreen({className = 'custom-fullscreen', style = null} = {}) {
// Superfluous bling.
const buttonStyle = style != null ? style : 'font-size:1rem;font-weight:bold;padding:8px;background:hsl(,100%,90%);border:1px solid hsl(40,100%,50%); border-radius:4px;box-shadow:0 .5px 2px 1px rgba(0,0,0,.2);cursor: pointer';
const button = html`<button style="${buttonStyle}">Go fullscreen!`;
// Vanilla version for standards compliant browsers.
if(document.documentElement.requestFullscreen) {
button.onclick = () => {
mutable is_fullscreen = true
const parent = document.documentElement;
parent.requestFullscreen().then(() => {
const cleanup = () => {
if(document.fullscreenElement) return;
parent.classList.remove(className);
document.removeEventListener('fullscreenchange', cleanup);
};
parent.classList.add(className);
// Can't use {once: true}, because event fires too late.
document.addEventListener('fullscreenchange', cleanup);
});
}
}
// Because Safari is the new IE.
else {
const screenfull = require('screenfull@4.2.0/dist/screenfull.js').catch(() => window['screenfull']);
// We would need some debouncing, in case screenfull isn't loaded
// yet and user clicks frantically. Then again, it's Safari.
button.onclick = () => {
mutable is_fullscreen = true
screenfull.then(sf => {
const parent = document.documentElement;
sf.request(parent).then(() => {
const cleanup = () => {
if(sf.isFullscreen) return;
parent.classList.remove(className);
sf.off('change', cleanup);
};
parent.classList.add(className);
sf.on('change', cleanup);
});
});
};
}
// Styles don't rely on the :fullscreen selector to avoid interfering with
// Observable's fullscreen mode. Instead a class is applied to html during
// fullscreen.
return html`
${button}
<style>
html.${className},
html.${className} body {
overflow: auto;
height: 100vh;
width: 100vw;
}
html.${className} body > div {
// display: flex; // disable in order ot have map show up
// flex-direction: col; // disable in order ot have map show up
// flex-wrap: wrap;
background: white;
// height: 100%; // causes tooltip to be too tall
width: auto;
overflow: auto;
position: relative;
}
html.${className} body > div > div {
// margin-bottom: 0 !important;
// margin-bottom: 0;
// margin-top: 0px;
// min-height: 0 !important;
// width: 100vw; // causes tooltip to be too wide
// max-height: 100%;
// overflow: auto;
// padding: .5rem;
// box-sizing: border-box;
}

html.sw body > div > div {
margin-bottom: 0 !important;
margin-top: -5px !important;
// margin-left: 5px !important;
// min-height: 0 !important;
// width: 100vw; // causes tooltip to be too wide
// max-height: 100%;
// overflow: auto;
padding-left: 5px;//.5rem;
// box-sizing: border-box;
}
`;
}
Insert cell
cgm.network
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more