Ci sono svariati modi per creare grafici in tempo reale e presentarli in una pagina web: una tecnica classica per gli istogrammi è quella che prevede la colorazione delle celle di una tabella senza bordi a seconda dei valori da rappresentare.
Con ASP.NET abbiamo a disposizione delle librerie grafiche per creare grafici accattivanti, sia 2D sia 3D, disegnandoli in base alle esigenze del momento. In questo articolo facciamo un esperimento e creiamo un grafico a partire da una sorgente di dati.
Il file sorgente
Per prima cosa ci serve una fonte dati. In questo esempio usiamo un documento XML, ma possiamo ottenere gli stessi risultati anche con una fonte dati diversa (database, array).
Il grafico che vogliamo produrre, rappresenterà l'andamento delle vendite di due tipologie di uva, quella rossa e quella bianca. Il grafico mostrerà l'andamento delle vendite nei vari mesi per entrambi i tipi di uva. Nel nostro file XML avremo quindi 24 records, 12 per i mesi di vendita dell'uva bianca e 12 per quella nera.
Listato 1. File "Vendite.xml"
<?xml version="1.0"?>
...
<prodotto>
<nome>Uva Bianca</nome>
<mese>Dicembre</mese>
<qta>1200</qta>
</prodotto>
<prodotto>
<nome>Uva Nera</nome>
<mese>Gennaio</mese>
<qta>1000</qta>
</prodotto>
...
Per leggere un file XML potremmo usare la classe DataSet
o la classe XmlReader
. Per semplicità creiamo un metodo che sfrutti DataSet
e carichiamo i dati del documento XML all'interno di un oggetto DataTable
, grazie al metodo ReadXml()
.
Listato 2a. Lettura file Xml (VB.NET)
Private Function loadXML(ByVal path As String) As DataTable
'Lettura
Dim ds As New DataSet()
ds.ReadXml(path)
Return ds.Tables(0)
End Function
Listato 2b. Lettura file Xml (C#)
private DataTable loadXML(String path){
// Lettura
DataSet ds = new DataSet();
ds.ReadXml(path);
return ds.Tables[0];
}
Ricordiamo di importare il namespace System.Data
(che contiene la classe DataSet
) sull'intestazione della Web Form.
<%@ Import Namespace="System.Data" %>
Il namespace System.Drawing
Una volta acquisiti i dati possiamo creare il grafico. Per farlo ciò importiamo anche il namespace System.Drawing
, che fornisce le classi:
- Bitmap, che permette la creazione e l'implementazione dell'area di lavoro,
- Graphics che permette di creare gli elementi grafici da posizionare sull'area di lavoro.
Cominciamo a sviluppare il metodo createChart()
che useremo per la generazione del grafico scrivendo una prima porzione di codice per di creare l'area di lavoro sulla quale posizioneremo il nostro grafico.
Listato 3a. Creazione area di lavoro (VB.NET)
'Creazione Grafico
Private
Sub createChart(ByVal dt As DataTable)
'Area di lavoro 500x400
Dim w As
Int32 = 500
Dim h As Int32 = 400
Dim base
As New Bitmap(w, h)
Dim area As Graphics =
Graphics.FromImage(base)
...
End Sub
Listato 3b. Creazione area di lavoro (C#)
// Creazione grafico
private void createChart(DataTable dt) {
// Area di lavoro 500x400
int w = 300;
int h = 300;
Bitmap bas = new Bitmap(w, h);
Graphics area = Graphics.FromImage(bas);
...
}
Abbiamo creato così un'area di lavoro di 500 pixel in larghezza e 400 in altezza. L'area di lavoro è stata successivamente acquisita da un oggetto grafico in grado di disegnarci sopra.
A questo punto non ci resta che disegnare le colonne e gli assi x e y del nostro grafico. Sull'asse x rappresentiamo le quantità che andranno da un minimo di 0 quintali fino ad un massimo di 4000 quintali. Sull'asse y visualizzeremo le colonne di entrambe le vendite, raggruppate per mese.
Una prima cosa alla quale fare attenzione è la modalità con la quale la classe Graphics
posiziona gli oggetti nell'area di lavoro. Le coordinate (x = 0, y = 0
) rappresentano il punto nell'angolo in alto a sinistra. Quindi, dobbiamo usare una semplice accortezza algebrica per calcolare le giuste posizioni.
Listato 4. Formula per la posizione verticale delle colonne
yColonna = altezzaArea - valoreColonna
Una volta completato il codice per il metodo createChart()
(versione VB.NET | versione C#), Possiamo incastonare tutto nel metodo page_load().
Listato 5a. Page Load (VB.NET)
' Caricamento della pagina
Private Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
' Caricamento file XML
Dim dt As DataTable = loadXML(MapPath("Vendite.xml"))
' Creazione grafico
createChart(dt)
End Sub
Listato 5b. Page Load (C#)
// Caricamento della pagina
private void page_load(Object sender, EventArgs e){
//Caricamento file XML
DataTable dt = loadXML(MapPath("Vendite.xml"));
//Creazione grafico
createChart(dt);
}
Finalmente otteniamo il grafico che abbiamo costruito.
Ora possiamo abbellire il nostro grafico, magari posizionando una riga orizzontale al di sotto delle colonne, ed una verticale prima di tutte le colonne; per rapprensetare i valori delle coordinate x e y (assi cartesiani). Un ulteriore abbellimento consiste nel suddividere in gruppo le colonne dello stesso mese.
Listato 6. Abbellimento del grafico
'Lettura dei dati
Dim x As Int32 = 10
'aggiungo 10 per lo spazio per l'asse y
Dim y As Int32 = 0
For Each row As DataRow In dt.Rows
Dim qta As Int32 = row("qta") / 10
y = h - qta - 10 'aggiungo 10 per lo spazio per l'asse x
Dim colonna As New Rectangle(x, y, 5, qta)
'alternanza colori
If x Mod 2 = 0
Then area.FillRectangle(Brushes.Red, colonna)
x = x + 7
Else
area.FillRectangle(Brushes.Blue, colonna)
x = x + 15
End If
Next
'creazione assi
area.DrawLine(Pens.Black, 0, y - 70, 0, h) 'asse x
area.DrawLine(Pens.Black, 0, h - 1, (x + 10), h - 1) 'asse y
I risultati che si possono ottenere in questo modo, applicando un pò conoscenze di geometria e matematica sono davvero interessanti. Con i metodi messi a disposizione da queste due classi si possono creare anche grafici in 3 dimensioni, grafici a torta a punti e tanto altro...
Conclusioni
I vantaggi sono diversi: in primo luogo non dobbiamo escogitare accorgimenti per la grafica, stiamo producendo un'immagine! Inoltre non dobbiamo preoccuparci della riproducibilità del grafico, un'immagine può tranquillamente essere acquisita e stamapata da un qualunque browser. Infine non dobbiamo acquistare particolari prodotti di Charting poichè usiamo un qualcosa già presente nel NET Framework.
Bisongna sempre tener conto però delle prestazioni. Più il grafico sarà complesso ed accattivante, più lavoro ci sarà per il server e maggiore sarà l'attesa dell'utente.