sábado, 25 de mayo de 2013

Una regla de color uniforme, redondeada, con cero y colores desplazados (y 5)

ENUNCIADO


En la entrada (1) de esta serie definimos los objetivos: una regla de color personalizada para un mapa generado mediante Reporting Services que fuera uniforme, redondeada, con cero y colores desplazados
En la entrada (2) vimos como podemos cargar un informe en memoria, para modificarlo y volcarlo en un control ReportViewer.
En la entrada (3) modificamos la definición XML del informe dejando pendientes las funciones que cálculan los rangos y colores.
En la entrada (4) calculamos los intervalo numéricos de la regla.


En está última entrada de la serie veremos como obtener una escala de colores con degradados centrados en el rango que contiene el valor cero.

En nuestro caso para simplificar y porque los colores son los mas intuitivos situaremos el centro en el color amarillo y degradaremos hacia el azulo por los valores negativos y hacia el rojo por los valores positivos.

En mi algoritmo el color amarillo se situará a la derecha o a la izquierda del cero según el número de rangos que haya en cada lado. El objetivo es aminorar la perdida de definición en los colores y visualizar siempre los tres colores, lo que podría no ocurrir si el cero estuviera en primera o última posición.

SOLUCIÓN

El algoritmo rellena la lista en tres pasos:
Primero por la parte derecha donde:
  • El azul está en máximos (#FF o 255) y decrece hasta casi #00.
  • El rojo y el verde crecen desde #00 hasta  "casi"  #FF
En este punto entra el amarillo (#FFFF00)
Luego la parte derecha:
  • El azul se mantiene en #00
  • El rojo que empieza en casi #FF deberá terminar en #FF (aunque puede sufrir una perdida de intensidad intermedia).
  • El verde empieza el casi #FF pero termina en #00
Obviamente el "truco" del algoritmo está en calcular:

  • El punto medio donde situamos el amarillo
  • Los pasos para cada color en los degradados derecho e izquierdo.
  • 'Operar' con colores para generar el string hexadecimal.


    ''' 
    ''' Calcula un rango de colores que empieza en azul, pasa por el verde en el valor cero y termina en el rojo
    ''' 
    ''' colección de extremos de rango que contiene el valor cero
    ''' devuelve la lista de colores
    ''' 
    Public Function coloresCero(rango As List(Of Double)) As List(Of String)
        Dim colores As New List(Of String)
        Dim posicionMedio = rango.IndexOf(0) + 1
        If posicionMedio > rango.Count / 2 Then posicionMedio -= 1
        Dim cuentaIniciales = posicionMedio - 1
        Dim cuentaFinales = rango.Count - posicionMedio

        Dim pasoInicial = 255 / cuentaIniciales
        Dim pasoMedio = 255 / (cuentaIniciales + 1)

        For i = 0 To cuentaIniciales - 1
            colores.Add(HexString(pasoMedio * i, pasoMedio * i, 255 - pasoInicial * i))
        Next
        colores.Add(HexString(255, 255, 0))
        Dim pasoFinal = 255 / cuentaFinales
        pasoMedio = 255 / cuentaFinales
        For i = 1 To cuentaFinales
            colores.Add(HexString((pasoFinal * i) + (pasoMedio * (cuentaFinales - i)),
                                  pasoMedio * (cuentaFinales - i), 0))
        Next
        Return colores
    End Function

    Function HexString(r As Integer, g As Integer, b As Integer) As String
        ' cálcula el string hexadecimal de color RGB
      Return "#" + hex(r) + hex(g) + hex(b)
    End Function

    Function hex(v As Integer) As String
        ' Obtiene el hexadecimal de un entero con cero por delante si hace falta
        Dim r = String.Format("{0:x}", v)
        If r.Length > 1 Then
            Return r
        Else
            Return "0" + r
        End If

    End Function

Reconozco que hay un poco de juego fácil en la elección de los colores, pero eran los que me pedían. ¡Más fácil habría sido elegir como central el verde¡.

No hay comentarios: