Qui sotto descrivo le idee che mi hanno aiutato nella scrittura del programma e potrebbero essere utili a chi si avvicina per la prima volta alle librerie gd.
Lo script si trova in questo sito . E' funzionante anche se poi non ci ho più lavorato...per inciso chi lo volesse provare deve crearsi un file di testo con un editor tipo "notepad" all'interno del quale deve aggiungere 2 o più colonne di numeri. Lo script sceglie la prima colonna come asse delle"x" e le altre colonne come serie per le "y". Ricordarsi di attivare le pop-up per vedere il grafico. Ops chiedo scusa ma le immagini citate ne testo sono andate perdute comunque si capisce lo stesso. Lo sistemerò quando avrò un po' di tempo...
Come prima difficolta' trovo il fatto che la funzione file() di PHP legge i file in righe. E' ovvio che io devo farglieli leggere in colonna...file() crea un vettore dove ogni elemento e' composto da tutti i caratteri della riga...sgradevole.
Uso la funzione explode() e separo ogni carattere dela stringa generando due vettori che contengono i caratteri delle due colonne.
Il codicllo di seguito fa questo lavoro e stampa le due colonne di dati dal file dati.dat
< ?php
$lines = file('dati.dat');
foreach($lines as $line_num => $line) {
$temp = explode(" ", $line);
$col1[$line_num] = $temp[0];
$col2[$line_num] = $temp[1];
}
//stampa gli output
foreach($col2 as $num => $line){
echo $line."\n";
}
foreach($col1 as $num => $line){
echo $line."\n";
}
?>
Il prossimo codice e'il codice fondamentale che prende i dati dalle 2 colonne del file dati.dat e disegna in una immagine 400X400 i punti corrispondenti.
< ?php
$lines = file('dati.dat');
foreach($lines as $line_num => $line) {
$temp = explode(" ", $line);
$colx[$line_num] = $temp[0];
$coly[$line_num] = $temp[1];
}
$dx=400;
$dy=400;
$image = imagecreate($dx, $dy);
$bg = imagecolorallocate($image, 0, 0, 0);
////questo e' il ciclo principale, quello che disegna l'immgine////
foreach($colx as $num => $line){
//cambio le coordinate il vertice di coor(0,0) diventa quello in basso a sx
$X=$line;
$Y=$dy-$coly[$num];
$col_ellipse = imagecolorallocate($image, 255, 255, 255);
imagefilledellipse($image, $X, $Y, 1, 1, $col_ellipse);
}
//////////////////////////////////////////////////////////
header("Content-type: image/png");
imagepng($image);
?>

Ilprimo difetto che salta all'occhio e' il fatto che il grafico e' troppo piccolo rispetto le dimensioni dell'immagine...questo e' dovuto chiaramente al fatto che la scala usata e' quella dei pixel ossia
1 punto = 1 pixel.
Il prossimo passo sara' quindi quello di introdurre una scala per rendere guardabile il grafico.
Provo ad introdurre un fattore di scala 20 su entrambi gli assi: in pratica l'immagine prende
1 punto = 20 pixel
il risultato e' che la risoluzione del grafico aumenta di 20 volte ed ora riusciamo a distinguere i punti...

Lo script di prima diventa questo:
< ?php
$lines = file('dati.dat');
foreach($lines as $line_num => $line) {
$temp = explode(" ", $line);
$colx[$line_num] = $temp[0];
$coly[$line_num] = $temp[1];
}
$dx=400;
$dy=400;
$image = imagecreate($dx, $dy);
$bg = imagecolorallocate($image, 0, 0, 0);
////introduco i fattori di scala////
$sx=20;
$sy=20;
/////////////questo e' il ciclo principale, quello che disegna l'immgine///////////////
foreach($colx as $num => $line){
//cambio le coordinate il vertice di coor(0,0) diventa quello in basso a sx
$X=($sx*$line);
$Y= $dy-($coly[$num]*$sy);
$col_ellipse = imagecolorallocate($image, 255, 255, 255);
imagefilledellipse($image, $X, $Y, 1, 1, $col_ellipse);
}
//////////////////////////////////////////////////////////////////////////////////////
header("Content-type: image/png");
imagepng($image);
?>
Mi sorge un altro prolema: se io introduco numeri troppo grandi tenendo una scala grande puo' succedere che il grafico vada fuori scala con grande disappunto...forse potrei rendere questo scriptino un po' piu' intelligente ossia fissata una dimensione dell'immagine esempio 400X400 lo script deve scegliere la risoluzione (la scala) adatta affinche' il grafico resti sempre dentro l'immagine...
Oops nel fare questa modifica ho scoperto che il comando file() legge le righe del file dati senza troncare la fine di ogni riga inserendo quindi spazi che mandano in confusione la funzione max(). Conviene inserire nel ciclo di lettura il comando rtrim() che tronca una stringa di tutti gli spazi presenti dopo il testo....fatto!
< ?php
$lines = file('dati.dat');
foreach($lines as $line_num => $line) {
$trimline = rtrim($line);
$temp = explode(" ", $trimline);
$colx[$line_num] = $temp[0];
$coly[$line_num] = $temp[1];
}
$dx=400;
$dy=400;
$image = imagecreate($dx, $dy);
$bg = imagecolorallocate($image, 0, 0, 0);
////introduco i fattori di scala////
$sx = $dx/(max($colx)- min($colx));
$sy = $dy/(max($coly)- min($coly));
//$sx=10;
//$sy=10;
/////////////questo e' il ciclo principale, quello che disegna l'immgine///////////////
foreach($colx as $num => $line){
//cambio le coordinate il vertice di coor(0,0) diventa quello in basso a sx
$X=($sx*$line);
$Y= $dy-($coly[$num]*$sy);
$col_ellipse = imagecolorallocate($image, 255, 255, 255);
imagefilledellipse($image, $X, $Y, 1, 1, $col_ellipse);
}
//////////////////////////////////////////////////////////////////////////////////////
header("Content-type: image/png");
imagepng($image);
?>
Il risultato e' questo:

Il metodo che ho usato e' abbastanza banale ma a quanto pare efficace. In pratica controllo il valore massimo e il minimo all'interno di ogni serie di dati e cosi' vedo il range di valori. Alla fine basta chiedersi quante volte ci stanno tutti questi numeri nella dimensione immagine che ho prefissato?? Semplice per esempio se i valori min e max della serie sono 1 e 21 e la dimensine dell' immagine e' 400X400 basta fare questo conto:
400/(21-1)=20
in pratica ingrandisco di venti volte la serie e riesco a riempire cosi' facendo tutta l'immagine senza avere nessun valore fuori immagine...e' piu' facile farlo che spiegarlo. Un fattore da considerare e' quello del bordo per esempio nel caso precedente il numero piu' grande risulta visualizzato sul bordo per evitare questo introduco una costante che riduce la scala in modo prefissato. Un coefficente ragionevole sembra essere 0,2 tornero' su questo argomento in seguito.
Un'altra cosa che mi fa incazzare e' che se metto un "a capo" allafine dell'ultima riga di dati lo script mi va in confusione...penso che sia ancora una volta colpa del comando file()...in realta' basta mettere una condizione is_numeric() sugli elementi dei vettori, cosi' facendo risolvo anche eventuali problemi di sicurezza ed eventuali header prsenti nel file nonche' il nome dei campi.
Il prossimo passo per completare il motore di questo programmino e' di insegnarli a trattare i numeri negativi poiche' in realta', per il momento, sa disegnare solo serie di dati positivi...la cosa sgradevole e' che la libreria gd accettana solo numeri positivi n quanto il sistema di riferimento che usa parte dal vertice in alto a sx di coordinate (0,0). Gia' dai primi script si nota che ho spostato (come e' piu' usato nei sistemi di riferimento caresiani) il vertice nel punto in basso a sx dell'immagine nera. Uso un cambio di coordinate nel quale il vertice e' traslato nel punto di coordinate piu' piccole.
Ovviamente ci sono problemi di "bordo " in quanto il punto di vertice non vinene visualizzato. Risolvero' il problema del bordo piu' avanti...
Il codice diventa questo scrivo solo il ciclo principale
...
foreach($colx as $num => $line){
//cambio le coordinate il vertice di coor(0,0) diventa quello in basso a sx
$vx = min($colx);
$vy = min($coly);
$X=($sx*($line-$vx));
$Y= ($dy)-(($coly[$num]-$vy)*$sy);
$col_ellipse = imagecolorallocate($image, 255, 255, 255);
imagefilledellipse($image, $X, $Y, 1, 1, $col_ellipse);
}
...

L'immagine rappresenta 40 numeri casuali compresi tra -10 e 10 generati con la funzione rand().
Il prossimo passo e' quello di aggiungere i gli assi cartesiani possibilmente dotati di scala e numeri...per aggiungere gli asi bisogna prima trovare l'origine relativamente al sistem di riferimento che rappresenta i dati chiaramente il cambio di coordinate da' 0,0 per xc = abs($sx*$vx) e yc = abs(($dy) - abs($vy*$sy)) i valori assoluti sono necessari per evitare coordinate negative che la lib GD non accetta. $sx ed $sy rappresentano i fattori di scala $vx e $vy il cambio di coordinate $dy la dimensione y dell-immagine. Trvato il centro diventa poca cosa tracciare 2 linee con la funzione imageline() .
Il grafico qui sotto traccia la funzione coseno da -2pi a 2pi essendo una funzione perfettamente simetrica gli assi vengo tracciati al centro dell'immagine

al contrario con una serie di dati assimmetrica si vede come lo script scelga il sistema di coordinate piu' adatto alla corretta visualizzazione dei dati

Ora veniamo al problema del bordo...penso di risolverlo creando una regione interna all'immagine di circa il 90% delle dimensioni dell'immagine entro la quale verra creato il grafico. Cosi' facendo avanzo bordo per un box con la scala e i nomi degli assi...
Nessun commento:
Posta un commento