Public
Edited
Mar 13
Insert cell
Insert cell
embed = require("vega-embed@5")
Insert cell
vegalite = require("@observablehq/vega-lite@0.1")
Insert cell
import { aq, op } from '@uwdata/arquero'
Insert cell
viewof trains = aq // viewof shows the table view, but assigns the table value
.fromCSV(await FileAttachment('small_trains.csv').text())
.view({ height: 240 })
Insert cell
viewof gares = aq // viewof shows the table view, but assigns the table value
.fromCSV(await FileAttachment('garesdep@1.csv').text())
.view({ height: 240 })
Insert cell
Insert cell
embed({
$schema: "https://vega.github.io/schema/vega-lite/v5.json",
width: 500,
height: 300,
data: {url: "https://mjlobo.github.io/teaching/eivp/departements.json",
format: {type: "topojson", feature: "departements"} // on doit indiquer quels objets on va dessiner
},
projection: {type :"mercator" },
mark: {
type: "geoshape"
}
})
Insert cell
Insert cell
embed({
$schema: "https://vega.github.io/schema/vega-lite/v5.json",
width: 500,
height: 300,
data: {url: "https://mjlobo.github.io/teaching/eivp/departementswithid.json",
format: {type: "topojson", feature: "departements"}
},
projection: {type :"mercator" },
mark: {
type: "geoshape",
stroke: 'light-grey',
fill: 'grey',
strokeWidth: 0.5
}
})
Insert cell
Insert cell
garespardepartement = gares.groupby("CODE_DEP").count()
Insert cell
embed({
$schema: "https://vega.github.io/schema/vega-lite/v5.json",
width: 500,
height: 300,
data: {
url: "https://mjlobo.github.io/teaching/eivp/departementswithid.json",
format: {type: "topojson", feature: "departements"}
},
transform: [{
lookup: "id", //lookup correspond à la clé dans le tableau d'origine, dans ce cas le topojson
from: {
data: {
values: garespardepartement.objects() // le tableau ou on va chercher les données
},
key: "CODE_DEP", // la clé dans le tableau secondaire
fields: ["count"] // les champs qu'on va recupérer
},
default:'0' // la valeur par default si il n'y a pas de match. Même si on veut un nombre, on doit l'écrire comme un string
}],
projection: {
type: "mercator"
},
mark: "geoshape",
encoding: {
color: {
field: "count",
type: "quantitative"
}
}
})
Insert cell
Insert cell
trainswithdate = trains.derive({date:d=>op.datetime(d.year, d.month-1)})
Insert cell
trainsByStationsAndDate = trainswithdate.groupby('date', 'departure_station', 'arrival_station',).rollup({total_num_trips: d => op.mean(d.total_num_trips), num_late_at_departure: d=>op.mean(d.num_late_at_departure),num_arriving_late: d=>op.mean(d.num_arriving_late), avg_delay_all_departing: d=>op.mean(d.avg_delay_all_departing), avg_delay_all_arriving: d=>op.mean(d.avg_delay_all_arriving)})
Insert cell
trainsByStations = trainsByStationsAndDate.groupby('departure_station', 'arrival_station').rollup({total_num_trips: d => op.sum(d.total_num_trips), num_late_at_departure: d=>op.sum(d.num_late_at_departure),num_arriving_late: d=>op.sum(d.num_arriving_late), avg_delay_all_departing: d=>op.mean(d.avg_delay_all_departing), avg_delay_all_arriving: d=>op.mean(d.avg_delay_all_arriving)})
Insert cell
trainsByDepartureStation = trainsByStations.groupby('departure_station').rollup({total_num_trips: d => op.sum(d.total_num_trips), num_late_at_departure: d=>op.sum(d.num_late_at_departure),num_arriving_late: d=>op.sum(d.num_arriving_late), avg_delay_all_departing: d=>op.mean(d.avg_delay_all_departing), avg_delay_all_arriving: d=>op.mean(d.avg_delay_all_arriving) })
Insert cell
trainsByDepartureStationAndDepartement=trainsByDepartureStation.join_left(gares, ['departure_station', 'LIBELLE']).groupby('CODE_DEP').rollup({total_num_trips: d => op.sum(d.total_num_trips), num_late_at_departure: d=>op.sum(d.num_late_at_departure),num_arriving_late: d=>op.sum(d.num_arriving_late), avg_delay_all_departing: d=>op.mean(d.avg_delay_all_departing), avg_delay_all_arriving: d=>op.mean(d.avg_delay_all_arriving)}).derive({ratio_late_at_departure: d=>d.num_late_at_departure/d.total_num_trips})
Insert cell
embed({
$schema: "https://vega.github.io/schema/vega-lite/v5.json",
width: 500,
height: 300,
data: {
url: "https://mjlobo.github.io/teaching/eivp/departementswithid.json",
format: {type: "topojson", feature: "departements"}
},
transform: [{
lookup: "id", //lookup correspond à la clé dans le tableau d'origine, dans ce cas le topojson
from: {
data: {
values: trainsByDepartureStationAndDepartement.objects() // le tableau ou on va chercher les données
},
key: "CODE_DEP", // la clé dans le tableau secondaire
fields: ["total_num_trips"] // les champs qu'on va recupérer
},
default:'0' // la valeur par default si il n'y a pas de match. Même si on veut un nombre, on doit l'écrire comme un string
}],
projection: {
type: "mercator"
},
mark: "geoshape",
encoding: {
color: {
field: "total_num_trips",
type: "quantitative"
}
}
})
Insert cell
trainsByDepartureStationAndDepartement.view()
Insert cell
Insert cell
embed({
$schema: "https://vega.github.io/schema/vega-lite/v3.json",
width: 400,
height: 300,
layer: [
{
data: {
url: "https://mjlobo.github.io/teaching/eivp/departements.json",
format: {
type: "topojson",
feature: "departements"
}
},
projection: {
type: "mercator"
},
mark: {
type: "geoshape",
fill: "lightgray",
stroke: "white"
}
},
{
data: {
values: gares.objects()
},
projection: {
type: "mercator"
},
mark: "circle",
encoding: {
longitude: {
field: "X_WGS84",
type: "quantitative"
},
latitude: {
field: "Y_WGS84",
type: "quantitative"
},
size: {"value": 20},
color: {"value": "steelblue"}
}
}
]
})
Insert cell
Insert cell
trainsByDepartureWithCoords = trainsByDepartureStation.join_left(gares,['departure_station','LIBELLE']).filter(d=>d.LIBELLE != null).reify()
Insert cell
trainsByDepartureWithCoords.view()
Insert cell
embed({
$schema: "https://vega.github.io/schema/vega-lite/v5.json",
width: 500,
height: 300,
layer: [
{
data: {
url: "https://mjlobo.github.io/teaching/eivp/departements.json",
format: {
type: "topojson",
feature: "departements"
}
},
projection: {
type: "mercator"
},
mark: {
type: "geoshape",
fill: "lightgray",
stroke: "white"
}
},
{
data: {
values: trainsByDepartureWithCoords.objects()
},
params: [
{
name: "selected", //la variable qui va stocker l'objet(s) selectionné
select: {type: "point", "on": "mouseover"}
}
],
projection: {
type: "mercator"
},
mark: {type: "circle"},
encoding: {
longitude: {
field: "X_WGS84",
type: "quantitative"
},
latitude: {
field: "Y_WGS84",
type: "quantitative"
},
size: {
field: "total_num_trips",
type: "quantitative"
},
color: {
condition: {"param": "selected", "value": 'red', "empty": false},
value: 'steelblue'
},
tooltip: [{field: "LIBELLE", "type": "nominal"}, {field: "total_num_trips", type: "quantitative", title: "Nombre de departs"}]
}
}
]
})
Insert cell
Insert cell
embed({
$schema: "https://vega.github.io/schema/vega-lite/v5.json",
width: 400,
height: 300,
layer: [
{
data: {
url: "https://mjlobo.github.io/teaching/eivp/departements.json",
format: {
type: "topojson",
feature: "departements"
}
},
projection: {
type: "mercator"
},
mark: {
type: "geoshape",
fill: "lightgray",
stroke: "white"
}
},
{
params: [
{
name: "selected", //la variable qui va stocker l'objet(s) selectionné
select: {type: "point", "on": "mouseover"} //type de selection, ici on va sélectionner un point lorsque la souris survole le point
}
],
data: {
values: gares.objects()
},
projection: {
type: "mercator"
},
mark:
{
type: "circle",
},
encoding: {
longitude: {
field: "X_WGS84",
type: "quantitative"
},
latitude: {
field: "Y_WGS84",
type: "quantitative"
},
tooltip: [{field: "LIBELLE", "type": "nominal"}],
size: {
condition: {param: "selected", "value": 200, "empty": false}, // ici la condition veut dire que si l'objet correspond à l'objet selected, la tailler sera 100, sinon 50. Le paramètre permet de dire que si la sélection est vide on la considère pas comme dans l'objet "selected".
value: 50
},
color: {"value": "steelblue"}
}
}
]
})
Insert cell
Insert cell
embed({
$schema: "https://vega.github.io/schema/vega-lite/v5.json",
width: 400,
height: 300,
layer: [
{
data: {
url: "https://mjlobo.github.io/teaching/eivp/departements.json",
format: {
type: "topojson",
feature: "departements"
}
},
projection: {
type: "mercator"
},
mark: {
type: "geoshape",
fill: "lightgray",
stroke: "white"
}
},
{
params: [
{
name: "selected", //la variable qui va stocker l'objet(s) selectionné
select: {type: "point", "on": "mouseover"} //type de selection, ici on va sélectionner un point lorsque la souris survole le point
}
],
data: {
values: gares.objects()
},
projection: {
type: "mercator"
},
mark:
{
type: "circle",
},
encoding: {
longitude: {
field: "X_WGS84",
type: "quantitative"
},
latitude: {
field: "Y_WGS84",
type: "quantitative"
},
tooltip: [{field: "LIBELLE", "type": "nominal"}, {field: "DEPARTEMEN", "type": "nominal"}],
size: {
condition: {param: "selected", "value": 200, "empty": false}, // ici la condition veut dire que si l'objet correspond à l'objet selected, la tailler sera 100, sinon 50. Le paramètre permet de dire que si la sélection est vide on la considère pas comme dans l'objet "selected".
value: 50
},
color: {"value": "steelblue"}
}
}
]
})
Insert cell
embed({
$schema: "https://vega.github.io/schema/vega-lite/v5.json",
width: 400,
height: 300,
layer: [
{
data: {
url: "https://mjlobo.github.io/teaching/eivp/departements.json",
format: {
type: "topojson",
feature: "departements"
}
},
projection: {
type: "mercator"
},
mark: {
type: "geoshape",
fill: "lightgray",
stroke: "white"
}
},
{
params: [
{
name: "selected", //la variable qui va stocker l'objet(s) selectionné
select: {type: "point", "on": "mouseover"} //type de selection, ici on va sélectionner un point lorsque la souris survole le point
}
],
data: {
values: trainsByDepartureWithCoords.objects()
},
projection: {
type: "mercator"
},
mark:
{
type: "circle",
},
encoding: {
longitude: {
field: "X_WGS84",
type: "quantitative"
},
latitude: {
field: "Y_WGS84",
type: "quantitative"
},
tooltip: [{field: "LIBELLE", "type": "nominal"}, {field:"total_num_trips", "type":"quantitative"}],
size: {
value: 50
},
color: {
condition: {param:"selected", value: "red", empty: false},
value: "steelblue"
}
}
}
]
})
Insert cell
trainsByDepartureWithCoords.view()
Insert cell
Insert cell
maxNumTrains = trainsByStations.rollup({ maxNumTrains: d => op.max(d.total_num_trips) }).objects()[0].maxNumTrains
Insert cell
embed({
$schema: "https://vega.github.io/schema/vega-lite/v5.json",
width: 600,
height: 300,
params:
[
{
name: "MinNumTrains", "value": 0,
bind: {"input": "range", "min": 0, "max": maxNumTrains, "step": 100} // on definit le type de l'input et les valeurs minimales et maximales possibles
},
{
name: "MaxNumTrips", "value": 0,
bind: {"input": "range", "min": 0, "max": maxNumTrains, "step": 100} // on definit le type de l'input et les valeurs minimales et maximales possibles
},
],
layer: [
{
data: {
url: "https://mjlobo.github.io/teaching/eivp/departements.json",
format: {
type: "topojson",
feature: "departements"
}
},
projection: {
type: "mercator"
},
mark: {
type: "geoshape",
fill: "lightgray",
stroke: "white"
}
},
{
data: {
values: gares.objects()
},
projection: {
type: "mercator"
},
mark: {type: "point"},
encoding: {
longitude: {
field: "X_WGS84",
type: "quantitative"
},
latitude: {
field: "Y_WGS84",
type: "quantitative"
},
tooltip: {field: "LIBELLE", "type": "nominal"},
size: {"value": 20},
color: {"value": "steelblue"}
}
},
{ //on ajoute une layer pour représenter les trajets
data: {
values: trainsByStations.objects()
},
transform: [
{
lookup: "departure_station", // on va chercher les coordonnées des gares avec lookup
from: {
data: {values: gares.objects()},
key: "LIBELLE",
fields: ["X_WGS84", "Y_WGS84"],
},
as: ["X_WGS84_departure", "Y_WGS84_departure"]
},
{
lookup: "arrival_station",
from: {
data: {values: gares.objects()},
key: "LIBELLE",
fields: ["X_WGS84", "Y_WGS84"],
},
as: ["X_WGS84_arrival", "Y_WGS84_arrival"]
},
{filter: "datum.X_WGS84_departure != null && datum.Y_WGS84_departure != null && datum.X_WGS84_arrival != null && datum.Y_WGS84_arrival != null && datum.total_num_trips>MinNumTrains && datum.total_num_trips < MaxNumTrips"} //on ajoute un filtre pour ne pas considerer les trajets avec des gares qui n'ont pas de coordonnées, et pour utiliser le valeur venant du slider.
],
projection: {
type: "mercator"
},
mark: "rule",
encoding: {
longitude: {
field: "X_WGS84_departure",
type: "quantitative"
},
latitude: {
field: "Y_WGS84_departure",
type: "quantitative"
},
longitude2: {
field: "X_WGS84_arrival",
type: "quantitative"
},
latitude2: {
field: "Y_WGS84_arrival",
type: "quantitative"
},
color: {"field": "total_num_trips", type: "quantitative"}
}
}
]
})
Insert cell
Insert cell
Insert cell
embed({
$schema: "https://vega.github.io/schema/vega-lite/v5.json",
width: 400,
height: 300,
hconcat:[ // les vues dans se tableau vont être disposées horizontalement
{
layer: [
{
data: {
url: "https://mjlobo.github.io/teaching/eivp/departements.json",
format: {
type: "topojson",
feature: "departements"
}
},
projection: {
type: "mercator"
},
mark: {
type: "geoshape",
fill: "lightgray",
stroke: "white"
}
},
{
data: {
values: trainsByDepartureWithCoords.objects()
},
params: [
{
name: "selected", //la variable qui va stocker l'objet(s) selectionné
select: {type: "point", "on": "mouseover"}
}
],
projection: {
type: "mercator"
},
mark: {type: "circle"},
encoding: {
longitude: {
field: "X_WGS84",
type: "quantitative"
},
latitude: {
field: "Y_WGS84",
type: "quantitative"
},
size: {
//field: "total_num_trips",
field: "avg_delay_all_arriving",
type: "quantitative"
},
color: {
condition: {"param": "selected", "value": 'red', "empty": false},
value: 'steelblue'
},
tooltip: [{field: "LIBELLE", "type": "nominal"}, {field: "total_num_trips", type: "quantitative", title: "Nombre de departs"}]
}
}
]},
{
layer: [
{
data: {
url: "https://mjlobo.github.io/teaching/eivp/departements.json",
format: {
type: "topojson",
feature: "departements"
}
},
projection: {
type: "mercator"
},
mark: {
type: "geoshape",
fill: "lightgray",
stroke: "white"
}
},
{
data: {
values: trainsByDepartureWithCoords.objects()
},
params: [
{
name: "selected", //la variable qui va stocker l'objet(s) selectionné
select: {type: "point", "on": "mouseover"}
}
],
projection: {
type: "mercator"
},
mark: {type: "point"},
encoding: {
longitude: {
field: "X_WGS84",
type: "quantitative"
},
latitude: {
field: "Y_WGS84",
type: "quantitative"
},
size: {
//field: "num_late_at_departure",
field: "avg_delay_all_departing",
type: "quantitative"
},
color: {
condition: {"param": "selected", "value": 'red', "empty": false},
value: 'steelblue'
},
tooltip: [{field: "LIBELLE", "type": "nominal"}, {field: "total_num_trips", type: "quantitative", title: "Nombre de departs"}]
}
}
]
}
]
})
Insert cell
createViz = (field)=>{
let viz = [
{
data: {
url: "https://mjlobo.github.io/teaching/eivp/departements.json",
format: {
type: "topojson",
feature: "departements"
}
},
projection: {
type: "mercator"
},
mark: {
type: "geoshape",
fill: "lightgray",
stroke: "white"
}
},
{
data: {
values: trainsByDepartureWithCoords.objects()
},
params: [
{
name: "selected", //la variable qui va stocker l'objet(s) selectionné
select: {type: "point", "on": "mouseover"}
}
],
projection: {
type: "mercator"
},
mark: {type: "circle"},
encoding: {
longitude: {
field: "X_WGS84",
type: "quantitative"
},
latitude: {
field: "Y_WGS84",
type: "quantitative"
},
size: {
//field: "num_late_at_departure",
field: field,
type: "quantitative",
legend: {
title: field
}
},
color: {
condition: {"param": "selected", "value": 'red', "empty": false},
value: 'steelblue'
},
tooltip: [{field: "LIBELLE", "type": "nominal"}, {field: "total_num_trips", type: "quantitative", title: "Nombre de departs"}]
}
}
]
return viz
}
Insert cell
embed({
$schema: "https://vega.github.io/schema/vega-lite/v5.json",
width: 400,
height: 300,
hconcat:[ // les vues dans se tableau vont être disposées horizontalement
{
layer: createViz("num_late_at_departure")
},
{
layer: createViz("num_arriving_late")
},
{
layer: createViz("total_num_trips")
}
]
})
Insert cell
Insert cell
trainsByStationFiltered = trainsByStations.semijoin(gares,['arrival_station', 'LIBELLE']).semijoin(gares,['departure_station', 'LIBELLE']).orderby('total_num_trips').reify().slice(0,40)
Insert cell
embed({
$schema: "https://vega.github.io/schema/vega-lite/v5.json",
hconcat: [
{
width:300,
height:300,
data: {
values: trainsByStationFiltered.objects()
},
params: [
{
name: "selected",
select: {type: "point"}
}
],
config: {
view: {
strokeWidth: 0,
step: 13
},
axis: {
domain: false
}
},
mark: "rect",
encoding: {
x: {
field: "departure_station",
type: "nominal"
},
y: {
field: "arrival_station",
type: "nominal",
},
color: {
field: "total_num_trips",
type: "quantitative",
legend: {
"title": null
}
},
stroke: {
condition: {
param: 'selected', value: 'orange', "empty": false
},
value:null
}
}
},
{
layer: [
{
data: {
url: "https://mjlobo.github.io/teaching/eivp/departements.json",
format: {
type: "topojson",
feature: "departements"
}
},
projection: {
type: "mercator"
},
mark: {
type: "geoshape",
fill: "lightgray",
stroke: "white"
}
},
{
data: {
values: gares.objects()
},
projection: {
type: "mercator"
},
mark: {type: "point"},
encoding: {
longitude: {
field: "X_WGS84",
type: "quantitative"
},
latitude: {
field: "Y_WGS84",
type: "quantitative"
},
tooltip: {field: "LIBELLE", "type": "nominal"},
size: {"value": 20},
color: {"value": "steelblue"}
}
},
{ //on ajoute une layer pour représenter les trajets
data: {
values: trainsByStationFiltered.objects()
},
params: [
{
name: "selected",
select: {type: "point"}
}
],
transform: [
{
lookup: "departure_station", // on va chercher les coordonnées des gares avec lookup
from: {
data: {values: gares.objects()},
key: "LIBELLE",
fields: ["X_WGS84", "Y_WGS84"],
},
as: ["X_WGS84_departure", "Y_WGS84_departure"]
},
{
lookup: "arrival_station",
from: {
data: {values: gares.objects()},
key: "LIBELLE",
fields: ["X_WGS84", "Y_WGS84"],
},
as: ["X_WGS84_arrival", "Y_WGS84_arrival"]
},
{filter: "datum.X_WGS84_departure != null && datum.Y_WGS84_departure != null && datum.X_WGS84_arrival != null && datum.Y_WGS84_arrival != null"} //on ajoute un filtre pour ne pas considerer les trajets avec des gares qui n'ont pas de coordonnées, et pour utiliser le valeur venant du slider.
],
projection: {
type: "mercator"
},
mark: "rule",
encoding: {
longitude: {
field: "X_WGS84_departure",
type: "quantitative"
},
latitude: {
field: "Y_WGS84_departure",
type: "quantitative"
},
longitude2: {
field: "X_WGS84_arrival",
type: "quantitative"
},
latitude2: {
field: "Y_WGS84_arrival",
type: "quantitative"
},
color: {
condition: {param: 'selected', value: 'orange', empty: false},
value: 'steelblue'
}
}
}
]
}
]
})
Insert cell
md` ## Pour aller plus loin
### Exercises
1. Dans la visualisation de flux, activez la sélection afin de que lorsque vous positionnez la souris sur une gare, on voit seulement les flux allant ou venant à cette gare.
2. Modifiez la vue combinée matrice O/D et carte de flux d'après vous suggestions d'encodage.`
Insert cell
embed({
$schema: "https://vega.github.io/schema/vega-lite/v5.json",
width: 500,
height: 300,
layer: [
{
data: {
url: "https://mjlobo.github.io/teaching/eivp/departements.json",
format: {
type: "topojson",
feature: "departements"
}
},
projection: {
type: "mercator"
},
mark: {
type: "geoshape",
fill: "lightgray",
stroke: "white"
}
},
{
data: {
values: trainsByStations.objects()
},
params: [{name: "paintbrush",select: {type: "point", "on": "mouseover", "fields": ["arrival_station"]}, "nearest": true}],
transform: [
{aggregate: [{op: "count", as: "routes"}], groupby: ["arrival_station"]},
{
lookup: "arrival_station",
from: {
data: {values: gares.objects()},
key: "LIBELLE",
fields: ["X_WGS84", "Y_WGS84"]
}
},
{filter: "datum.X_WGS84 != null && datum.Y_WGS84 != null"}
],
projection: {
type: "mercator"
},
mark: {type: "point"},
encoding: {
longitude: {
field: "X_WGS84",
type: "quantitative"
},
latitude: {
field: "Y_WGS84",
type: "quantitative"
},
tooltip: {field: "arrival_station", "type": "nominal"},
size: {
condition: {"param": "paintbrush", "value": 300, "empty": false},
value: 50
},
color: {"value": "steelblue"}
}
},
{
data: {
values: trainsByStations.objects()
},
transform: [
{
lookup: "departure_station",
from: {
data: {values: gares.objects()},
key: "LIBELLE",
fields: ["X_WGS84", "Y_WGS84"],
},
//as: ["X_WGS84_departure", "Y_WGS84_departure"]
},
{
lookup: "arrival_station",
from: {
data: {values: gares.objects()},
key: "LIBELLE",
fields: ["X_WGS84", "Y_WGS84"],
},
as: ["X_WGS84_arrival", "Y_WGS84_arrival"]
},
{
filter: {
param: "paintbrush",
//"empty": false
}
},
{filter: "datum.X_WGS84!= null && datum.Y_WGS84 != null && datum.X_WGS84_arrival!= null && datum.Y_WGS84_arrival != null"},
],
projection: {
type: "mercator"
},
mark: "rule",
encoding: {
longitude: {
field: "X_WGS84",
type: "quantitative"
},
latitude: {
field: "Y_WGS84",
type: "quantitative"
},
longitude2: {
field: "X_WGS84_arrival",
type: "quantitative"
},
latitude2: {
field: "Y_WGS84_arrival",
type: "quantitative"
},
// color: {"value": "steelblue"},
color: {
field: "total_num_trips",
type: "quantitative"
}
}
}
]
})
Insert cell
embed({
$schema: "https://vega.github.io/schema/vega-lite/v5.json",
hconcat: [
{
width: 300,
height: 300,
data: {
values: trainsByStationFiltered.objects(),
},
params: [
{
name: "selected",
select: { type: "point" },
},
],
config: {
view: {
strokeWidth: 0,
step: 13,
},
axis: {
domain: false,
},
},
mark: "rect",
encoding: {
x: {
field: "departure_station",
type: "nominal",
},
y: {
field: "arrival_station",
type: "nominal",
},
color: {
field: "total_num_trips",
type: "quantitative",
legend: {
title: null,
},
},
stroke: {
condition: {
param: "selected",
value: "orange",
empty: false,
},
value: null,
},
},
},
{
layer: [
{
data: {
url: "https://mjlobo.github.io/teaching/eivp/departements.json",
format: {
type: "topojson",
feature: "departements",
},
},
projection: {
type: "mercator",
},
mark: {
type: "geoshape",
fill: "lightgray",
stroke: "white",
},
},
{
data: {
values: gares.objects(),
},
projection: {
type: "mercator",
},
mark: { type: "point" },
encoding: {
longitude: {
field: "X_WGS84",
type: "quantitative",
},
latitude: {
field: "Y_WGS84",
type: "quantitative",
},
tooltip: { field: "LIBELLE", type: "nominal" },
size: { value: 20 },
color: { value: "steelblue" },
},
},
{
data: {
values: trainsByStationFiltered.objects(),
},
params: [
{
name: "selected",
select: { type: "point" },
},
],
transform: [
{
lookup: "departure_station",
from: {
data: { values: gares.objects() },
key: "LIBELLE",
fields: ["X_WGS84", "Y_WGS84"],
},
as: ["X_WGS84_departure", "Y_WGS84_departure"],
},
{
lookup: "arrival_station",
from: {
data: { values: gares.objects() },
key: "LIBELLE",
fields: ["X_WGS84", "Y_WGS84"],
},
as: ["X_WGS84_arrival", "Y_WGS84_arrival"],
},
{
filter:
"datum.X_WGS84_departure != null && datum.Y_WGS84_departure != null && datum.X_WGS84_arrival != null && datum.Y_WGS84_arrival != null",
},
],
projection: {
type: "mercator",
},
mark: "rule",
encoding: {
longitude: {
field: "X_WGS84_departure",
type: "quantitative",
},
latitude: {
field: "Y_WGS84_departure",
type: "quantitative",
},
longitude2: {
field: "X_WGS84_arrival",
type: "quantitative",
},
latitude2: {
field: "Y_WGS84_arrival",
type: "quantitative",
},
color: {
condition: { param: "selected", value: "orange", empty: false },
field: "total_num_trips",
type: "quantitative",
scale: {
scheme: "plasma",
},
},
},
},
],
},
],
});
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