Számítógépes Grafika 5. gyakorlat Programtervező informatikus (esti) 2009/2010 őszi félév
Információk Gyakorlati diák:
Adatvizualizáció Adatok vizuális reprezentációja valamilyen általuk eltárolt információ közlésének céljával Tudományos és információs vizualizáció Például: – Emberi géntérkép ábrázolása – Egy weblap látogatói milyen utat tesznek meg a honlapon
Általában Nagy mennyiségű adatunk van (Saul Surman New York Times példája) Akár időben változó adatok Interakció igénye Több tudományterületről is kell eszközöket használni (adatbányászat, grafika stb.) Ezért alaposan meg kell tervezni a megjelenítést
Adatgyűjtés Adatgyűjtés fejlett eszközökkel bír A legfontosabb: – Mire kell választ adnunk a vizualizációs feladat során? Hogyan használhatjuk fel az adatokat a kérdés megválaszolására? Például: metró térképek (Harry Beck 1930)
Adatgyűjtés Rengeteg ingyenes forrás, pl.: – –
A Kérdés A vizualizáció a kommunikáció egy formája Ahhoz, hogy hatékony legyen tudnunk kell mit akarunk kifejezni Minél egyértelműbb a kérdés, annál egyértelműbb lehet a válasz Csak akkor vághatunk bele a tényleges vizualizációba (a kérdés megválaszolásába), ha már tudjuk a kérdést
Vizualizáció 1. Adatszerzés (internetről, lemezről stb.) 2. Feldolgozás (parse, adat struktúrálása) 3. Szűrés (érdektelen adatok eltávolítása) 4. Bányászat (statisztikai vagy adatbányászati) 5. Reprezentáció választása (lista, fa stb.) 6. Reprezentáció finomítása, testreszabása 7. Interakció: adatok és/vagy megjelenítés módosítására
1. példa Idősor ábrázolása Esetünkben: magyarország népességváltozása és foglalkoztatottságok
Mi a kérdés? Hogyan alakult a mai Magyarország területén a népességszám a múlt század folyamán és a foglalkoztatottság a három nagy gazdasági területen?
1. Adatgyűjtés Eredeti forrás: tml/tabl1_1_1.html tml/tabl1_1_1.html
2. Feldolgozás és 3. Szűrés A táblázatból csak öt oszlopot hagyunk meg (év, népesség illetve mezőgazdasági, erdőgazdálkodási és vízgazdálkodási foglalkoztatottság) A tizedesvesszőket pontokra cseréljük, az idézőjeleket eltávolítjuk Ezeket az xls-ből tabulátorral elválasztva csv-be exportáltunk: – sor1.csv sor1.csv
4. Bányászat Minden adatot megjelenítünk De egyszerre csak egy, évtől függő adat lesz a képernyőn
5. Reprezentáció választása Egy grafikonnal ábrázoljuk mind a négyféle tárolt adatot
Első változat Innen: 05/Idosor.zip 05/Idosor.zip
TLReader.pde class TimeLineReader { ArrayList data; int rowCount; TimeLineReader(String fileName) { data = new ArrayList(); String[] lines = loadStrings(fileName); rowCount = lines.length; for (int i=0; i<rowCount; ++i) { String[] row = lines[i].replace(",", "."). replace("\"", ""). split("\t"); data.add(row); } Tizedesvesszőket tizedespontokra cseréljük Idézőjeleket eltávolítjuk
TLReader.pde boolean isValid(int row, int col) { String field = ((String[])data.get(row))[col]; return !field.contains(".."); } String getString(int row, int col) { String field = ((String[])data.get(row))[col]; return field; } int getInt(int row, int col) { String field = ((String[])data.get(row))[col]; return parseInt(field) ; } float getFloat(int row, int col) { String field = ((String[])data.get(row))[col]; return parseFloat(field) ; }
TLReader.pde float getColMax(int col) { float fmax = MIN_FLOAT; for (int i=0; i<data.size(); ++i) { if ( isValid(i, col) ) fmax = max(fmax, getFloat(i, col)); } return fmax; } int getColumnCount() { return ((String[])data.get(0)).length; }
Idosor.pde TimeLineReader r; float plotX1, plotY1; float plotX2, plotY2; float yearMin; float yearMax; float popMin; float popMax; PFont fnt; int currentColumn;
Idosor.pde void setup() { size(600, 400); plotX1 = 50; plotY1 = 60; plotX2 = width - 50; plotY2 = height - 50; r = new TimeLineReader( " 5/idosor1.csv");
Idosor.pde yearMin = r.getColMin(0); yearMax = r.getColMax(0); dataMin = r.getColMin(1); dataMax = r.getColMax(1); currentColumn = 1; fnt = loadFont("fnt.vlw"); textFont(fnt); }
Karakterkészlet generálása
Idosor.pde void draw() { background(128); fill(255); noStroke(); rectMode(CORNERS); rect(plotX1, plotY1, plotX2, plotY2); String title = r.getString(0, currentColumn); textSize(20); fill(0); text(title, plotX1, plotY1-10); drawDataPoints(currentColumn); }
Idosor.pde void drawDataPoints(int col) { stroke(0, 64, 255); strokeWeight(3); for (int i=1; i<r.getRowCount(); ++i) { if (r.isValid(i, col)) { float x = map(r.getFloat(i, 0), yearMin, yearMax, plotX1, plotX2); float y = map(r.getFloat(i, col), dataMin, dataMax, plotY1, plotY2); point(x,y); }
Utasítások map(value, low1, high1, low2, high2) – A value értéket amit a [low1, high1] intervallumnak eleme átképezi a [low2, high2] intervallumba rectmode(mode) – A téglalapot meghatározó négy számadat paraméter értelmezésének módja: CORNER: balfelső pont + szélesség, magasság CORNERS: balfelső pont, jobbalsó pont CENTER: középpont, szélesség, magasság RADIUS: középpont, szélesség fele, magasság fele
Eredmény
6. Reprezentáció finomítása Ne csak pontokként jelenjenek meg az adatok, hanem vonalak láncaként
Utasítások noFill(): – Nem lesz kitöltése a zárt alakzatoknak beginShape(), endShape(MODE): – Csúcspontokból (vertex) álló összetett alakzatok (shape) megjelenítése – A MODE határozza meg, miként értelmezendőek a beginShape() és endShape() hívások között definiált csúcspontok („összekötési szabály”)
Utasítások
Idosor.pde void drawDataPoints(int col) { stroke(64, 128, 185); strokeWeight(3); noFill(); beginShape(); for (int i=1; i<r.getRowCount(); ++i) { if (r.isValid(i, col)) { float x = map(r.getFloat(i, 0), yearMin, yearMax, plotX1, plotX2); float y = map(r.getFloat(i, col), dataMin, dataMax, plotY2, plotY1); vertex(x,y); } endShape(); }
Eredmény
6. Reprezentáció finomítása Jelenjenek meg a feliratok a tengelyeken (milyen évben milyen értékek vannak)! Ennek megfelelően legyenek segédvonalak is az ábrán! Letölthető innen: 05/Idosor2.zip 05/Idosor2.zip
Utasítások textAlign(XMODE, YMODE): – XMODE a vízszintes igazítás módja, LEFT, CENTER vagy RIGHT – YMODE a függőlegesé: TOP, BOTTOM, CENTER vagy BASELINE
Idosor.pde void draw() { background(168); fill(255); noStroke(); rectMode(CORNERS); rect(plotX1, plotY1, plotX2, plotY2); String title = r.getString(0, currentColumn); textSize(16); fill(0); textAlign(LEFT, BOTTOM); text(title, plotX1, plotY1-10); drawYearLabels(currentColumn); drawColLabels(); drawDataPoints(currentColumn); }
Két új változó int yearInterval : leghamarabb hány év különbséggel rajzolunk ki népességadat segédvonalat függőlegesen int dataInterval : a vizsgált adat értékkészletének hány egységenként csinálunk segédvonalat vízszintesen
Utasítások nf(intvalue, digits): – Számból szöveggé alakítás formázással (digits jegynyi érték lesz kiírva) nf(floatValue, left, right) nfc(): – Stringgé alakítás ezreselválasztó vesszőkkel
Idosor.pde void drawYearLabels(int col) { fill(0); textSize(10); textAlign(CENTER, TOP); int lastYear = 0; for (int i=1; i<r.getRowCount(); ++i) { int ye = r.getInt(i, 0); if ( ye-lastYear >= yearInterval ) { float x = map( ye, yearMin, yearMax, plotX1, plotX2 ); stroke(220); strokeWeight(1); text(ye, x, plotY2 + 10); line(x, plotY2, x, plotY1); lastYear = ye;} }
Idosor.pde void drawColLabels() { stroke(0); fill(0); textSize(10); textAlign(RIGHT, CENTER);
Idosor.pde for (float f = dataMin; f < dataMax; f += dataInterval) { float y = map(f, dataMin, dataMax, plotY2, plotY1); line(plotX1 - 3, y, plotX1 + 3, y); text( nfc( (int)(f*1000) ), plotX1 - 5, y); } line(plotX1 - 3, plotY1, plotX1 + 3, plotY1); text( nfc( (int)(dataMax*1000) ), plotX1 - 5, plotY1); } line(plotX1 - 3, plotY1, plotX1 + 3, plotY1); text( nfc( (int)(dataMax*1000) ), plotX1 - 5, plotY1); }
Eredmény
7. Interakció Lehessen választani, hogy a népesség vagy adott foglalkoztatási adatokat akarom nézni Letölthető innen: 05/Idosor3.zip 05/Idosor3.zip
MButton class MButton { boolean toggled, mouseOver; int x, y, w, h; color cLine, cFill; String txt; MButton(int ix, int iy, int iw, int ih, String t) { toggled = false; x = ix; y = iy; w = iw; h = ih; txt = t; cLine = color(50); cFill = color(140); }
MButton boolean isMouseOver() { if ( mouseX >= x && mouseX = y && mouseY <= y+h) return true; else return false; } void Update() { mouseOver = isMouseOver(); }
MButton void Clicked() { Toggle(isMouseOver()); } void Toggle(boolean b) { toggled = b; if (toggled) cFill = color(255); else cFill = color(140); }
MButton void Draw() { strokeWeight(1); stroke(cLine); if (mouseOver && !toggled) fill(200); else fill(cFill); rectMode(CORNER); rect(x, y, w, h); fill(0); textAlign(CENTER, CENTER); textSize(12); text(txt, x+w/2, y+h/2); }
Idosor.pde ArrayList buttons; void setup() {... int szel = (int)( (plotX2-plotX1)/ (float)(r.getColumnCount()-1)); for (int i=1; i<r.getColumnCount(); ++i) { Mbutton b = new MButton( (int)plotX1 + (i-1)*szel, (int)plotY1 - 30, szel, 30, r.getString(0, i)); b.Toggle(i==0); buttons.add(b); }... }
Idosor.pde void draw() {... drawButtons(); drawYearLabels(currentColumn); drawColLabels(); drawDataPoints(currentColumn); } void drawButtons() { for (int i=0; i<buttons.size(); ++i) { MButton b = (MButton)buttons.get(i); b.Update(); b.Draw(); }
Idosor.pde void mousePressed() { if (mouseButton == LEFT && mouseX plotX1 && mouseY > plotY1-30 && mouseY < plotY1) { for (int i=0; i<buttons.size(); ++i) { MButton b = (MButton)buttons.get(i); b.Clicked(); if (b.isMouseOver()) { currentColumn = i+1; dataMin = r.getColMin(currentColumn); dataMax = r.getColMax(currentColumn); }
Eredmény
Feladat 1 Ha az egér egy létező érték közelében van, akkor jelenjen meg az ottani adat A szöveg mindig úgy jelenjen meg, hogy ne lógjon ki a rajzterületről!
Feladat 2 Legyen árnyéka a szövegnek
Feladat 3 Lehessen változtatni hány évenként nézzük az adatokat és azt is, hogy az adatok segédvonalkázása milyen sűrűn történjen