10.3. Taules
Moltes de les dades a les quals podem tenir accés les podem trobar en forma de taules amb files i columnes. En P5 disposem d’un mètode per llegir les dades de taules accedint a les seves cel·les mitjançant coordenades.

Font: adaptació de L. McCarthy; A. P. C. Reas; B. Fry (2015). Getting Started with P5.Js: Making Interactive Graphics in JavaScript and Processing. Make Community, LLC.
Per carregar una taula, normalment ho farem en un format de text pla conegut com a .csv (comma-separated values), en el qual els valors de cada fila estan separats mitjançant comes. Des de qualsevol editor de fulls de càlcul podem exportar les dades d’una taula en format .csv.
Prenguem com a exemple les dades següents sobre els deu llacs més grans del món:

Font: elaboració pròpia.
Veiem que les dades que conté la taula són el nom del llac, la superfície, la latitud i la longitud. L’equivalent en format .csv, que podem obrir en qualsevol editor de text, és el següent:

Font: elaboració pròpia.
Llegint una taula
Aquest fitxer .csv és el que podem carregar al nostre sketch de p5.js per accedir a les seves dades i visualitzar-les. El primer que hem de fer, igual que amb qualsevol fitxer extern, és pujar-lo a la mediateca de CodeLab. Una vegada fet això, usarem el mètode loadTable() per carregar la taula.
let tableData; function preload() { tableData = loadTable("lakes.csv", "header"); }
Notem que carreguem la taula en preload(), per evitar que mirem d’accedir a les seves dades sense haver-la descarregat encara. Com a primer paràmetre de loadTable() li hem de passar el nom del fitxer que conté la taula i com a segon paràmetre li passarem “header” per indicar-li que, en el nostre cas, la primera fila no són dades en si sinó que són els noms de cada columna.
Ara ja podem accedir a les dades de la taula fent ús de les seves coordenades. Ho farem mitjançant el mètode get(), al qual passarem les coordenades de la cel·la que vulguem consultar. Si volem per exemple accedir a la dada de la cel·la situada a la primera fila (recordem que no tenim en compte la primera fila amb els noms) i a la primera columna, necessitarem la coordenada (0, 0).
let lakesData; function preload() { lakesData = loadTable("lakes.csv", "header"); } function setup() { createCanvas(800, 300); // Primera fila, primera columna console.log(lakesData.get(0, 0)); // Segona fila, tercera columna console.log(lakesData.getNum(1, 2)); }
Quan busquem extreure dades numèriques podem usar getNum() en lloc de get() per assegurar-nos que la dada s’emmagatzemi com a nombre i no com a text, la qual cosa ens pot portar a errors més endavant.
Per llegir tota la taula d’una vegada i emmagatzemar-ne les dades, podem utilitzar bucles i arrays amb la finalitat d’automatitzar el procés i tenir les dades ordenades d’una manera més convenient:
let lakesData; let areas = []; function preload() { lakesData = loadTable("lakes.csv", "header"); } function setup() { createCanvas(800, 300); for(let i = 0; i < lakesData.getRowCount(); i++) { areas[i] = lakesData.getNum(i, 2); } }
En aquest cas, hem emmagatzemat a l’array areas les dades de la superfície de cada llac, que són a la tercera columna. Podem fer el mateix per a la resta de les dades.
Visualitzant la taula
Ara que ja hem extret les dades, les podem usar per dibuixar al nostre sketch. Podríem per exemple crear una classe Lake() que s’encarregui de dibuixar-les.
class Lake { constructor(name, lat, lon, area) { this.name = name; this.posX = map(lon, -180, 180, 0, width); this.posY = map(lat, -90, 90, height, 0); this.area = area; this.diameter = 2 * sqrt(area / PI); this.alpha = 50; } display(){ fill(0, 0, 255, this.alpha); noStroke(); circle(this.posX, this.posY, this.diameter); } }
Al constructor li donem com a paràmetres les dades que podem extreure de la taula: nom, latitud, longitud i superfície, i els assignem a les propietats que més tard utilitzarem en el mètode display() per dibuixar-les. Per calcular posX i posY hem tingut en compte que la latitud pot anar de –180 a 180 graus i que la longitud pot anar de –90 a 90 graus.
Ara podem col·locar el codi de la classe en una altra pestanya i escriure el codi principal del nostre sketch:
let lakesData; let areas = []; let names = []; let lats = []; let lons = []; let lakes = []; function preload() { lakesData = loadTable("lakes.csv", "header"); } function setup() { createCanvas(400, 250); for(let i = 0; i < lakesData.getRowCount(); i ++){ areas[i] = lakesData.getNum(i, 2); names[i] = lakesData.get(i, 1); lats[i] = lakesData.get(i, 3); lons[i] = lakesData.get(i, 4); } for(let i = 0; i < lakesData.getRowCount(); i++) { let newLake = new Lake(names[i], lats[i], lons[i], areas[i] * 0.025); lakes[i] = newLake; } } function draw() { background(250); for(let i = 0; i < lakes.length; i ++) { lakes[i].display(); } }
Primer creem una sèrie d’arrays per emmagatzemar les dades que extraurem de la taula, que emplenem al setup() mitjançant bucles. A continuació, creem els objectes Lake passant-los com a paràmetres les dades anteriorment extretes i, finalment, al draw() iterem per l’array d’objectes Lake per dibuixar-los mitjançant el seu mètode display().
Com a resultat obtindrem una visualització de la superfície dels llacs associada a la seva posició geogràfica:

Font: elaboració pròpia.
Amb les dades que tenim, podríem fer més interessant la visualització i fer-la interactiva, amb la finalitat que, en passar per sobre dels cercles, ens mostrés el nom del llac. És tan senzill com afegir un mètode nou a la nostra classe Lake:
checkMouse() { let d = dist(mouseX, mouseY, this.posX, this.posY); if(d < this.diameter * 0.5){ this.alpha = 150; textAlign(CENTER); textSize(20); text(this.name, width * 0.5, height - 25); } else { this.alpha = 50; } }
I afegir la comprovació de la distància per a cada objecte Lake al draw() de l’sketch principal:
function draw() { background(250); for(let i = 0; i < lakes.length; i ++) { lakes[i].display(); lakes[i].checkMouse(); } }

Font: elaboració pròpia.