P5js
Børre Stenseth
Problemer >Kurver

Kurver

Hva

Tema er å lese og forstå kode og gjøre noen utvidelser med alternative dataformater.

Vi vil forsøke å lage en generalisert løsning som tegner et histogam basert på JSON-data. Jeg bruker arbeidet som er beskrevet på siden Dataformater som utgangspukt.

Det vi lager blir slik:

Denne framstillingen er basert på følgende JSON-fil:

{"tittel":"Stortingsvalg 2017","resultater":[
	{"navn":"AP",    "verdi":27.4, "farge":"240,60,54"},
	{"navn":"H",     "verdi":25.0, "farge":"106,155,198"},
	{"navn":"Frp",   "verdi":15.2, "farge":"95,65,121"},
	{"navn":"SP",    "verdi":10.3, "farge":"0,119,68"},
	{"navn":"SV",    "verdi":6.0, "farge":"222,104,47"},
	{"navn":"V",     "verdi":4.4, "farge":"131,189,66"},
	{"navn":"Krf",   "verdi":4.2, "farge":"255,190,46"},
	{"navn":"MDG",   "verdi":3.2, "farge":"0,167,120"},
	{"navn":"R",     "verdi":2.4, "farge":"164,31,45"},
	{"navn":"Andre", "verdi":1.9, "farge":"102,102,102"}
]
}

Jeg har valgt å legge ut fargene som RGB-verdier. Jeg kunne ha valgt andre formater, Et av mange nyttig verkøy for å se på fargeformatering er: CSS Color Converter.

Du kan kopiere denne løsningen fra denne zipfile kurver.zip
Den inneholder en kjørbar versjon med data i flere formater.

Selve websiden er enkel:

_test.html

Analyse

Som du ser viser grafen prosentverdier. Jeg har valgt å regne om alt til prosent. Det betyr også at hvis dataene er oppgitt som prosentverdier vil de forhåpentligvis fortsette å være det på grafen, etter konverteringen (!). Det er viktig å merke seg at denne løsningen er meningsløs dersom vi ikke har kontroll over alle utfallsmulighetene. Hvis vi f.eks. kuttet ut Ap i dataene ovenfor ville alle de andre verdiene fremstille "prosentfordelingen av de som har valgt et annet parti enn AP".

Jeg har valgt å betrakte koden som tre komponenter: sketch.js, datasett.js, histogram.js

  • sketch.js. Denne vil måtte endre seg etter hvilke data vi skal framstille, hvor mange kurver vi vil se samtidig osv.
  • datasett.js. Denne inneholder en objektbeskrivelse. datasett skal implementere prosentberegningen, og den skal kunne levere data som histogram-koden spør etter. De dataene som leveres skal også kunne støtte andre framstillingsformer, som f.eks kurver eller kakediagram. De funksjonene som skal med er:
    getTittel()
    getAntallVerdier()
    getMaxVerdi()
    getVerdi(i)
    getNavn(i)
    getFarge(i)
  • histogram.js. Dette er selve kurveframstillingen. Den skal arbeide på den måten at den får et datasett å jobbe med og spør dette etter de data som trengs.

Selve hovedskissen, sketch.js, er svært enkel og må som sagt tilpasses etter behov.

_kurver.js
/* 
kurver fra json-data
*/
var jsonObjekt;
var datakilde="data/valg.json";
//var datakilde="data/karakterer.json";
var DS;
function preload() {
  jsonObjekt = loadJSON(datakilde);
}
function setup(){
    var canvas = createCanvas(400,400);
    // plasserer det
    canvas.parent('canvasHere');
    DS=new jsonData(jsonObjekt);
}
function draw(){
    background(255);
    tegnHistogram(DS,0,0,width,height);
    noLoop();
}

De innleste JSON-dataene ordnes i en egen klasse, jsonData.

_jsonData.js
/*
Preparerer JSON-data for uttegning
*/
function jsonData(jsonObjekt){
    //-----------------------
    // setter opp data fra et JSONObject
    this.tittel=jsonObjekt.tittel;
    this.elementer=jsonObjekt.resultater;
    this.maxVerdi=0;
    this.sum=0;
    // regn om til prosent. hvis det er prosent
    // fra før så forblir det prosent
    for(var i=0;i < this.elementer.length;i++){
        this.maxVerdi=
            Math.max(this.elementer[i].verdi,this.maxVerdi);
        this.sum+=this.elementer[i].verdi;
        // hvit hvis fargen mangler
        if(!this.elementer[i].farge)
            this.elementer[i].farge="255,255,255";
    }
    // lag prosent
    for(var i=0;i < this.elementer.length;i++){
        this.elementer[i].verdi=
            this.elementer[i].verdi*100/this.sum;
    }
    this.maxVerdi=this.maxVerdi*100/this.sum;
    
    //---------------------------------
    // attributtene under må være med
    // de brukes av tegnefunksjonen
    this.getAntallVerdier=function(){
        return this.elementer.length;
    }
    this.getVerdi=function(i){
        return this.elementer[i].verdi;
    }
    this.getMaxVerdi=function(i){
        return this.maxVerdi;
    }    
    this.getNavn=function(i){
        return this.elementer[i].navn;
    }
    this.getTittel=function(){
        return this.tittel;
    }
    this.getFarge=function(i){
        var rgb =this.elementer[i].farge.split(",");
        return color(parseInt(rgb[0]),
                        parseInt(rgb[1]),
                        parseInt(rgb[2]));
    }
}

Funksjonen som tegner histogrammet er plassert i en egen fil, histogram.js. Årsaken er igjen et ønske om å skille funksjonalitet og holde orden. Det er mange ting som kan endres her. Det er f.eks. et svakt punkt at for lange navn på x-aksen vil skape vansker. Kanskje vi kunne lagt teksten på skrå?

_histogram.js
/*
uttegning av et histogram fra datasett DS, 
plassert med Top,Left,Width,Height
*/
function tegnHistogram(DS,L,T,W,H){
    push();
    translate(L,T);
    textSize(10);
    // konstanter som gir oss rammer
    // for uttegningen
    var marginL=30;
    var marginT=50;
    var marginR=10;
    var marginB=30;
    // ram det inn
    noFill();
    rect(0,0,W-1,H-1);
    // noen variable for å lette resonnementet
    // indre rektangel innenfor margene
    var innerL=marginL;
    var innerT=marginT;
    var innerH=H-marginT-marginB;
    var innerW=W-marginL-marginR;
    var innerB=H-marginB;
    
    // legger til 10 for å få en 
    // markering over største verdi
    var maxVerdi=DS.getMaxVerdi()+10;
    
    // x-aksen med søyler
    line(innerL,innerB,innerL+innerW,innerB);
    var antall=DS.getAntallVerdier();
    var dx=innerW/antall;
    for(var i=0; i< antall; i++){
        var yval=map(DS.getVerdi(i),0,maxVerdi,0,innerH);
        fill(DS.getFarge(i));
        rect(innerL+i*dx+1,innerB-1,dx,-yval);
        // under søylen og aksen
        textAlign(CENTER,TOP);
        fill(0);
        text(DS.getNavn(i),innerL+i*dx+dx/2,innerB+10);
        // over søylen
        textAlign(CENTER,BOTTOM);
        fill(0);
        // formaterer med en desimal
        text(parseFloat(DS.getVerdi(i)).toFixed(1),
             innerL+i*dx+dx/2,innerB-yval);
    }
    // y-aksen
    line(innerL,innerT,innerL,innerT+innerH);
    // marker hver 10.
    for(var i=0; i <= maxVerdi; i+=10){
        var yval=map(i,0,maxVerdi,0,innerH);
        stroke(200);
        line(innerL,innerB-yval,innerL+innerW,innerB-yval);
        textAlign(RIGHT,CENTER);
        fill(0);
        text(""+i,innerL-2,innerB-yval);
    }
    
    // tittel
    textAlign(CENTER,TOP);
    textSize(15);
    fill(0);
    text(DS.getTittel(),W/2,5);
    pop();
}

Oppgaverforslag

Merk at du vil ha nytte av både kode og data beskrevet på siden: Dataformater

Flere dataformater

Legg til en datasettbeskrivelse for CSV-data og en for XML-data. Begge skal kunne brukes av histogramfunksjonen.

Alternative kurver

Lag et alternativ til histogram, en annen kurveform som skal kunne betjene seg av de datasettene du har.

Problemer >Kurver