Public
Edited
Jun 6, 2023
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

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more