Saludos!
Amigos con respecto a la libreria ZedGraph, estoy tratado de crear una grafica con respecto a las horas transcurridas durante un dia, los datos los obtengo a traves de una base de datos.
la tabla en la cual estoy obteniendo los datos es de la siguiente forma:
(https://i.stack.imgur.com/Dr26E.jpg)
como notaran hay 8 campos de temperaturas, llamados vin0....vin7 y tambien el campo hora.
lo que pretendo hacer es que en el eje XAxis de la grafica se muestren las horas transcurridas, la cual estan guardas en la tabla de la base de datos, y en el eje YAxis se muestren los registros de las temperaturas, este es mi codigo :
Option Strict Off
Option Explicit On 'Permiten declaracion de varias variables'
Imports VB = Microsoft.VisualBasic
Imports ZedGraph
Imports MySql.Data.MySqlClient
Public Class Form1
Private Sub Form1_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize
Me.CenterToScreen()
Try
Conexion.ConnectionString = connStr
Conexion.Open()
Me.Text = "CONECTADO"
Catch ex As Exception
MsgBox(ex.Message)
Finally
Conexion.Close()
End Try
CreateGraph(ZedGraphControl1)
End Sub
Private Sub CreateGraph(ByVal zgc As ZedGraphControl)
Try
Dim query As String = "SELECT * from termocuplas"
Dim connection As New MySqlConnection(connStr)
Dim cmd As New MySqlCommand(query, connection)
connection.Open()
Dim reader As MySqlDataReader
reader = cmd.ExecuteReader()
Dim list1 = New PointPairList()
Dim x As String
Dim y As String
Dim myPane As GraphPane = zgc.GraphPane
myPane.Title.Text = "Termocupla"
myPane.XAxis.Title.Text = "Tiempo"
myPane.XAxis.Scale.MagAuto = False
myPane.YAxis.Title.Text = "Temperatura °C"
myPane.YAxis.Scale.MagAuto = False
While reader.Read()
y = reader.GetString(2)
x = reader.GetString(11)
list1.Add(x, y)
End While
Dim myCurve As LineItem = myPane.AddCurve("Termocupla Vapor", list1, Color.Red, SymbolType.None)
zgc.AxisChange()
reader.Close()
connection.Close()
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
End Class
tengo el problema que no esta funconando de forma correcta, aqui una muestra:
(https://i.stack.imgur.com/7mcPt.jpg)
como ven en el eje x se estan mostrando los valores de forma entera, y no en forma de la hora registrada, por ejemplo: 13:05:40 e igualmente para el eje Y, se esta mostrando los valores de forma entera, y no de la forma +38,85
que debo corregir para que funcione de la forma que quiero???
gracias de antemano!
Por qué declaras como 'string', 'x' e 'y'?
Decláralos como 'single', vamos conc decimales aun que luego vengana a redondearse según el tamaño del gráfico, expuestos a un tamaño mayor, la precisión será con más detalle...
...Y cuando leas los datos (aunque por alguna razón estén guardados en formato de string en la bd, en un fichero o donde sea), tu haz la conversión al tipo single. Así tu 'list1' debe almacenar valores single...
Luego... en la gráfica obivamente debes tener un factor de escala para el eje X y otro factor de escala para el valor Y...
Esto es, si una hora ocupa en el gráfico 20 píxeles de ancho (por ejemplo), quiere decir que tendrás 20 píxeles para mostrar 1 hora, luego 60 minutos, luego 3.600 segundos... por tanto un valor como: las 13:30:45, no aparecría dibujado distinto del valor 13:33:27 (por ejemplo), pués en cada píxel se deben acomodar 3 minutos.
No te preocupes, está bien, si toleras que se pueda hacer zoom, luego tendrás que para una hora, en vez de 20 píxeles, tuvieras por ejemplo 180, con lo cual ahora cada píxel podría acoger ya hasta 20 segundos (digamos que esa sería la precisión)....
Muchas gracias por tu atención y por tu recomendación, lo editare de la forma que me recomiendas.... y otra pregunta, haber si se puede hacer con esta librería, la intención del eje X es que sea la hora:minutos:segundos, entonces existirá alguna forma de añadir una barra de tipo scrol a la gráfica generada, de tal forma que a medida que cargue los datos, se puedan ver los días pasados???? Si me explico
No he operado nunca con esa librería... desconozco si lo permite.
No obstante si está capacitada para mostrar la imagen, lo puedes implementar tú, aunque con algo mñás de trabajo claro.
... supongamos que el gráfico está a un zoom de 4 horas (que muestra solo 4 horas), entonces tu generas imágenes en bagkground desde las 0 hasta las 3:59:59, luego otra desde las 4 hasta las 7:59:59, luego... y al final la última 20:59:59.
Luego eres tú quien en un backbuffer va pegando lo correspondiente entre dos imágenes y finalmente lo vuelcas a donde lo presentas... el control tal como señalas con un scroll del tipo que sea, incluso un "fijar y arrastrar" (si no quieres perder tiempo en poner una interfaz gráfica para ello)... vamos básicamente como la sucesión de desplazamiento horizontal de un videojuego, solo que más sencillo pués solo son dos imágenes con las que lidiar en cada momento.
Tienes 6 imagenes suponiendo un zoom de 4 horas, para cubrir el día completo.
img0|img1|img2|...|img5
En cualquier momento dado, puede haber a lo sumo parte de 2 imágenes:
|......|......|
Se trata pués de seleccionar, cortar y pegar la parte correspondiente de cada imagen en el 'viewport'.
Viewport, con la mitad de dos imagenes:
...|...
Viewport con 5/6 de una imagen y 1/6 de la siguiente.
.....|.
Viewport con una imagen completa (como ahora la tienes)
|......|
Espero que te quede clara la idea de como lograrlo, si la propia librería no tiene capacidad para ello.
Cita de: NEBIRE en 23 Agosto 2019, 23:32 PM
Por qué declaras como 'string', 'x' e 'y'?
Decláralos como 'single', vamos conc decimales aun que luego vengana a redondearse según el tamaño del gráfico, expuestos a un tamaño mayor, la precisión será con más detalle...
...Y cuando leas los datos (aunque por alguna razón estén guardados en formato de string en la bd, en un fichero o donde sea), tu haz la conversión al tipo single. Así tu 'list1' debe almacenar valores single...
Luego... en la gráfica obivamente debes tener un factor de escala para el eje X y otro factor de escala para el valor Y...
Esto es, si una hora ocupa en el gráfico 20 píxeles de ancho (por ejemplo), quiere decir que tendrás 20 píxeles para mostrar 1 hora, luego 60 minutos, luego 3.600 segundos... por tanto un valor como: las 13:30:45, no aparecría dibujado distinto del valor 13:33:27 (por ejemplo), pués en cada píxel se deben acomodar 3 minutos.
No te preocupes, está bien, si toleras que se pueda hacer zoom, luego tendrás que para una hora, en vez de 20 píxeles, tuvieras por ejemplo 180, con lo cual ahora cada píxel podría acoger ya hasta 20 segundos (digamos que esa sería la precisión)....
hey
lo he trabajado como single, mira esta parte:
Dim y As Single
Dim x As Single
Dim str As String
y los convierto en:
y = Convert.ToSingle(reader.GetString(2))
str = Replace(reader.GetString(11), ":", ".")
x = Convert.ToSingle(str)
como ves, a la hora estoy reemplazando el ":" por "." ya que me al tratar de hacer:
x=Convert.ToSingle(reader.GetString(11))
y reader.GetString(11) = "13:33:27", no me hace nada, y no grafica nada, por ello hice esa conversion. ahora al hacerla convirtiendolo me esta graficando de la siguiente forma:
(https://i.stack.imgur.com/TjKwQ.jpg)
ya le he mejorado un poco mas...... fijate en la imagen a continuación:
(https://i.stack.imgur.com/RZxQz.jpg)
ya con respecto al eje 'Y' no creo que haga falta mejorarla, pero en el eje 'X' si hace falta, fijate en el codigo:
Dim y As Single
Dim x As Single
If reader.Read Then
While reader.Read()
y = Convert.ToSingle(Replace(reader.GetString(3), ".", ","))
x = Convert.ToSingle(Replace(reader.GetString(11), ":", "."))
list1.Add(x, y)
End While
End If
que me recomiendas para mejorar el eje 'X', en vista de que el sistema trabaja en hora militar, de 0 a 23 hrs, estoy tratando de que solo se muestre la unidad de hora y al hacer zoom se muestre minutos y segundos, tal cual esta sucediendo en la temperatura.
Cita de: 01munrra en 26 Agosto 2019, 19:28 PM
...que me recomiendas para mejorar el eje 'X', en vista de que el sistema trabaja en hora militar, de 0 a 23 hrs, estoy tratando de que solo se muestre la unidad de hora y al hacer zoom se muestre minutos y segundos, tal cual esta sucediendo en la temperatura.
...
Te comentaba en el mensaje anterior, pero probablemente no te acomode buscar en la documentación de la librería si pueda devolver una imagen (un picture), para que tu solicitando todas las gráficas para un día, luego mediante operaciones gráficas puedas pegar a voluntad secciones en función del desplazamiento absoluto en el día.
De todos modos, veo en el gráfico, que muestra desde las 7 hasta las 14, es decir un tramo de 7 horas... la pregunta pertinente es: tiene alguna propiedad o método que permita hacer zoom (escalar), el eje X, por ejemplo para que tu puedas determinar que en el gráfico quepa solo una hora, 2, 4, 8, 24... es decir las que tu quieras?.
Me resultaría extraño, que la librería no permitiera ajustar la escala en ambos ejes.
En esa vista (de la última imagen), si acogiera solo una hora, sería magnifico, pués a ojo de buen cubero ocpa unos 750 píxels de ancho, así que cada píxel podría acoger a solo 5 segundos... lo que daría una muy buena precisión.
En última instancia, si la librería no permitiera ningún tipo de zoom (antes de intentar lo siguiente mira si de verdad la librería no lo permite, porque si lo permite lo que ahremos sería una sobrecarga innecesaria de trabajo), podemos manejarlo nosotros a nuestro antojo...
...hemos leído los datos de entrada, (sea cual sea el origen), y tenemos pués un array, una lista enlazada, etc... con los datos... por comodidad, voy a suponer que están en alojados en un array y que es una estructura de tipo puntof ...
Creando nuestro zoom artificial:
Estructura PuntoFlotante
x as single
y as single
fin estructura
dim puntos() as puntoflotante
funcion privada Escalar(por referencia P() as puntoflotante, EscalaX as single, EscalaY as single) as puntoflotante()
dim k as long
dim tmp() as puntoFlotante
dimensionar array temporal tmp(0 to p.Lenght)
bucle para k desde 0 a p.lenght
tmp(k).x = (p(k).x * escalaX)
tmp(k).y = (p(k).y * escalaY
repetir
devolver tmp
fin funcion
Ya hemos escalado los valores, ahora solo queda mandarlo para que sean dibujados...
' Tu tienes:
while reader.read
...
list1.add(x,y)
end while
' después conviene escalarlo:
dim list2 tipo...?
list2 = Escalar(list1, 3.25, 1)
' ahora lo enviás a dibujar:
... myPane.AddCurve(..., list2)
Nota que los valores de escala los he puesto un poco allá, con 3.25 el resultaod es que se estirará el eje x x 3.25 veces, en cambio como señalas la escala en el eje Y va bien, los dejamos en su propia esclaa 1.0...
Pudiera ocurrir dos cosas:
- Primero: Que la librería intérprete al revés la escala... si ves que se encoge en vez de ensancharse, cambia las multiplicaciones por divisiones.
- Segundo: Que la librería se empeñe (cabezonamente) en meter al completo el gráfico dentro del viewport de la gráfica. ...para ello entonces implica que estaría recorriendo la colección para tomar el valor mayor y menor y conforme a ellos generar un escalado interno para que se ajuste al tamaño del viewport. Desde fuera (para ti), el resultado es que el gráfico a pesar de nuestro escalado, se vería prácticamente igual, las diferencias serías las nimias de los redondeos. En tal caso, o la librería dispone de algún control de la misma que estás obviando por no leerte/buscar en la documentación, o simplemente es mala por dejar fijo un gráfico sin posibilidad de escalarlo y que recorte lo que no quepa...
p.d.:
Cuando veas que el escalado te funcione bien... , entonces se trata solo de ajustar que factores son los valores limitantes, esto es mira de limitar el factor máximo y minimo de escala.
Añades un scroll a la ventana y un label que diga Escala del eje X: 3.75 ...si el valor mínimo de escala fuera (pongamos) 0.01 y el valor máximo 100, como usas un scroll numérico sin decimales, tu señalas (para el scroll) el minimo a 1 y el máximo a 10000, el valor real de escala sabes que será: EscalaX = Scroll.Value / 100
Nota que cada vez que el usuario quisiera escalar, no ser´´ia preciso volver a leer los datos de origen... sería mejor disponer una función aparte, es decir tener dos funcionalidades, la lectura de datos, y la de escalado:
' lee datos dle origen, los almacena en list1 y los manda dibujar...
sub LeerDatos
...
while reader.read
...
list1.add(x,y)
end while
call Dibujar
fin sub
Y luego la de dibujar...
' escala y dibuja la gráfica...
Sub dibujar
dim list2 as tipo?
list2= Escalar(list1, EscalaX, EscalaY)
... myPane.AddCurve(..., list2)
Fin sub
ambos scrolls para escalar eje x e Y:
sub ScrollX_change(...)
EscalaX = scrollx.value/100
labelejeX.Text = "Factor de escala del eje X: " & escalaX.ToString
Call dibujar
End sub
sub ScrollY_change(...)
EscalaY = scrollY.value/100
labelejeY.Text = "Factor de escala del eje Y: " & EscalaY.ToString
Call dibujar
End sub
Saludos NEBIRE!!
con respecto al zoom, funciona perfect, en la grafica puedo acercar y ver los valores exactamente como estan almacenados en la base de datos, nunca habia graficado en ningun lenguaje, hasta ahora estoy aprendiendo de ello, mil gracias por tu ayuda.
he estado investigando en la web, no existe muchos ejemplos para Vb, solo se consiguen en C# vb.... estoy estudiando este codigo que hicieron, fijate en el link que posteare:
https://yazilimciyamektup.wordpress.com/tag/multiple-y-axis/ (https://yazilimciyamektup.wordpress.com/tag/multiple-y-axis/)
esta en ruso o aleman, algo asi..... al menos el codigo se puede entender un poco, segun he entendido, tengo que declarar al eje x como de tipo Date:
myPane.XAxis.Type = ZedGraph.AxisType.Date
myPane.XAxis.Scale.Format = "HH: mm: ss"
myPane.XAxis.Scale.MajorUnit = DateUnit.Second
lo diferencia es que el obtiene la fecha actual del sistema, en mi caso tendria que convertir el campo de tipo time a TimeSpan sino me equivoco.
Ya logre hacerlo, he aqui la respuesta por si alguien llegue a necesitarlo:
'Configuramos la Graph en el eje 'X' como tipo Date
myPane.XAxis.Type = ZedGraph.AxisType.Date
myPane.XAxis.Scale.Format = "HH:mm:ss"
myPane.XAxis.Scale.MajorUnit = DateUnit.Second
.
.
.
Dim list1 = New PointPairList()
Dim y As Single
Dim x As Single
Dim c As Date
If reader.Read Then
While reader.Read()
y = Replace(reader.GetString(3), ".", ",")
c = Convert.ToDateTime(reader.GetString(11))
x = c.ToOADate()
list1.Add(x, y)
End While
reader.Close()
End If
usando la funcion ToOADate() que nos sirve para Convertir el valor de esta instancia en la fecha/hora de Automation OLE equivalente.