miércoles, 12 de diciembre de 2018

Crear un gráfico en SAPUI5 (I)

Una de las cosas más resultonas que se puede aplicar a una aplicación, es la posibilidad de mostrar gráficos (charts). Una tarea que puede parecer compleja cuando nos toca pegarnos con ella en SAPUI5... porque lo es, para que vamos a engañarnos.

Así que vamos a ver si aprendemos a crear nuestros propios gráficos poco a poco, para llegar a construir cosas más complejas.

La verdad es que hay mucha documentación que mirar, y yo apenas he aprendido más que lo básico, así que espero que pueda ser útil para que podamos ir aprendiendo poco a poco. Primero crearemos un ejemplo muy sencillo y luego lo íremos ampliando en artículos posteriores.

¿Y qué ejemplo puede resultarnos interesante? ¿Quizá uno que nos muestre los ingresos de la compañía año tras año, o la venta de materiales por país? No, uno más interesante aún, ver cómo se distribuye la probabilidad de resultados en una tirada de dos dados de seis caras (lo que los frikis llamamos 2d6). Por si nos da por jugar al Dungeon World.

Menudo frikazo, ¿verdad?

Dónde podemos encontrar documentación


La documentación de los VizFrames, que es el control que vamos a utilizar, la podemos encontrar, como siempre, en la web de la SDK de SAPUI5 (de donde nos podemos copiar vilmente de los ejemplos que nos proporcionan).

Pero esta documentación se nos puede quedar corta, y quizá nos interese también tenre una más específica, que viene en la Chart Property Reference. Aquí nos detallan la utilidad de los distintos atributos que se usan para crear los gráficos, pero demasiado técnicamente, así que nos va a tocar intuir muchas cosas y aprender a base de ensayo y error.

El objetivo


Vamos a crear una aplicación de tipo SAPUI5 (ya sea con el WebIDE de SAP Cloud Platform o con Eclipse). En esa aplicación, añadiremos un gráfico (Chart) usando un eje de coordenadas:

  • En el eje X (coordenadas) mostraremos los distintos resultados que podemos obtener en una tirada de 2d6 (un resultado de 2 a 12).
  • En el eje Y (ordenadas) mostraremos la probabilidad de que aparezca cada resultado (en porcentaje).
  • Las gráficas las mostraremos en formato línea.

Primero añadiremos una línea con la probabilidad de que se obtenga cada resultado: Si lanzamos dos dados de seis caras, la probabilidad de que salga 2 (1+1) es de 1/36; la probabilidad de que salga 3 (2+1 o 1+2) es de 2/36; la probabilidad de que salga 4 (1+3, 2+2 o 3+1) es de 3/36, y así progresivamente.

Una vez montada esa primera línea, añadiremos una segunda con probabilidad acumulada. ¿Qué probabilidad hay que de salga 10 o más? ¿Y de que salga 8 o más? ¿Y de que salga 4 o más?

Tendremos, por tanto, dos líneas de probabilidades distintas en el mismo gráfico, ambas con valores entre 2 y 12.

Al final del artículo viene un enlace de GitHub para descargar el código, y cómo solucionar un error con el SVGElement.getTransformToElement que se produce si tenemos una versión canija de SAPUI5 y usamos Chrome.

Creando la vista


Nos creamos una aplicación sencilla de tipo SAPUI5. Nada que no sepamos hacer.

Vamos a definir los namespaces para las distintas librearías que vamos a usar. Aparte de la típica entrada para sap.m, necesitamos las tres siguientes:

xmlns:viz="sap.viz.ui5.controls"
xmlns:viz.feeds="sap.viz.ui5.controls.common.feeds"
xmlns:viz.data="sap.viz.ui5.data"



Ahora creamos el gráfico, usando para ello un control de tipo vizFrame. El tipo de gráfico que queremos usar (en nuestro caso, de tipo línea) lo definimos con el atributo viztype="line".

También le vamos a añadir dos agregaciones, para volcar y organizar los datos en el gráfico:

  • dataset, donde indicaremos el origen de los datos (aquí asignamos el modelo de datos);
  • feeds, donde indicaremos cómo mostramos esos datos en el gráfico (la probabilidad en Y y los posibles resultados en X).

<viz:vizframe height="100%" id="idVizFrame" uiconfig="{applicationSet:'fiori'}" viztype="line" width="100%">
   <viz:dataset></viz:dataset>
   <viz:feeds></viz:feeds>
</viz:vizframe>

Enlazar los datos: FlattenedDataset


Para enlazar el gráfico con los datos, definimos un elemento dentro de dataset. En nuestro caso, usamos uno de tipo FlattenedDataset. Dentro de él definiremos dos tipos de datos: los que harán las veces de dimensiones (el resultado, de 2 a 12) y los que harán las veces de medidas (la probabilidad).

Nos fijamos en que a las dimensiones y medidas les asignamos un nombre (name), que después enlazaremos en el elemento feed.

Los datos los pasaremos en un modelo de datos de tipo JSON con el formato { Dice : [ {value : "2" ; prob : "2.7"}, {value : "3" ; prob : "5.5"} ... ] }. Así que con todos estos datos, podemos definirlo de esta manera:

<viz:dataset>
    <viz.data:FlattenedDataset data="{/Dice}">
       <viz.data:dimensions>
         <viz.data:DimensionDefinition name="Resultado" value="{value}" />
      </viz.data:dimensions>
      <viz.data:measures>
         <viz.data:MeasureDefinition name="Probabilidad" value="{prob}" />
      </viz.data:measures>
   </viz.data:FlattenedDataset>
</viz:dataset>

Organizando los datos: FeedItem


¿Y cómo ubicamos cada uno de estos elementos en el gráfico? Con el elemento FeedItem. La configuración de este elemento es muy dependiente del tipo de gráfico y podemos encontrar la documentación (en ocasiones poco intuitiva) en el Chart Property Reference - Elegimos el tipo de gráfica - Pestaña bindings.

En nuestro ejemplo, usaremos para la dimensión uid="categoryAxis" (al eje X que va) y para la medida, uid="valueAxis" (al eje Y).

Enlazamos con los datos gracias al name, asignando el mismo nombre que hemos puesto en el dataset.

<viz:feeds>
   <viz.feeds:FeedItem uid="valueAxis" type="Measure" values="Probabilidad" />
   <viz.feeds:FeedItem uid="categoryAxis" type="Dimension" values="Resultado" />
</viz:feeds>

Hala, ya tenemos la vista creada. Fácil, ¿verdad? Porque hemos cogido el caso más sencillo, por supuesto, no me pidais mucho más :P.


Creando en controlador


Ahora toca pegarse con el controlador. Lo suyo es que enlazásemos la aplicación con un servicio oData que viniese del back-end, pero como es sólo un ejemplo, me he definido un JSON para crear un modelo de datos de tipo JSONModel, y a tirar millas.

El JSON se compone de un array de objetos con tres valores:
  • value con el resultado obtenido (de 2 a 12);
  • prob con la probabilidad de que salga ese resultado exacto;
  • probAc con la probabilidad acumulada de obtener ese valor o uno inferior. Cuando juegas al rol, éste es realmente en resultado interesante ;) (¿qué tengo que sacar? 5 o más...)

Como ya he definido el binding en la vista, al hacer el setModel ya estaré asignando estos datos directamente a la gráfica, ¡con eso ya deberíamos poder ver los datos!


He hecho un añadido extra al final, recuperando el vizFrame (en oVizFrame) y asignándole propiedades adicionales con setVizProperties. En este ejemplo, lo único que hago es modificar el título de la gráfica para poner 2d6. Las propiedades que se pueden asignar vienen en la documentación.

Vamos a documentarnos un poco

Pues con esta modificación, si ejecutamos la aplicación, nos saldrá el resultado esperado, un gráfico de líneas... con una única línea, de momento.



Y si queremos añadir varias líneas...


Lo único que tenemos que hacer es definir nuevas medidas en el dataset y enlazarlos en el feed.

<viz.data:measures>
   <viz.data:MeasureDefinition name="Probabilidad" value="{prob}" />
   <viz.data:MeasureDefinition name="Probabilidad Acumulada" value="{probAc}" />
</viz.data:measures>
...
<viz:feeds>
   <viz.feeds:FeedItem uid="valueAxis" type="Measure" values="Probabilidad" />
   <viz.feeds:FeedItem uid="valueAxis" type="Measure" values="Probabilidad Acumulada" />

...

Ojo aquí, que también podemos definir las dos medidas en una única línea, separando los nombres con comas:

<viz.feeds:FeedItem uid="valueAxis" type="Measure" values="Probabilidad,Probabilidad Acumulada"  />



Y así tenemos la gráfica que queríamos desde el principio.

Gráfica todo molona.

Queremos más


Esta aplicación podemos hacerla más interesante (como si no lo fuese ya, puf, interesantísima), mostrando un pop-up informando de los valores de cada punto y permitiendo escoger entre varias tiradas diferente y cambiando la visualización de forma dinámica. Pero para eso nos seguiremos peleando en los siguientes artículos.

Si te interesa el código, puedes bajártelo en este enlace.

Error en Chrome con SVGElement


Como detalle a mencionar, en 2015, con una actualización de Chrome (a partir de la versión 48), en dicho navegador los gráficos comenzaron a fallar. ¿El motivo? Que dejaron de usar la función SVGElement.getTransformToElement, que debe usar internamente el VizFrame.

Según he podido leer, esto ya funciona bien a partir de SAPUI5 1.32.11, pero si estamos usando una versión inferior, lo podemos solucionar añadiendo la siguiente línea en el onInit:

SVGElement.prototype.getTransformToElement = SVGElement.prototype.getTransformToElement || function(elem) { return elem.getScreenCTM().inverse().multiply(this.getScreenCTM()); };

Podemos encontrar más información al respecto en el siguiente post:




2 comentarios:

  1. Puedes hacer un articulo con graficos más complejos?

    Parecido a este: https://sapui5.hana.ondemand.com/#/sample/sap.viz.sample.Bar/preview

    ResponderEliminar
    Respuestas
    1. En principio, mi idea era ir complicando la gráfica, aunque de momento he postpuesto el desarrollo de nuevos post porque tengo otros pendientes también.´

      En cualquier caso, la idea es dar una primera visión de cómo funcionan las gráficas con este post, y que cada uno pueda ir aprendiendo nuevos pasos a partir de éste ;) .

      Eliminar

Nota: solo los miembros de este blog pueden publicar comentarios.