Welcome to Idea R | Branding - Web Agency - Digital Strategies
Switch to the mobile layout

      Idea R - Do your competitors are always ahead? - Raise your voice with web marketing!

Blog

Take a software geek from the 80s, add a new generation graphic designer and dilute with a longtime marketing strategist. Shake vigorously and you'll get the Idea R's blog.

Change language... italiano

Un esempio di infografica con ASP.NET

Published on 1/19/2012
Categories: Web Design
Un esempio di infografica con ASP.NET

This article is available in English too.

Proviamo a creare una semplice inforgrafica a partire da un file XML contenente i dati sulle emissioni di anidride carbonica in Europa.
Il file ha il seguente formato:

<dataset>
  <nodes>
    <node>
      <rank>58</rank>
      <country>Norway</country>
      <tonsperson>11.40</tonsperson>
      <tons>52.35</tons>
    </node>
    <node>
      <rank>54</rank>
      <country>Sweden</country>
      <tonsperson>6.53</tonsperson>
      <tons>58.77</tons>
    </node>

    ...

  </nodes>
</dataset>

Per poter posizionare le informazioni su una cartina geografica, dobbiamo aggiungere i campi latitudine e longitudine per ogni stato europeo (non c'è bisogno di una precisione elevata).

<dataset>
  <nodes>
    <node>
      <rank>58</rank>
      <country>Norway</country>
      <tonsperson>11.40</tonsperson>
      <tons>52.35</tons>
      <latitude>62</latitude>
      <longitude>15</longitude>

    </node>
    <node>
      <rank>54</rank>
      <country>Sweden</country>
      <tonsperson>6.53</tonsperson>
      <tons>58.77</tons>
      <latitude>60</latitude>
      <
longitude>17</longitude>
    </node>

    ...

  </nodes>
</dataset>

Per utilizzare i dati dobbiamo caricarli in una sorgente dati XML, utilizzando l'oggetto System.Xml.XmlDataSource, ma il problema è che l'oggetto vuole i campi dei record specificati come se fossero attributi del nodo, cioè il file dovrebbe essere della forma:

<nodes>
  <node rank="58" country="Norway" tonsperson="11.40" tons="52.35"
    latitude="62" longitude="15">
  </node>

  ...

</nodes>

Per trasformare il documento XML non è necessario farlo a mano, ma più semplicemente utilizzeremo un file XSL (eXtensible Stylesheet Language) che all'interno conterrà le specifiche XSLT (XSL Transformations) di trasformazione. Il file è il seguente:

<?xml version="1.0"?>
  <xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" >
  <xsl:strip-space elements="*"/>
  <xsl:output method="xml"
    omit-xml-declaration="yes"
    indent="yes"
    standalone="yes" />
  <xsl:template match="/">
    <xsl:for-each select="dataset">
      <xsl:for-each select="nodes">
        <xsl:element name="nodes">
          <xsl:for-each select="node">
            <xsl:element name="node">
              <xsl:attribute name="rank">
                <xsl:value-of select="rank"/>
              </xsl:attribute>
              <xsl:attribute name="country">
                <xsl:value-of select="country"/>
              </xsl:attribute>
              <xsl:attribute name="tonsperson">
                <xsl:value-of select="tonsperson"/>
              </xsl:attribute>
              <xsl:attribute name="tons">
                <xsl:value-of select="tons"/>
              </xsl:attribute>
              <xsl:attribute name="latitude">
                <xsl:value-of select="latitude"/>
              </xsl:attribute>
              <xsl:attribute name="longitude">
                <xsl:value-of select="longitude"/>
              </xsl:attribute>
            </xsl:element>
          </xsl:for-each>
        </xsl:element>
      </xsl:for-each>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

Creiamo ora una nuova pagina ed inseriamo all'interno un oggetto System.Web.UI.DataVisualization.Charting.Chart, specificando nel nostro caso un grafico di tipo bolla, con due valori in ordinata e che non abbia nessuna griglia o bordo se non una mappa dell'Europa come sfondo:

<asp:Chart ID="m_Chart" runat="server" Height="600px" Width="600px" >
  <Series>
    <asp:Series ChartArea="m_ChartArea" Name="m_Series"
      MarkerBorderColor="Brown" ChartType="Bubble"
      MarkerStyle="Circle" YValuesPerPoint="2">
    </asp:Series>
  </Series>
  <ChartAreas>
    <asp:ChartArea Name="m_ChartArea" BackImage="europe.png"
      BackImageAlignment="Center" BackImageWrapMode="Scaled">
      <AxisY LineColor="Transparent">
        <MajorGrid Enabled="False" />
        <MajorTickMark Enabled="False" />
        <LabelStyle Enabled="False" />
      </AxisY>
      <AxisX LineColor="Transparent">
        <MajorGrid Enabled="False" />
        <MajorTickMark Enabled="False" />
        <LabelStyle Enabled="False" />
      </AxisX>
    </asp:ChartArea>
  </ChartAreas>
</asp:Chart>

Procediamo ora a caricare il documento XML all'interno dell'oggetto XmlDataSource.

protected void Page_Load(object sender, EventArgs e)
{
    // Open the data source applying the transformation
    XmlDataSource dataSource = new XmlDataSource();
    dataSource.DataFile="dataset.xml";
    dataSource.TransformFile = "transform.xsl";
    XmlDocument doc = dataSource.GetXmlDocument();
    ...

Il seguente codice carica i punti all'interno del grafico, dove come ascissa si usa la longitudine, come primo valore di ordinata la latitudine e come secondo valore, ovvero il raggio della bolla, si usano i milioni di tonnellate di CO2 emesse da ogni paese. Come etichetta di ogni bolla si usa la nazione. Il valore tonnellate/persona viene invece utilizzato per definire il colore della bolla, più il valore è alto, più la bolla tende al rosso acceso.

Per fare in modo che l'algoritmo funzioni con qualsiasi insieme di dati e per fare in modo che i colori si distribuiscano sempre uniformemente, il calcolo della colorazione viene fatta in due fasi, nella prima scansione di determinano i valori minimi e massimo, nella seconda scansione si dilatano i colori all'interno dell'intervallo appena determinato.
Si noti che la virgola decimale presente nel file XML potrebbe non coincidere con quella della lingua installata nel sistema operativo in uso, dunque la conversione va fatta usando la cultura corrente.
Infine, una sintesi testuale delle informazioni di ogni bolla viene visualizzata come tooltip.

 // Load points into the chart
Series series = m_Chart.Series[0];
XmlNodeList nodes = doc.SelectNodes("//node");
Double? minTonsPerson = null;
Double? maxTonsPerson = null;
foreach (XmlNode node in nodes)
{
  String country = node.Attributes["country"].Value;
  // Convert double values using current culture
  Double latitude = Convert.ToDouble(node.Attributes["latitude"].Value,
    CultureInfo.InvariantCulture);
  Double longitude = Convert.ToDouble(node.Attributes["longitude"].Value,
    CultureInfo.InvariantCulture);
  Double tons = Convert.ToDouble(node.Attributes["tons"].Value,
    CultureInfo.InvariantCulture);
  String tonsPersonStr = node.Attributes["tonsperson"].Value;
  Double tonsPerson = Convert.ToDouble(tonsPersonStr,
    CultureInfo.InvariantCulture);

  // ...remember the minimum and maximum tons/person values
  if (minTonsPerson.HasValue)
  {
    if (tonsPerson < minTonsPerson.Value) minTonsPerson = tonsPerson;
  }
  else
    minTonsPerson = tonsPerson;
  if (maxTonsPerson.HasValue)
  {
    if (tonsPerson > maxTonsPerson.Value) maxTonsPerson = tonsPerson;
  }
  else
    maxTonsPerson = tonsPerson;

  DataPoint point = new DataPoint(longitude, new Double[] { latitude, tons });
  point.Label = country;
  // ...save the tons/person value, it will be used later to change the bubble color
  point.SetCustomProperty("tonsPerson", tonsPersonStr);
  // ...set details tooltip
  point.ToolTip = String.Format("{0}: {1} milliontons", country, tons);
  series.Points.Add(point);
}

// Change bubble color accordingly to the tons/person value
foreach (DataPoint point in series.Points)
{
  Double tonsPerson = Convert.ToDouble(point.GetCustomProperty("tonsPerson"),
  CultureInfo.InvariantCulture);
  point.Color = Color.FromArgb(255, Convert.ToInt32(255 - 155 *
    (tonsPerson - minTonsPerson) /
    (maxTonsPerson - minTonsPerson)), 0);
}

You are the reader number 21,590.

Comments

Previous article

Previous article

IBM, Apple and Microsoft: a semiotics analysis

Next article

Custom Facebook tabs

Next article

Scroll to top