Public
Edited
Jul 22, 2021
Insert cell
md`# Observable: Créez et Publiez votre dataviz sur le web
<figure><img alt="Girl reading" style="width: 100%; max-width: 440px;" src="https://raw.githubusercontent.com/ToulouseDataViz/meetup_observable-master/master/data/Grand_Logo.png"></figure>`
Insert cell
md`## Etape 1: Trouver des données

* #### Jeu 1: Historique des températures dans les grandes villes d'Occitanie
* #### Jeu 2: Evolution du taux de CO2 dans l'atmosphère

<figure><a target="_blank" href="https://fr.co2.earth/"><img src="https://raw.githubusercontent.com/ToulouseDataViz/meetup_observable-master/master/data/co2_earth.png" alt="" title="Click to enlarge" style="width: 100%; max-width: 100px;" /></a>

<a target="_blank" href="https://www.noaa.gov/"><img src="https://raw.githubusercontent.com/ToulouseDataViz/meetup_observable-master/master/data/noaa.png" alt="" title="Click to enlarge" style="width: 100%; max-width: 100px;" /></a></figure>

Retrouver les jeux de données sur le <a target="_blank" href="https://github.com/ToulouseDataViz/meetup_observable-master">GitHub Toulouse-Dataviz</a> et télécharger le fichier CO2_history.csv en local sur votre ordinateur. Dans l'onglet data, cliquer sur le fichier CO2_history.csv puis sur raw. le fichier s'affiche dans le navigateur. Faire un click droit et sélectionner "enregistrer sous...".
`
Insert cell
md`## Etape 2: Charger les données locales dans le notebook observable`
Insert cell
viewof f= html`<input type=file accept="text/*">`
Insert cell
data_co2 = d3.csvParse(await Files.text(f))
Insert cell
md`## Etape 3: Créer une viz avec la librairie <a target="_blank" href="https://vega.github.io/vega-lite/examples/">VEGA-LITE</a>
(voir <a target="_blank" href="http://toulouse-dataviz.fr/evenements">meetup #24</a> et la [présentation]("https://observablehq.com/@alainro/introduction-a-vega-lite") d'Alain Roan sur Vega-Lite).
C'est simple et rapide....`
Insert cell
md`<dl>
<dt>a. Chargement de la librairie.</dt>`
Insert cell
vl = require("@observablehq/vega-lite@0.0")
Insert cell
md`<dl>
<dt>b. Construction du graphique en ligne.</dt>`
Insert cell
co2_graph_vega = vl({
"title": "Evolution du taux de CO2 dans l'atmosphère",
"data": {
"values": data_co2},
"width" :800,
"height" : 300,
"mark": {
"type": "line",
"color": "red"
},
"encoding": {
"x": {
"Unit": "number",
"field": "Date",
"type": "quantitative",
},
"y": {
"unit": "number",
"field": "CO2 en ppm",
"scale": {"zero": true}
}
},
"config": {"background": "#fff7f3"}
})

Insert cell
md`## Etape 4: Créer une viz avec la librairie <a target="_blank" href="https://c3js.org/examples.html">C3</a> (basée sur d3.js)
Autre variante, autres fonctionalités..`

Insert cell
md`a. Chargement des données depuis github et traitement`
Insert cell
data_co2_git = d3.csv("https://raw.githubusercontent.com/ToulouseDataViz/meetup_observable-master/master/data/CO2_history.csv", v => {
const temp = {}
temp.Date= +v.Date
temp['CO2']= +v['CO2 en ppm']
return temp
})
Insert cell
md`<dl>
<dt>b. Chargement des librairies d3.js et c3.js.</dt>`
Insert cell
d3 = require('d3@v5')
Insert cell
c3 = {
window.d3 = await require("d3@5");
const c3 = await require("c3@0.6.10/c3.min.js");
const link = html`<link rel=stylesheet href=${await require.resolve("c3@0.6/c3.min.css")}>`;
if (c3._link) c3._link.remove();
document.body.append(c3._link = link);
return function(spec) {
const div = html`<div>`;
document.body.appendChild(div);
c3.generate(Object.assign({bindto: div}, spec));
document.body.removeChild(div);
return div;
};
}
Insert cell
md`b. Construction du graphique en ligne.`
Insert cell
co2_chart3 ={
var content = html`
<div id="chart_co2_container2" </div>`
yield content
// Avec c3.js, les données x et y doivent être sous forme de variables indépendantes
// La première ligne doit contenir le header
var years= [];
var co2_rate = ["Evolution du taux de CO2 dans l'atmosphère"];
for (var i=0; i< data_co2_git.length; i++){
years.push(data_co2_git[i]['Date']),
co2_rate.push(data_co2_git[i]['CO2'])
}
var chart = c3({
bindto :'#chart_co2_container2',
title: {
text: 'Evolution du taux de CO2 dans l\'atmosphère'
},
size: {
height: 400,
width: 950
},
data:{
columns: [co2_rate],
},
grid: {
x: {
show: true
},
y: {
show: true
}
},
axis :{
y: {
label: 'Taux de CO2 en ppm',
max: 400,
min: 0
},
x:{
label: 'Date',
max: 2100,
min: 0,
type: 'category',
categories :years,
tick: {
multiline : false,
values : ["0","500","1000","1500","1850","1950", "2050"],
}
}
},
zoom: {enabled: true},
point: {
show: false
}
});
d3.select('#chart_co2_container2').style('background-color', "#fff7f3")
}
Insert cell
md`## Etape 5: Créer une viz avec la librairie d3.js
pour une viz sur mesure du jeu 1.`
Insert cell
md`b. Filtrage des données: Années > 1850`
Insert cell
data_co2_git_filter= data_co2_git.filter(function(d) { return d.Date >=1500 })
Insert cell
md`c. Construction du graphique en ligne.`
Insert cell
chart2 = {

var content = html`
<div id="chart_co2_container" >
</div>`
yield content
const svg= d3.select('#chart_co2_container')
.append("svg")
.attr("class", "svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//const svg = d3.select(DOM.svg(width, height));
//=============================== Axes ====================================
svg.append("g")
.call(xAxis);

svg.append("g")
.call(yAxis);
//=============================== Ligne ====================================
svg.append("path")
.datum(data_co2_git_filter)
.attr("d", line)
.attr("fill", "none")
.style("stroke", "steelblue")
.style("stroke-width", "2px");
//=============================== grille =====================================
svg.append("g")
.attr("class", "grid")
.attr("transform", `translate(${margin.left},0)`)
.call(make_y_gridlines()
.tickSize(-width)
.tickFormat("")
);

svg.append("g")
.attr("class", "grid")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(make_x_gridlines()
.tickSize(-height)
.tickFormat("")
)
d3.selectAll(".grid")
.style("color","lightgrey")
.style("stroke-opacity", 0,2);
//======================== configuration du gradient de l'aire =================
svg.append("linearGradient")
.attr("id", "area-gradient")
.attr("gradientUnits", "userSpaceOnUse")
.attr("x1", 0).attr("y1", y(270))
.attr("x2", 0).attr("y2", y(400))
.selectAll("stop")
.data([
{offset: "0%", color: "white"},
{offset: "20%", color: "lightgray"},
{offset: "50%", color: "red"},
{offset: "75%", color: "black"},
{offset: "100%", color: "black"},
])
.enter().append("stop")
.attr("offset", function(d) { return d.offset; })
.attr("stop-color", function(d) { return d.color; })
.attr("stop-opacity", .8);
//================================== aire =====================================
svg.append("path")
.data([data_co2_git_filter])
.attr("class", "area")
.attr("d", area);
d3.selectAll(".area")
.style("fill", "url(#area-gradient)")
.style("stroke-width", "0px");

//=============================== cercles ====================================
svg.selectAll("dot")
.data(data_co2_git_filter)
.enter().append("circle")
.style("fill", "lightgray")
.attr("r",2)
.attr("cx", function(d) { return x(d.Date); })
.attr("cy", function(d) { return y(d['CO2']); })
.append("title")
.text(function (d) {
return "date : "+d.Date+"\nco2 : "+d.CO2+" ppm"
})
//======================== Titre axes =====================================
svg.append("text")
.attr("y",10)
.attr("x",35)
.attr("font-size", 14)
.style("text-anchor", "start")
.text("CO2 en ppm");
svg.append("text")
.attr("y",height-margin.bottom+20)
.attr("x",width)
.attr("font-size", 14)
.style("text-anchor", "end")
.text("Date");
//======================== Titre graphique =====================================
svg.append("text")
.attr("y",height)
.attr("x",width/2)
.attr("font-size", 14)
.style("text-anchor", "middle")
.text("Evolution du taux de CO2 dans l'atmosphère");
//======================== Commentaires =====================================
var texts = svg.append("g").selectAll("text").data(events).enter();
texts.append('text')
.text(function(d){ return d.text})
.attr('class', 'barsEndlineText')
.attr('text-anchor', 'end')
.attr("x", function(d){ return x(d.x)})
.attr("y", function(d){ return y(d.an.CO2+d.em);})
.attr("font-size", 14)
.attr("fill", function(d) {return color_text(d.an.Date)})
texts.append("line")
.attr("x1", function(d){ return x(d.an.Date);})
.attr("y1", function(d){ return y(d.an.CO2);})
.attr("x2", function(d){ return x(d.x)})
.attr("y2", function(d){ return y(d.an.CO2);})
.attr("stroke-width", 1)
//.attr("stroke", "black")
.attr("stroke", function(d) {return color_text(d.an.Date)})
.attr("stroke-dasharray", "6")
return svg;
//return svg.node();
}
Insert cell
//
Insert cell
md`d. Variable et Fonctions associées au graphique`
Insert cell
height = 800
Insert cell
//width =950
Insert cell
margin = ({top: 20, right: 30, bottom: 50, left: 40})
Insert cell
color_text = d3.scaleLinear().domain([1970, 2018])
.range(["red", "black"])
Insert cell
x = d3.scaleLinear()
.domain(d3.extent(data_co2_git_filter, d => d.Date))
.range([margin.left, width - margin.right])

Insert cell
xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).ticks(width / 80,'d').tickSizeOuter(0))

Insert cell
y = d3.scaleLinear()
.domain([270, d3.max(data_co2_git_filter, d => d['CO2'])]).nice()
.range([height - margin.bottom, margin.top])
Insert cell
yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y))
.call(g => g.select(".domain").remove())
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", 3)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text(data_co2_git_filter.y))
Insert cell
function make_x_gridlines() {
return d3.axisBottom(x)
.ticks(20)
}
Insert cell
function make_y_gridlines() {
return d3.axisLeft(y)
.ticks(10)
}
Insert cell
line = d3.line()
.defined(d => !isNaN(d['CO2']))
.x(d => x(d.Date))
.y(d => y(d['CO2']))
Insert cell
// Configuration Aire
area = d3.area()
.x(function(d) { return x(d.Date); })
.y0(height - margin.bottom)
.y1(function(d) { return y(d['CO2']); });
Insert cell
md`e. Liste des commentaires`
Insert cell
events = [
{x : 1975, em :0, an : data_co2_git_filter.find(d => d.Date ==1979), text :"En 1979, Haroun Tazieff déclarait que la production de CO2 produirait un effet de serre et éleverait la température de 2 ou 3 degrés"},
{x : 1975,em :0, an : data_co2_git_filter.find(d => d.Date ==1988), text :"En 1988, Le GIEC est créé pour fournir des études sur les changements climatiques et leurs répercussions potentielles."},
{x : 1975,em :0, an : data_co2_git_filter.find(d => d.Date ==1992), text :"En 1992, le sommet de Rio fixe des lignes d’action non contraignantes pour une meilleure gestion de la planète."},
{x : 1975,em :0, an : data_co2_git_filter.find(d => d.Date ==2006), text: "En 2006, Al Gore sort le film 'La vérité qui dérange'"},
{x : 1975,em :0, an : data_co2_git_filter.find(d => d.Date ==2002), text: "En 2002, le président français Jacques Chirac déclarait « Notre maison brûle et nous regardons ailleurs »"},
{x : 1975,em :0, an : data_co2_git_filter.find(d => d.Date ==2015), text :"En 2015 à Paris, 196 pays signent des accords non contraignants pour limiter la hausse de T° bien en dessous de 2 °C"},
{x : 1975,em :-1, an : data_co2_git_filter.find(d => d.Date ==2017), text: "En 2017, les états unis se retirent des accords de Paris."},
{x : 1975,em :1, an : data_co2_git_filter.find(d => d.Date ==2018), text :"En 2018, aucun pays n’est aligné sur les objectifs fixés."},
{x : 1968, em :0,an : data_co2_git_filter.find(d => d.Date ==1972), text :"En 1972, le rapport Meadow souligne les dangers pour la planète de la croissance économique et démographique"}]
Insert cell
md`e. Elements de style CSS associé au graphique`
Insert cell
html`<style>
body {background-color: #ffffeb;}
</style>`

Insert cell
md`## Etape 6: Créer une viz "reactive" avec la librairie d3.js
pour une viz sur mesure du jeu 2.`
Insert cell
md`a. Chargement des données depuis github et traitement`
Insert cell
// transformation des noms des champs, du format des données string => number et découposition de la date
data_temp = d3.csv("https://raw.githubusercontent.com/ToulouseDataViz/meetup_observable-master/master/data/occitanie_history.csv", ({NAME,ELEVATION,DATE,PRCP,SNWD,TAVG,TMAX,TMIN}) => ({name : NAME, date : DATE, year : formatYear(parseTime(DATE)), month : formatMonth(parseTime(DATE)), day : formatDay(parseTime(DATE)), dayYear : formatDayYear(parseTime(DATE)),ELEVATION,PRCP,SNWD,TAVG,TMAX,TMIN}))
Insert cell
md`b. fonctions de traitement du format de la date associés`
Insert cell
parseTime = d3.timeParse("%d/%m/%Y");
Insert cell
formatDay = d3.timeFormat("%d")
Insert cell
formatMonth = d3.timeFormat("%B")
Insert cell
formatYear = d3.timeFormat("%Y")
Insert cell
formatDayYear = d3.timeFormat("%j")
Insert cell
md`b. Recherche du nom des villes dans le jeu de données`
Insert cell
ville = data_temp.map(d=>d.name)
Insert cell
villeListe =Array.from(new Set(ville));
Insert cell
new Set(ville)
Insert cell
md`c. Création selecteur de ville`
Insert cell
viewof selectedVille = dropdown(villeListe)
Insert cell
import {dropdown} from "@rohscx/inputs"
Insert cell
data_temp_filter = data_temp.filter(d=> d.name == selectedVille && +d.year <= 2017)
Insert cell
md`c. Construction du graphique carte de chaleur.`
Insert cell
temp_chart = {
var content = html`
<div id="chart_temp_container">
</div>`;
yield content;

//var height = 1200;
var margin = { top: 60, right: 30, bottom: 120, left: 40 };
//var width = Math.max(Math.min(window.innerWidth, 1000), 500) - margin.left - margin.right
//var width = Math.max(Math.min(window.innerWidth, 1000), 500) - margin.left - margin.right
var gridSizeDay = Math.floor(width / days.length) * 1.2;
var gridSizeYear = Math.floor(width / days.length) * 4;
var height = gridSizeYear * years.length;

var maingroup = d3
.select('#chart_temp_container')
.append("svg")
.attr("class", "svg")
.attr("width", 1600 + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// ====================================== LABEL YEAR ================================
var yearsLabels = maingroup
.selectAll(".yearsLabel")
.data(years)
.enter()
.append("text")
.attr("font-size", 12)
.text(function(d) {
return d % 5 == 0 ? d : "";
})
.attr("x", 0)
.attr("y", function(d, i) {
return i * gridSizeYear;
})
.attr("transform", "translate(-4," + gridSizeYear / 2 + ")")
.style("text-anchor", "end");

// ====================================== LABEL MONTH ================================
var monthsLabels = maingroup
.selectAll(".monthLabel")
.data(days)
.enter()
.append("text")
.attr("class", "textmonthLabel")
.text(function(d) {
return +d % 30 == 0 ? monthName(d) : "";
})
.attr("x", function(d, i) {
return (i - 5) * gridSizeDay;
})
.attr("font-size", 10)
.attr("y", 0)
.attr("transform", "translate(" + gridSizeDay / 2 + ", -6)")
.style("text-anchor", "end");

var monthBar = maingroup
.selectAll(".monthBar")
.data([31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365])
.enter()
.append("line")
.attr("x1", function(d) {
return d * gridSizeDay;
})
.attr("y1", 0)
.attr("x2", function(d) {
return d * gridSizeDay;
})
.attr("y1", -10)
.style("stroke", "blue")
.style("stroke-opacity", 1)
.style("fill", "blue");

//================================ TEXTE===========================================
maingroup
.append("text")
.attr("class", "credit")
.attr("x", 0)
.attr("y", gridSizeYear * (years.length + 1) + 15)
.style("text-anchor", "start")
.attr("font-size", 10)
.text(
"Basé sur les données de la NOAA (National Oceanic and Atmosphéric Administration - USA)"
);

maingroup
.append("text")
.attr("class", "title")
.attr("x", width / 2)
.attr("y", -45)
.style("text-anchor", "middle")
.text(
name +
" - Températures journalières minimales de " +
minDate +
" à " +
maxDate
);

maingroup
.append("text")
.attr("class", "subtitle")
.attr("x", width / 2)
.attr("y", -25)
.attr("font-size", 12)
.style("text-anchor", "middle")
.text("(" + data_temp_filter.length + " mesures)");

//================================ HEATMAP ========================================
var heatMap = maingroup
.selectAll(".hour")
.data(data_temp_filter)
.enter()
.append("rect")
.attr("x", function(d) {
return d.dayYear * gridSizeDay;
})
.attr("y", function(d) {
return (d.year - 1958) * gridSizeYear;
})
.attr("width", gridSizeDay)
.attr("height", gridSizeYear)
.style("stroke", "none")
.style("stroke-opacity", 0)
.style("fill", function(d) {
return colorScale(d.TMIN);
})
.append("title")
.text(function(d) {
return "date : " + d.date + "\nTempérature : " + d.TMIN + " °C";
});

//==================================== LEGEND ===========================================

var legendSize = 5;
var legendMap = maingroup
.selectAll(".legend")
.data(d3.range(minScale, maxScale + 1, 1))
.enter()
.append("rect")
.attr("x", function(d) {
return width / 2.5 + d * legendSize * gridSizeDay;
})
.attr("y", gridSizeYear * (years.length + 1) + 50)
.attr("width", gridSizeDay * legendSize)
.attr("height", gridSizeYear * 2)
.style("stroke", "none")
.style("stroke-opacity", 0)
.style("fill", function(d) {
return colorScale(d);
});

maingroup
.append("text")
.attr("class", "legendTitle")
.attr("x", width / 2)
.attr("y", gridSizeYear * years.length + 50)
.attr("font-size", 14)
.style("text-anchor", "middle")
.text("Echelle des températures");

var legenLabels = maingroup
.selectAll(".legendLabel")
.data(d3.range(minScale, maxScale + 1, 1))
.enter()
.append("text")
.attr("font-size", 10)
.text(function(d) {
return d % 5 == 0 ? d : "";
})
.attr("x", function(d) {
return (
width / 2.5 + legendSize * gridSizeDay + d * legendSize * gridSizeDay
);
})
.attr("y", gridSizeYear * (years.length + 1) + 80)
.attr("transform", "translate(-4," + gridSizeYear / 2 + ")")
.attr("font-size", 10)
.style("text-anchor", "middle");

return svg;
}
Insert cell
md`d. Variables et fonctions associées au graphique`
Insert cell
name = data_temp_filter[0]["name"].split(",")[0]
Insert cell
minDate =d3.min(data_temp_filter, d => d.year)
Insert cell
maxDate =d3.max(data_temp_filter, d => d.year)
Insert cell
minScale=-20
Insert cell
maxScale=30
Insert cell
days = d3.range(1,366,1)
Insert cell
years = d3.range(
d3.extent(data_temp_filter, d => d.year)[0],
d3.extent(data_temp_filter, d => d.year)[1],
1
)
Insert cell
d3.extent(data_temp_filter, d => d.year)
Insert cell
monthName = d3.scaleOrdinal()
.domain([30,60,90,120,150,180,210,240,270,300,330,360])
.range (["Jan","Fev","Mars","Avr","Mai","Juin","Juil","Aout","Sept","Oct","Nov","Dec"])
Insert cell
colorScale = d3.scaleLinear()
.domain([minScale,0,(maxScale-0)/2, maxScale])
.range(["#3182bd","#249716",'#fcc147', "#c30f45"]);
Insert cell
md`## Etape 7: Intégrer un notebook Observable dans une page HTML ou directement dans un site internet.
pour partager avec le plus grand nombre..`
Insert cell
md`#### a. Notebook observable dans une page html`
Insert cell
md`* #### Construire la page HTML`
Insert cell
html`
&nbsp&nbsp&nbsp&nbsp&nbspOuvrez la cellule pour voir le template!


<!DOCTYPE html>
<meta charset="utf-8">
<title>meetup_7_nov_2019</title>
<meta charset="utf-8">

<style>
========= Définir les éléments css du notebook ==========
</style>

<body>
<!-- ==== Définir les elts DOM pour accueillir les cellules observable ===== -->

<script type="module">
=========== le code javascript =========================
======== Appel des cellules du notebook observable ======
</script>

</body>
`
Insert cell
md`* #### voir la page html construite <a target="_blank" href="https://github.com/ToulouseDataViz/meetup_observable-master/blob/master/index.html">ici</a>`
Insert cell
md`* #### Publier la page html
intégrer la page dans la structure du site ou créer un iframe dans le site pour appeler cette page. Cette solution est pratique pour les sites WordPress par exemple. `
Insert cell
md`#### b. Intégrer une ou plusieurs celulle(s) observable directement dans une page d'un site web
* Dans le menu de la celulle observable, choisir 'Embed code', copier le code proposé dans la page web
Attention: si la cellule fait appel à du CSS celui-ci n'est pas intégré dans la page web.`
Insert cell
md`### Références`
Insert cell
md`Pour le <a target="_blank" href="https://www.datavis.fr/index.php?page=day-hour-heatmap">heatmap</a>

Pour les données <a target="_blank" href="https://www.co2.earth/historical-co2-datasets">CO2</a>.

Pour les données <a target="_blank" href="https://www.ncdc.noaa.gov/data-access/paleoclimatology-data/datasets/ice-core">historiques de température</a>.

<a target="_blank" href="http://www.meteo-blois.fr/2015/04/exploiter-les-donnees-meteo-gratuites-dinternet/">Article </a> explicatif sur le processus de récupération des données historiques et climatiques de la NOAA
`
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