Archiv der Kategorie: div. skripts

Weichzeichnen (Blur)


Objekte weichzeichnen zu können ist, ein Muss, will man ansprechende Graphiken erstellen. Die Processing eigene weichzeichen-Funktion blur() hat einen entscheidenden Nachteil. Sie ist sehr langsam. Will man Objekte in einer Animation (in Echtzeit) weichzeichnen ist sie somit obsolet.

Mario Klingemann hat einen sehr schnellen Weichzeichen-Algorithmus geschrieben, welcher wiederum von PhiLho erweitert wurde, so dass er nun auch mit dem Alpha Kanal umgehen kann. Damit hat man die Möglichkeit auch mit transparentem Hintergrund zu arbeiten.

// ==================================================
// fastblur, reworked by PhiLho from the Processing Forum
// by Mario Klingemann http://incubator.quasimondo.com/processing/superfast_blur.php
// ==================================================
void fastBlur(PImage img, int radius){
if (radius<1){
return;
}
int w=img.width;
int h=img.height;
int wm=w-1;
int hm=h-1;
int wh=w*h;
int div=radius+radius+1;
int a[]=new int[wh]; // i've added this
int r[]=new int[wh];
int g[]=new int[wh];
int b[]=new int[wh];
int asum,rsum,gsum,bsum,x,y,i,p,p1,p2,yp,yi,yw; // and the asum here
int vmin[] = new int[max(w,h)];
int vmax[] = new int[max(w,h)];
int[] pix=img.pixels;
int dv[]=new int[256*div];
for (i=0;i<256*div;i++){
dv[i]=(i/div);
}

yw=yi=0;

for (y=0;y<h;y++){
asum=rsum=gsum=bsum=0; // asum
for(i=-radius;i<=radius;i++){
p=pix[yi+min(wm,max(i,0))];
asum+=(p>>24) & 0xff;
rsum+=(p & 0xff0000)>>16;
gsum+=(p & 0x00ff00)>>8;
bsum+= p & 0x0000ff;
}
for (x=0;x<w;x++){
a[yi]=dv[asum];
r[yi]=dv[rsum];
g[yi]=dv[gsum];
b[yi]=dv[bsum];

if(y==0){
vmin[x]=min(x+radius+1,wm);
vmax[x]=max(x-radius,0);
}
p1=pix[yw+vmin[x]];
p2=pix[yw+vmax[x]];

asum+=((p1>>24) & 0xff)-((p2>>24) & 0xff); // asum
rsum+=((p1 & 0xff0000)-(p2 & 0xff0000))>>16;
gsum+=((p1 & 0x00ff00)-(p2 & 0x00ff00))>>8;
bsum+= (p1 & 0x0000ff)-(p2 & 0x0000ff);
yi++;
}
yw+=w;
}

for (x=0;x<w;x++){
asum=rsum=gsum=bsum=0;
yp=-radius*w;
for(i=-radius;i<=radius;i++){
yi=max(0,yp)+x;
asum+=a[yi]; // asum
rsum+=r[yi];
gsum+=g[yi];
bsum+=b[yi];
yp+=w;
}
yi=x;
for (y=0;y<h;y++){
pix[yi] = (dv[asum]<<24) | (dv[rsum]<<16) | (dv[gsum]<<8) | dv[bsum];
if(x==0){
vmin[y]=min(y+radius+1,hm)*w;
vmax[y]=max(y-radius,0)*w;
}
p1=x+vmin[y];
p2=x+vmax[y];

asum+=a[p1]-a[p2]; // asum
rsum+=r[p1]-r[p2];
gsum+=g[p1]-g[p2];
bsum+=b[p1]-b[p2];

yi+=w;
}
}
}

Wie setzt man den Algorithmus ein?

Wie wir der ersten Zeile entnehmen können, verlangt die Funktion beim Aufruf ein PImage und einen Integer als Parameter. Wir können nun also ein *.jpg laden und dieses, bevor wir es am Bildschirm ausgeben, mit dem Algorithmus weichzeichnen.

Beispiel FotoBlur:   starte Applet

PImage img;

void setup() {
img=loadImage("blume.JPG");
size(img.width, img.height);
}

void draw() {
img=loadImage("blume.JPG");
fastBlur(img,mouseX/5);
image(img, 0, 0);
}

Wollen wir aber von Processing gezeichnete Objekte weichzeichen, müssen wir diese in einen sog. FrameBuffer zeichnen. Siehe hochauflösende Bilder ausgeben.

Beispiel  Bluring selbst gezeichneter Objekte: Starte Applet

Hier wird ein Graphik-Objekt (ellipse()) in einen Offscreen Buffer gezeichnet und dieser dann mit fastBlur() weichgezeichnet.


/*---------------------------------------------------------
By Thomas Koberger
https://lernprocessing.wordpress.com/
---------------------------------------------------------*/

PImage img;
PGraphics pg;

void setup() {
img=loadImage("blume.JPG");
size(img.width, img.height);
}

void draw() {
image(img, 0, 0);
pg= createGraphics(width, height, P2D);
pg.beginDraw();
pg.noFill();
pg.strokeWeight(8);
pg.stroke(50, 100, 250);
pg.ellipse(width/2, height/2, 200, 200);
pg.loadPixels();
fastBlur(pg,mouseX/10);
pg.endDraw();
image(pg, 0, 0);
}

Beispiel Agenten weichzeichnen: starte Applet

Hier werden Agenten in kleine FrameBuffer gerendert, weichgezeichnet und dann angezeigt. Außerdem wird das ganze Bild, jeweils weichgezeichnet und im nächsten Frame als Hintergrund verwendet.

SourceCode siehe Applet!

Hochauflösende Bilder ausgeben


Will man einen Sketch hochauflösend (z.B. für den Druck) ausgeben, kann man alles, was in einem draw() Durchlauf gezeichnet wird, in einen sog. offScreen Buffer zeichnen und dann als Graphik speichern. Diese Technik wird auch „Double Buffering “ genannt.

Dafür erstellt man erst ein PGraphics Objekt. Dafür gibt man Breite, Höhe und den Renderer an.

PGraphics pg = createGraphics(width, height, JAVA2D);

// from http://amnonp5.wordpress.com/2012/01/28/25-life-saving-tips-for-processing/

void setup() {
size(500, 500);
}

void draw() {
background(255);
smooth();
strokeWeight(10);
fill(255, 0, 0);
ellipse(width/2, height/2, 200, 200);
}

void keyPressed() {
if (key == 's') {
save("normal.png");
saveHiRes(5);
exit();
}
}

void saveHiRes(int scaleFactor) {

// erstellt neues PGraphics Objekt
PGraphics hires = createGraphics(width*scaleFactor, height*scaleFactor, JAVA2D);

//alles ab hier wird in das PGraphics Objekt gezeichnet
beginRecord(hires);
//und skaliert
hires.scale(scaleFactor);
draw();
endRecord();
//das PGraphics Objekt wird als .png gespeichert
hires.save("hires.png");
}

Anstatt wie hier alle Zeichenvorgänge von draw() in das PGraphics Objekt zu rendern, kann man mit folgenden Anweisungen auch direkt darauf zeichnen.

hires.strokeWeight(10);PGraphics
hires.fill(255, 0, 0);
hires.ellipse(width/2, height/2, 200, 200);

MySQL Datenbankanbindung mit PHP


Wie praktisch ist es doch, wenn man die in einem Script anfallenden Daten in einer Datenbank speichern kann. Dazu verwenden wir in diesem Fall eine MySQL Datenbank auf http://www.bplaced.net/. Auf der Plattform sind nach der Anmeldung bis zu 8 Datenbanken erstellbar.

Mein Beispiel sollte die Ergebnisse eines Facebook Users bei einem Online-Game (programmiert in ProcessingJS) zwecks Highscore Listenerstellung in einer Datenbank abspeichern.

Wir werden uns hier anschauen, wie man Daten mit der Hilfe von kleinen PHP Scripts von Processing aus in eine MySQL Datenbank übertragen kann. Das ist besonders bei der Verwendung von ProcessingJS praktisch, da man vom Browser aus keine Berechtigung hat, Dateien zu erstellen und zu verändern. Somit bleibt eigentlich nur der Weg über die Datenbank.

Also beginnen wir mit der Erstellung der Datenbank. Das kann auf bplaced.net sehr kompfortabel über phpmyadmin erledigt werden.

PhpMyAdmin Tutorial: http://www.vms-tutorial.de/wiki/PhpMyAdmin#Zugang_zu_phpMyAdmin

Ich will mich hier nicht weiter mit Datenbankgrundlagen beschäftigen. Einschlägige Tutorials sind überall im Netz zu finden. Zum Beispiel hier: PHP und MySQL Einführung

Datenbank erstellen

Wir erstellen also hier nun eine Datenbank laut Anleitung: http://eass.bplaced.net/4-Datenbanken.

Beliebiger Name, bei mir: „facebook„. Und darin eine Tabelle mit dem Namen „Ergebnisse„.

In dieser Tabelle legen wir nun 4 Spalten an:

  1. ID: mit autoincrement, bewirkt eine eindeutige Kennung für jeden Eintrag
  2. fbid: speichert in userem Beispiel eine Zahl vom Typ bigint mit den jeweiligen Facebook-UserID’s
  3. name: Name des Facebook-Users als String
  4. score:  Das Ergebnis bei einem fiktiven Facebook-Spiel

Datenbank füllen:

Wenn die Struktur der Datenbank steht können wir versuchen Daten mit PHP per get oder post über http in die Datenbank einzutragen.

Hilfreich ist hier diese Seite: http://www.tutorialspoint.com/mysql/index.htm. Sie enthält für alle wichtigen Aufgaben Quellcode in SQL und PHP. Mehr braucht man nur selten!

Vorsicht: XXXXXX ist mit entsprechenden Werten zu ersetzten!

<?PHP

//Variablen für den Verbindugsaufbau
$dbhost = 'localhost';
$dbuser = 'XXXXXX';
$dbpass = 'XXXXXX';
$dbname = 'kobe_facebook';

//Verbindung wird aufgebaut
$dblink = mysql_connect("$dbhost", "$dbuser", "$dbpass")  or die ('I cannot connect to the database because: ' . mysql_error());
mysql_select_db("$dbname");

//Variablen werden aus über GET entgegengenommen.
$fbid = $_GET['fbid'];
$name = $_GET['name'];
$score = $_GET['score'];

//Variablen werden in die Datenbank geschrieben
$result = mysql_query("INSERT INTO ergebnisse (fbid, name, score) VALUES ('$fbid','$name','$score')");

//Abfrage auf Fehler
if(! $result )
{
die('Could not enter data: ' . mysql_error());
}

//Datenbankverbindung wird geschlossen
mysql_close($dblink);
?>

So. Will man dieses Script mit ProcessingJS nutzen kann, muss man es im Sketchordner unter /template_js speichern. Ist der Ordner nicht vorhanden, kann er mit im Processing Menü mit –> JavaScript –> Show Custom Template erstellt werden. Wenn man nun den Sketch „Exportiert“, wird von Processing automatisch ein Ordner mit dem Namen applet_js erstellt, der dann alles nötige (auch den Inhalt von /template_js) enthält. Der Inhalt dieses Ordners wird jetzt per FTP (z.B.: mit Filezilla) auf bplaced hochgeladen.

Nun ist es möglich die Tabellen-Spalten fbid, name und score mit den von uns über http:// übertragenen Werten zu befüllen.

Ein entsprechender Request könnte so aussehen:

http://username.bplaced.net/folder/setfb.php?fbid=88888888&name=richard%20stallman&score=89

Wobei username der Benutzername bei pblaced und folder der Ordner auf pblaced ist, in dem sich das PHP-Script befindet.

Wenn eine Probeübertragung geklappt hat, kann man nun auch Scripte schreiben, die Daten auslesen, oder die Datenbank updaten.

Daten auslesen:

Damit man überhaupt, um bei unserem Beispiel HighscoreListe zu bleiben, entschieden werden kann, ob jemand schon registriert ist, oder nicht, nehmen wir eine kleine Änderung im Code vor (nach dem Verbindungsaufbau):

//Variablen werden aus über GET entgegengenommen.
$fbid = $_GET['fbid'];

//der Eintrag zur entsprechenden fbid wird aufgerufen
$result = mysql_query("SELECT * FROM ergebnisse WHERE fbid='$fbid'", $dblink);

//kein Eintrag zur fbid
if(mysql_num_rows($result)==0){
echo"noData;";
}

//Eintrag vorhanden
while($row = mysql_fetch_array($result, MYSQL_ASSOC))
{
echo "isthere;";
echo $row['score'];
}

Wenn jemand schon in der Datenbank registriert ist, sollte nämlich kein neuer Eintrag erstellt werden, sondern ein Update stattfinden. Andernfalls wird ein neuer Eintrag erstellt.

Getestet kann das Script so werden:

http://username.bplaced.net/folder/fbidlookup.php?fbid=88888888

Update eines Datensatzes:

Ist zu einem bestimmten User schon ein Eintrag vorhanden und er übertrifft aber seine persönliche Bestmarke, dann soll der Eintrag score ein Update erfahren. Die Umsetzung ist hier so, dass das PHP Script ein Update durchführt, aber nur aufgerufen wird, wenn dies gewünscht ist.


//Variablen werden aus über GET entgegengenommen.
$fbid = $_GET['fbid'];
$name = $_GET['name'];
$score = $_GET['score'];

//Update des Wertes für score
$result = mysql_query("UPDATE ergebnisse SET score=$score WHERE fbid=$fbid");

//Falls das nicht funktioniert hat
if(! $result )
{
die('Could not enter data: ' . mysql_error());
}

http://username.bplaced.net/folder/updatefb.php?fbid=88888888&name=richard%20stallman&score=89

Kommunikation Processing mit PHP:

Processing bringt die Funktion loadStrings() mit, die  Ergebnisse einer solchen Anfrage in ein StringArray schreibt. Dieses Array kann dann mit Hilfer der split()- Funktion weiter zerlegt werden. So werden auch komplexe Abfagen mit relativ wenig Code analysierbar.

</pre>
//Aufruf des PHP Scripts fbidlookup mit der fbid als Parameter

String [] InList=loadStrings("http://username.bplaced.net/folder/fbidlookup.php?fbid="+uid);

// Ergebnis ausplitten
String[] list = split(InList[0], ';');
<blockquote>

Ausgabe des Scripts:
InList[0] = isthere;89

list[0]=“isthere“;

list[1]=“89″;

Geschafft. Nur eine Kleinigkeit: list[1] kann natürlich nicht mit einer float oder int -Variable verglichen werden, da sie vom Typ String ist.

Mit der Anweisung

parseFloat(list[1]

kann man mit dem Inhalt aber zum Beispiel eine float Variable füllen.

Fertig!

Ergänzung:

Hat man größere Datenmengen zu übertragen, wird das den Programmablauf empfindlich stören. Die Lösung wäre dann die Abfragen in einem eigenen Thread vorzunehmen.

Threads


Wenn man Daten zur Programmlauffzeit aus einer Datei oder gar dem Internet laden will, hat man ein Problem: Das Programm bleibt stehen und läuft erst dann weiter, wenn die angefragten Daten übermittelt worden sind. Das kann mit unter sehr lange dauern und der User muß warten. Das Programm ruckelt und die User Experience ist schlecht.

Als Lösung für dieses Problem gibt es in Java die Möglichkeit zeitintensive Code- Abschnitte in eigenen Programmabschnitten laufen zu lassen, und nur jeden Frame abzufragen, ob die Daten bereits verfügbar sind, oder nicht.

Sind diese verfügbar, werden sie abgerufen und verarbeitet. Sind sie es nicht, läuft das Programm einfach ohne sie weiter.

Dies ist auch in Processing möglich. Es bringt die Klasse (thread) schon mit. Die einfachste Implementierung würde dann wie folgt aussehen.

void setup() { size(200,200);
// Die Funktion eine Funktion soll in einem separaten thread ausgeführt werden
thread("eineFunction");
}

void draw() {

}

void eineFunction() {
// Diese Funktion wird einem eigenen Thread ausgeführt.
}

Damit kann man nun noch nicht soviel anfangen. Daniel Shiffman hat hier dokumentiert, wie man die Thread-Klasse von Processing so erweitern und adaptieren kann, um sie sinnvoll nutzen zu können.

Ich habe die Klasse dann um eine Abfrage mit der YQL (siehe Geodaten und mehr mit der YQL) erweitert:

Wir schreiben also unsere eingene Klasse, nämlich die SimpleThread-Klasse. Um die Funktionen der Thread Klasse von Processing nutzen zu können, erweitern wir diese mit folgender Code-Zeile:

class SimpleThread extends Thread {

Dann daklarieren wir darin unsere Variablen:

boolean running;           // Is the thread running?  Yes or no?
boolean available;         // Daten verfügbar?  Yes or no?
int wait;                  // How many milliseconds should we wait in between executions?
String id;                 // Thread name
int count;                 // counter
float lat, lng;            // Längen und Breitengrad
PApplet parent;            //Wir brauchen eine Instanz von Processing, damit hier seine Funktionen zur Verfügung stehen
XMLElement xmlResponse;

Einige von diesen ebnötigen wir, damit die Thread Klasse funktioniert, andere um unsere Abfrage laufen lassen zu können. Z.B.: das PApplet parent ist eine Instanz der Processing PApplet Klasse. Diese benötigen wir, um Processing-Funktionen in unserer Klasse nutzen zu können.

Dann folgt der Konstruktor, welcher definiert, welche Variablen bei der Erzeugung einer Instanz an die Thread Klasse übergeben werden müssen. Da ist unter anderem wieder unser PApplet dabei.

// Constructor, create the thread
// It is not running by default
SimpleThread (PApplet theApplet, int w, String s) {
wait = w;
running = false;
id = s;
count = 0;
parent = theApplet;
}

Mit dabei können die originalen Processing Thread-Funktionen überschrieben werden, indem wir sie mit eigenen Funktionen gleichen namens ersetzen und sie um unseren eigenen Code ergänzen.

Ein Thread muss gestartet und beendet werden. Dies machen wir mit void start() und void quit(). Bei der Startfunktion ist die Anweisung in der letzten Zeile interessant: super.start(); Sie ruft die Processing Thread Funktion start() auf, damit nach unseren eigenen Anweisungen auch alles aufgerufen wird, was nötig ist um einen Thread erfolgreich zu starten(),

// Overriding "start()"
void start () {
// Set running equal to true
running = true;
// Print messages
println("Starting thread (will execute every " + wait + " milliseconds.)");
// Do whatever start does in Thread, don't forget this!
super.start();
}
// Our method that quits the thread
void quit() {
System.out.println("Quitting.");
running = false;  // Setting running to false ends the loop in run()
// IUn case the thread is waiting. . .
interrupt();
}

Es folgt nun noch der für uns interessanteste Teil des Ganzen, nämlich die Funktion void run(), welche ausgeführt wird, wenn der Thread läuft. Dort kommen nun unsere Abfragen hinein.

// We must implement run, this gets triggered by start()
void run () {
while (running) {
//println(id + ": " + count);
count++;
loc=loc.replace(' ', '_');
loc=loc.replace(",", "%2C");
println ("loc: "+loc);
try {
String restUrl="http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20geo.places%20where%20text%3D%22"+loc+"%22&diagnostics=true";

xmlResponse = new XMLElement(parent, restUrl);
XMLElement[] placeXMLElements = xmlResponse.getChildren("results/place");
// println("Found " + placeXMLElements.length + " places");
if (placeXMLElements.length>0) {
String name = placeXMLElements[0].getChild(2).getContent();
lat = new Float(placeXMLElements[0].getChild(10).getChild(0).getContent());
lng = new Float(placeXMLElements[0].getChild(10).getChild(1).getContent());
}
}
catch (NullPointerException e) {
println("Couldn't launch request: " + e);
};

available=true;

// Ok, let's wait for however long we should wait
try {
sleep((long)(wait));
}
catch (Exception e) {
}
}
System.out.println(id + " thread is done!");  // The thread is done when we get to the end of run()
}

Zu guter Letzt brauchen wir noch einige Funktionen, um mit unserem Thread kommunizieren und ihn steueren zu können. Alles zusammen sieht nun so aus:


SimpleThread thread1;

String loc;

void setup() {
size(200, 200);

//ein neuer Thread wird erzeugt
thread1 = new SimpleThread(this, 100, "a");
thread1.start();
loc = "vienna";
}

void draw() {
background(255);
fill(0);
println(thread1.available());
// wenn die Daten verfügbar sind, werden sie abgerufen und eine neue Abfrage übergeben
if (thread1.available()) {
println(thread1.getLat()+" " +thread1.getLng());
thread1.setLoc("paris");
}
}

class SimpleThread extends Thread {

boolean running;           // Is the thread running?  Yes or no?
boolean available;         // Daten verfügbar?  Yes or no?
int wait;                  // How many milliseconds should we wait in between executions?
String id;                 // Thread name
int count;                 // counter
float lat, lng;            // Längen und Breitengrad
PApplet parent;            //Wir brauchen eine Instanz von Processing, damit hier seine Funktionen zur Verfügung stehen
XMLElement xmlResponse;

// Constructor, create the thread
// It is not running by default
SimpleThread (PApplet theApplet, int w, String s) {
wait = w;
running = false;
id = s;
count = 0;
parent = theApplet;
}

float getLat() {
return lat;
}

float getLng() {
return lng;
}

int getCount() {
return count;
}

boolean available() {
return available;
}

void setLoc(String newLoc) {
available=false;
loc=newLoc;
}

// Overriding "start()"
void start () {
// Set running equal to true
running = true;
// Print messages
println("Starting thread (will execute every " + wait + " milliseconds.)");
// Do whatever start does in Thread, don't forget this!
super.start();
}

// We must implement run, this gets triggered by start()
void run () {
while (running) {
//println(id + ": " + count);
count++;
loc=loc.replace(' ', '_');
loc=loc.replace(",", "%2C");
println ("loc: "+loc);
try {
String restUrl="http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20geo.places%20where%20text%3D%22"+loc+"%22&diagnostics=true";

xmlResponse = new XMLElement(parent, restUrl);
XMLElement[] placeXMLElements = xmlResponse.getChildren("results/place");
// println("Found " + placeXMLElements.length + " places");
if (placeXMLElements.length>0) {
String name = placeXMLElements[0].getChild(2).getContent();
lat = new Float(placeXMLElements[0].getChild(10).getChild(0).getContent());
lng = new Float(placeXMLElements[0].getChild(10).getChild(1).getContent());
}
}
catch (NullPointerException e) {
println("Couldn't launch request: " + e);
};

available=true;

// Ok, let's wait for however long we should wait
try {
sleep((long)(wait));
}
catch (Exception e) {
}
}
System.out.println(id + " thread is done!");  // The thread is done when we get to the end of run()
}
// We must implement run, this gets triggered by start()
void run () {
while (running) {
//println(id + ": " + count);
count++;
loc=loc.replace(' ', '_');
loc=loc.replace(",", "%2C");
println ("loc: "+loc);
try {
String restUrl="http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20geo.places%20where%20text%3D%22"+loc+"%22&diagnostics=true";

xmlResponse = new XMLElement(parent, restUrl);
XMLElement[] placeXMLElements = xmlResponse.getChildren("results/place");
// println("Found " + placeXMLElements.length + " places");
if (placeXMLElements.length>0) {
String name = placeXMLElements[0].getChild(2).getContent();
lat = new Float(placeXMLElements[0].getChild(10).getChild(0).getContent());
lng = new Float(placeXMLElements[0].getChild(10).getChild(1).getContent());
}
}
catch (NullPointerException e) {
println("Couldn't launch request: " + e);
};

available=true;

// Ok, let's wait for however long we should wait
try {
sleep((long)(wait));
}
catch (Exception e) {
}
}
System.out.println(id + " thread is done!");  // The thread is done when we get to the end of run()
}
// Our method that quits the thread
void quit() {
System.out.println("Quitting.");
running = false;  // Setting running to false ends the loop in run()
// IUn case the thread is waiting. . .
interrupt();
}
}

Geodaten auf Karte einzeichnen


Wenn man Daten visualisieren will, geht es sehr oft darum Geodaten zu verarbeiten und zu modifizieren.

Die Geodaten holen wir uns über die YQL aus Placemarker. Die Koordinaten liegen dann in Form von 2 floats (Breiten– und Längengrad) vor.

Beim Zeichnen der Koordinaten auf einer (Welt-) Karte ergibt sich zunächst das Problem, dass man eine Kugeloberfläche nicht verzerrungsfrei auf einer ebenen Fläche abbilden kann. Es gibt sehr viele verschiedene Möglichkeiten, wie man mit dem Problem umgehen kann. Einen guten Überblick liefert diese Seite.

Ich habe mich für die (meiner Meinung nach) einfachsten Möglichkeit der Plate Carrée („plane square“ oder Plattkarte) Projektion entschieden.

Hier kann man die Längen- und Breitenkoordinaten direkt in x- und y Koordinaten umrechnen.

float x = map(longitude, -180, 180, 0, width);
float y = map(latitude, 90, -90, 0, height);
Mehr Info zur „Plattkarte“ gibt es auf Wikipedia.

Für diese Projekton hat Till Nagel eine eigene Placemarker-Klasse geschrieben, die ich für mein Projekt leicht verändert habe.

class PlaceMarker {

String name;
float lat;
float lng;

PlaceMarker(String name, float lat, float lng) {
this.name = name;
this.lat = lat;
this.lng = lng;
}

float getX() {
return map(lng, -180, 180, 0, width);
}

float getY() {
return map(lat, 90, -90, 0, height);
}

void display(int results) {

// Equirectangular projection
float x = map(lng, -180, 180, 0, width);
float y = map(lat, 90, -90, 0, height);
}

String toString() {
return name + " (" + lat + ", " + lng + ")";
}
}

try-catch Block


Ein sog. try-catch Block kann genutzt werden, um einen Codeabschnitt zu umschließen, der voraussichtlich einmal eine Fehlermeldung zurück geben wird. Dies ist z.B. bei Schreibvorgängen auf der Festplatte und noch viel eher beim Abrufen von Daten aus dem Internet der Fall.

Der try-catch Block kann einen Fehler (oder besser eine Ausnahme – Exeption) abfangen und so den Absturz des Programms verhindern.

try {
// hier läuft der fehleranfällige Code

} catch (Fehlerklasse Fehlervariable) {

// Fehlerbehandlung
};

Ablauf:

Passiert im try-Abschnitt wirklich ein Fehler (es wird eine Exception „geworfen“), wird die catch-Funktion aufgerufen und die Fehlermeldung in der Fehlervariable gespeichert. Nun hat man die Möglichkeit im Catch Abschnitt mit dem Fehler umzugehen (Abfrage nochmal starten, oder was auch immer….) und die Fehlervariable in der Konsole auszugeben, damit man dort auch sieht, dass ein Fehler passiert ist. Das Programm stürzt ja nicht mehr ab.

Praktisches Beispiel Internet Abfrage :

try {
String restUrl="http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20geo.places%20where%20text%3D%22"+loc+"%22&diagnostics=true";

xmlResponse = new XMLElement(parent, restUrl);
XMLElement[] placeXMLElements = xmlResponse.getChildren("results/place");
// println("Found " + placeXMLElements.length + " places");
if (placeXMLElements.length>0) {
String name = placeXMLElements[0].getChild(2).getContent();
float lat = new Float(placeXMLElements[0].getChild(10).getChild(0).getContent());
float lng = new Float(placeXMLElements[0].getChild(10).getChild(1).getContent());

PlaceMarker placeMarker = new PlaceMarker(name, col1, col2, lat, lng);
placeMarkers.add(placeMarker);
println("MarkersSize: " + placeMarkers.size());
//println(i + ": " + placeMarker);
//println(i + ". " + name + " (" + lat + ", " + lng + ")");
}
}
catch (NullPointerException e) {
println("Couldn't launch request: " + e);
};

Man könnte jetzt auf die Idee kommen bei jeder Fehlermeldung einen try – catch Block einzusezen, um den Programmabsturz zu verhindern. Dies sollte man allerdings dringend vermeiden. Die Folge wäre Code, der zwar nicht macht was er soll, aber auch nicht abstürzt und vielleicht nicht einmal eine Fehlermeldung ausspuckt.

Fazit: try-catch Blöcke sollten nicht eingesetzt werden, um schlechte Programmierung zu verschleiern.

Geodaten und mehr mit Yahoo Query Language


Das wollten wir doch immer schon. Geodaten mit beliebigen Suchbegriffen verbinden und noch vieles mehr. Ein Service von Yahoo, nämlich die YQL (Yahoo Query Language) macht es möglich. Es handelt sich dabei um eine an die Syntax von SQL angelehnte Abfragesprache, die mittlerweile sehr vielen Datenbanken abfragen durchführen kann. Eine gute englischsprachige Erklärung zur YQL gibt es von Till Nagel.

Wir nutzen die YQL, um Ortskoordinaten abzufragen. Ein Account bei Yahoo ist dafür nicht einmal nötig.

Info über Datenbanken und eine Abfragekonsole zum Testen der Anfragen gibt es unter: http://developer.yahoo.com/yql/console/

Dort findet man unter den Beispielabfragen:

select * from geo.places where text=“vienna“

Als Antwort auf die Anfrage bekommt man folgende XML Datei:
Sie enhält im Tag centroid die gewünschten Daten, nämlich den mit latitude getagden Breitengrad und den mit longitude getagden Längengrad.

Diese wollen wir nun mit Processing auslesen. Dafür verwenden wir das Processing Objekt XMLElement (ab Processing 2.0 nur noch XML).

Folgende Processing Funktion gibt uns Längen- und Breitengrad zu einem gesuchten Ort zurück.

XMLElement xmlResponse;

void getLoc(String loc) {

// Da die Daten aus Twitter stammen, enthalten sie teilweise Leerzeichen und Beistriche, ersetzt werden müssen,
//um keine Fehlermeldung zu provozieren.
loc=loc.replace(' ', '_');
loc=loc.replace(",", "%2C");
println ("loc: "+loc);

//Es ist nicht sicher, dass der Ortsname auch tatsächlich existiert. Dann würde das Programm abbrechen.
// Um das zu vermeiden wird hier ein try catch Block verwendet.
try {

//Abfrage
String restUrl="http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20geo.places%20where%20text%3D%22"+loc+"%22&diagnostics=true";

//Abfangen der Antwort
xmlResponse = new XMLElement(this, restUrl);

//Analyse der Antwort
XMLElement[] placeXMLElements = xmlResponse.getChildren("results/place");
// println("Found " + placeXMLElements.length + " places");
if (placeXMLElements.length>0) {
String name = placeXMLElements[0].getChild(2).getContent();

//Auslesen der Geodaten
float lat = new Float(placeXMLElements[0].getChild(10).getChild(0).getContent());
float lng = new Float(placeXMLElements[0].getChild(10).getChild(1).getContent());
}
}
catch (NullPointerException e) {
println("Couldn't launch request: " + e);
};
}

Link zum Placemarker Projekt: http://developer.yahoo.com/geo/placemaker/

Easing


Um eine Bewegung nicht abrupt ablaufen zu lassen gibt es eine einfache Technik – das „Easing“. Wird ein Objekt nicht sofort an seinen Endpunkt bewegt, sondern nur einen bestimmten Prozentwert in dessen Richtung. Das führt zu einer harmonischen runden Bewegung.

Konkret wird der Unterschied in der x-Positionen von Objekt und Mauszeiger errechnet (dx). Wenn dieser Wert größer ist als 1, dann wird ein neuer Wert für die Objektposition berechnet (x+= dx*easing;).

Beispiel: eindimensionales Easing starte Applet

float x = 0.0; //aktuelle x-Position des Objekts
float easing = 0.05; //Stärke der Dämpfung

void setup() {
smooth();
size(500, 100);
}

void draw () {
background(0);
float targetX = mouseX;
// Unterschied der x-Position von Objekt und Mauszeiger
float dx = targetX -x;
//abs gibt den ansoluten Wert einer Zahl aus
if (abs(dx) >1.0) {
x+= dx*easing;
}
ellipse(mouseX, 30, 40, 40);
ellipse(x, 30, 40, 40);
} 

Aufgabe: Erweitere das obige Programm so, dass auch eine Bewegung der Objekte in Y-Richtung möglich ist. starte Applet

XMLElement


Viele große Internetportale bieten mittlerweile Programmierschnittstellen, sog. API’s an. Über diese kann man z.B. mit Processing Daten abrufen. Die Daten werden per URL angefordert. Die Antwort kommt meist (nicht immer) per XML-File. XML ist eine Auszeichnungssprache (Extensible Markup Language). Dabei werden die Daten in sog. Tags, ähnlich wie in HTML verpackt.

Um diese Daten verarbeiten zu können, gibt es in Processing Das Objekt XMLElement. In diesem können ganze XML Files gespeichert werden. XMLElement bringt Methoden mit, um einzelne Datensätze auslesen zu können.

Eine Anfrage, wie im Projekt FacebookAnwendung 1 sieht dann z.B. so aus (mit println() ausgelesen):

http://api.facebook.com/restserver.php?api_key=YourAPIKey&call_id=YourAPPID&fields=first_name,last_name,hometown_location,pic&format=XML&method=facebook.Users.getInfo&uids=YourUserID&v=1.0&sig=4c0d5ef0edf23eb16d95ed17e4fab781

Als Antwort schickt Facebook dann die folgende Datei:

<Users_getInfo_response xsi:schemaLocation="http://api.facebook.com/1.0/ 
http://api.facebook.com/1.0/facebook.xsd" list="true">
 <user xmlns="http://api.facebook.com/1.0/">
   <first_name xmlns="http://api.facebook.com/1.0/">YourFirstName</first_name>
   <hometown_location xsi:nil="true"/>
   <last_name xmlns="http://api.facebook.com/1.0/">YourLastName</last_name>
   <pic xmlns="http://api.facebook.com/1.0/">
      
   </pic>
   <uid xmlns="http://api.facebook.com/1.0/">YourUserID</uid>
   </user>
</Users_getInfo_response>

Alle diese Infos zwischen Tags, also <first_name>YourFirstName</first_name> nennt man Elemente. Diese Elemente sind hierarchisch gegliedert, d.h. <user> wäre das Parent Element von <first_name>, <last_name>, <pic>, usw. Diese sind dann also Child-Elemente von <user>. xmlns=“http://api.facebook.com/1.0/&#8220; steckt in einem öffnenden Tag und ist eine sog. Attribut.

Angenommen wir haben die obige Antwort in ein XMLElement myXML gespeichert, so können wir mit folgenden Anweisungen die Daten auslesen:

  • myXML.getChild(„user“)
    gibt das Child-Element von <Users>, also <User> zurück

    • <user xmlns="http://api.facebook.com/1.0/">
         <first_name xmlns="http://api.facebook.com/1.0/">YourFirstName</first_name>
         <hometown_location xsi:nil="true"/>
      <last_name xmlns="http://api.facebook.com/1.0/">YourLastName</last_name>
      <pic xmlns="http://api.facebook.com/1.0/">
      
      </pic>
      <uid xmlns="http://api.facebook.com/1.0/">YourUserID</uid>
      </user>
  • myXML.getChild(„user/first_name“)
    • <first_name xmlns="http://api.facebook.com/1.0/">YourFirstName</first_name>
  • myXML.getChild(„user“).getContent()
    gibt den Inhalt des Elements <User> zurück, in dem Fall leer, also null

    • null
  • myXML.getChild(„user/first_name“).getContent()
    gibt den Inhalt des Elements <First_Name> zurück

    • YourFirsName
  • myXML.getChildren()
    gibt alle Child-Elemente eines Elements als String-Array zurück z.B.:
  • myXML.getChild(„user“).getChildren()
    • [0]   <first_name xmlns="http://api.facebook.com/1.0/">YourFirstName</first_name>
      [1]   <hometown_location xmlns="http://api.facebook.com/1.0/" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
      [2]   <last_name xmlns="http://api.facebook.com/1.0/">YourLastName</last_name>
      [3]  <pic xmlns="http://api.facebook.com/1.0/"> 
      http://profile.ak.fbcdn.net/hprofile-ak-snc4/hs272.snc3/23196_YourUserID_7319_s.jpg</pic>
    • [4]   <uid xmlns="http://api.facebook.com/1.0/">YourUID</uid>

Den Content der Elemente kann man entweder als String, oder Array abspeichern und dann im Programm weiterverarbeiten.

Darüber hinaus gibt es auch noch die Möglichkeit selbst XMLElemente zu erstellen. Eine komplette Elementreferenz findet man hier http://processing.googlecode.com/svn/trunk/processing/build/javadoc/core/index.html.