plotim.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. # # P L O T . I M # #
  2. # https://github.com/tbartos14/plotim
  3. # version 0.5.5, 5/22/2018
  4. # readme needs update for residual plots
  5. import tkinter as tk
  6. import math
  7. import numpy as np
  8. def vertical_text(text):
  9. newtext = ""
  10. for character in text:
  11. newtext += character + "\n"
  12. return newtext
  13. class linear_plot(object):
  14. def __init__(self, bordernorth=50, bordersouth=50, bordereast=30, borderwest=50,
  15. title="Linear Plot", \
  16. draw_lines=True, draw_points=False, ytitle="y", xtitle="x", line_colors=["#0000bb"],
  17. image=None):
  18. self.bordernorth = bordernorth
  19. self.bordersouth = bordersouth
  20. self.bordereast = bordereast
  21. self.borderwest = borderwest
  22. self.originalyaxistitle = ytitle
  23. self.yaxistitle = vertical_text(ytitle)
  24. self.xaxistitle = xtitle
  25. self.yaxis = [0, 10]
  26. self.xaxis = [0, 10]
  27. self.title = title
  28. self.draw_lines = draw_lines
  29. self.draw_points = draw_points
  30. self.linecolors = line_colors
  31. self.image = image
  32. def create_canvas(self, master):
  33. self.master = master
  34. self.canvas = tk.Canvas(self.master)
  35. self.canvas.bind("<Configure>", self.on_resize)
  36. self.windowx = self.canvas.winfo_reqwidth()
  37. self.windowy = self.canvas.winfo_reqheight()
  38. self.on_resize()
  39. return self.canvas
  40. def set_scale(self, xaxis, yaxis):
  41. self.yaxis = yaxis
  42. self.xaxis = xaxis
  43. def on_resize(self,event = None):
  44. if event:
  45. self.windowx = event.width - 4
  46. self.windowy = event.height - 4
  47. self.canvas.config(width=self.windowx, height=self.windowy)
  48. self.canvas.delete("all")
  49. self.graphx = self.windowx - self.bordereast - self.borderwest
  50. self.graphy = self.windowy - self.bordernorth - self.bordersouth
  51. self.canvas.create_text(self.borderwest + self.graphx / 2, self.bordernorth / 2, text=self.title)
  52. self.canvas.create_text(self.borderwest / 3, self.bordernorth + self.graphy / 2, text=self.yaxistitle)
  53. self.canvas.create_text(self.borderwest + self.graphx / 2, self.windowy - self.bordersouth / 3,
  54. text=self.xaxistitle)
  55. self.canvas.create_rectangle(self.bordernorth, self.borderwest, (self.windowx - self.bordereast),
  56. (self.windowy - self.bordersouth), fill="white", outline="white")
  57. # finding limits for axis
  58. self.yrange = abs(self.yaxis[1] - self.yaxis[0]) - 1
  59. self.xrange = abs(self.xaxis[1] - self.xaxis[0]) - 1
  60. # choosing what kind of scale to use
  61. self.yrangefactor = -round(math.log10(self.yrange) - 1)
  62. self.xrangefactor = -round(math.log10(self.xrange) - 1)
  63. # determining how many lines need to be placed
  64. # minimums/maximums for ease of reading
  65. self.yrangemin = ((int((self.yaxis[0]) * (10 ** (self.yrangefactor)))) / (10 ** (self.yrangefactor)))
  66. self.xrangemin = ((int((self.xaxis[0]) * (10 ** (self.xrangefactor)))) / (10 ** (self.xrangefactor)))
  67. self.yrangemax = ((int((self.yaxis[0]) * (10 ** (self.yrangefactor)))) / (10 ** (self.yrangefactor)) + (
  68. (int(self.yrange * (10 ** self.yrangefactor)) + 1) * (10 ** (-1 * self.yrangefactor))))
  69. self.xrangemax = ((int((self.xaxis[0]) * (10 ** (self.xrangefactor)))) / (10 ** (self.xrangefactor)) + (
  70. (int(self.xrange * (10 ** self.xrangefactor)) + 1) * (10 ** (-1 * self.xrangefactor))))
  71. # determining increments
  72. # finding if scales are appropriate
  73. self.additionalscaley = 0
  74. self.additionalscalex = 0
  75. # seeing if data needs more space (y)
  76. while True:
  77. if self.yaxis[1] > self.yrangemax:
  78. self.additionalscaley = self.additionalscaley + 1
  79. self.yrangemax = (
  80. (int((self.yaxis[0]) * (10 ** (self.yrangefactor)))) / (10 ** (self.yrangefactor)) + (
  81. (int(self.yrange * (10 ** self.yrangefactor)) + 1 + self.additionalscaley) * (
  82. 10 ** (-1 * self.yrangefactor))))
  83. else:
  84. break
  85. # (x)
  86. while True:
  87. if self.xaxis[1] > self.xrangemax:
  88. self.additionalscalex = self.additionalscalex + 1
  89. self.xrangemax = (
  90. (int((self.xaxis[0]) * (10 ** (self.xrangefactor)))) / (10 ** (self.xrangefactor)) + (
  91. (int(self.xrange * (10 ** self.xrangefactor)) + 1 + self.additionalscalex) * (
  92. 10 ** (-1 * self.xrangefactor))))
  93. else:
  94. break
  95. self.yincrement = int(self.yrange * (10 ** self.yrangefactor)) + self.additionalscaley + 1
  96. self.xincrement = int(self.xrange * (10 ** self.xrangefactor)) + self.additionalscalex + 1
  97. # now we determine y
  98. for increment in range(0, self.yincrement + 1):
  99. self.canvas.create_line(self.borderwest + 1,
  100. (self.windowy - self.bordersouth) - (increment / self.yincrement) * (
  101. self.windowy - self.bordernorth - self.bordersouth), \
  102. self.borderwest + self.graphx,
  103. (self.windowy - self.bordersouth) - (increment / self.yincrement) * (
  104. self.windowy - self.bordernorth - self.bordersouth), fill="#bbbbbb",
  105. dash=(2, 2), width=2 if increment % 5 == 0 else 1)
  106. self.canvas.create_text(self.borderwest - 12,
  107. (self.windowy - self.bordersouth) - (increment / self.yincrement) * (
  108. self.windowy - self.bordernorth - self.bordersouth), \
  109. text="{0:4.4}".format((int((self.yaxis[0]) * (10 ** (self.yrangefactor)))) / (
  110. 10 ** (self.yrangefactor)) + (
  111. (increment) * (10 ** (-1 * self.yrangefactor)))))
  112. # determining x
  113. for increment in range(0, self.xincrement + 1):
  114. self.canvas.create_line(
  115. self.bordersouth + (increment / self.xincrement) * (self.windowx - self.bordereast - self.borderwest),
  116. (self.windowy - self.bordersouth) - 1, \
  117. self.bordersouth + (increment / self.xincrement) * (self.windowx - self.bordereast - self.borderwest),
  118. (self.windowy - self.bordersouth - self.graphy), fill="#bbbbbb", dash=(2, 2), width=2 if increment % 5 == 0 else 1)
  119. self.canvas.create_text(
  120. self.borderwest + (increment / self.xincrement) * (self.windowx - self.bordereast - self.borderwest),
  121. (self.windowy - self.bordersouth) + 12, \
  122. text="{0:4.4}".format((int((self.xaxis[0]) * (10 ** (self.xrangefactor)))) / (10 ** (self.xrangefactor)) + (
  123. (increment) * (10 ** (-1 * self.xrangefactor)))))
  124. self.canvas.create_line(self.bordernorth, self.borderwest, self.bordernorth, (self.windowy - self.bordersouth))
  125. self.canvas.create_line(self.borderwest, (self.windowy - self.bordersouth), (self.windowx - self.bordereast),
  126. (self.windowy - self.bordersouth))
  127. return self.canvas
  128. def point_to_coords(self, x, y, offsetX=0, offsetY=0):
  129. return ((x - self.xrangemin) * (self.windowx - self.bordereast - self.borderwest) / (self.xrangemax - self.xrangemin) + self.borderwest + offsetX,
  130. (self.windowy - self.bordersouth) - (y - self.yrangemin) * (self.windowy - self.bordernorth - self.bordersouth) / (self.yrangemax - self.yrangemin) + offsetY)
  131. def point_to_coords_np(self, coords, offsetX=0, offsetY=0):
  132. coords[:,0] = (coords[:,0] - self.xrangemin) * (self.windowx - self.bordereast - self.borderwest) / (self.xrangemax - self.xrangemin) + self.borderwest + offsetX
  133. coords[:,1] = (self.windowy - self.bordersouth) - (coords[:,1] - self.yrangemin) * (self.windowy - self.bordernorth - self.bordersouth) / (self.yrangemax - self.yrangemin) + offsetY
  134. ovals = []
  135. images = []
  136. def plot_data(self, xs, ys):
  137. #self.canvas.delete("graph")
  138. # adding lines
  139. if self.draw_lines == True:
  140. self.canvas.delete("line")
  141. for plot in range(len(xs)):
  142. xpoints = xs[plot]
  143. ypoints = ys[plot]
  144. linecolor = self.linecolors[plot]
  145. if isinstance(xpoints, (np.ndarray)) and isinstance(ypoints, (np.ndarray)):
  146. coords = np.stack((xpoints, ypoints), axis=1)
  147. coords = coords[~np.isnan(coords[:,1])]
  148. self.point_to_coords_np(coords)
  149. coords = coords.flatten().tolist()
  150. else:
  151. coords = []
  152. for point in range(0, len(xpoints)):
  153. if not math.isnan(ypoints[point]):
  154. coord = self.point_to_coords(xpoints[point], ypoints[point])
  155. coords.append(coord[0])
  156. coords.append(coord[1])
  157. if len(coords) > 2:
  158. self.canvas.create_line(
  159. coords,
  160. fill=linecolor, tags="line", width=3)
  161. # adding points!
  162. if self.draw_points == True:
  163. while len(self.ovals) < len(xs):
  164. self.ovals.append([None] * len(xs[0]))
  165. for plot in range(len(xs)):
  166. xpoints = xs[plot]
  167. ypoints = ys[plot]
  168. for point in range(0, len(xpoints)):
  169. if math.isnan(ypoints[point]):
  170. self.canvas.delete(self.ovals[plot][point])
  171. self.ovals[plot][point] = None
  172. elif self.ovals[plot][point] != None:
  173. self.canvas.coords(
  174. self.ovals[plot][point],
  175. *self.point_to_coords(xpoints[point], ypoints[point], 3, 3),
  176. *self.point_to_coords(xpoints[point], ypoints[point], -3, -3),
  177. )
  178. else:
  179. self.ovals[plot][point] = self.canvas.create_oval(
  180. *self.point_to_coords(xpoints[point], ypoints[point], 3, 3),
  181. *self.point_to_coords(xpoints[point], ypoints[point], -3, -3),
  182. fill="white", tags="oval")
  183. # use an image?
  184. if self.image != None:
  185. while len(self.images) < len(xs):
  186. self.images.append([None] * len(xs[0]))
  187. for plot in range(len(xs)):
  188. xpoints = xs[plot]
  189. ypoints = ys[plot]
  190. pointimage = tk.PhotoImage(file=self.image)
  191. for point in range(0, len(xpoints)):
  192. if math.isnan(ypoints[point]):
  193. self.canvas.delete(self.images[plot][point])
  194. self.images[plot][point] = None
  195. elif self.images[plot][point] != None:
  196. self.canvas.coords(
  197. self.images[plot][point],
  198. *self.point_to_coords(xpoints[point], ypoints[point])
  199. )
  200. else:
  201. self.images[plot][point] = self.canvas.create_image(
  202. *self.point_to_coords(xpoints[point], ypoints[point]),
  203. image=pointimage, tags="image")
  204. return self.canvas
  205. if __name__ == "__main__":
  206. # Datetime,RecNbr,WS_mph_Avg,PAR_Den_Avg,WS_mph_S_WVT,WindDir_SD1_WVT,AirTF_Avg,Rain_in_Tot,RH,WindDir_D1_WVT
  207. #!/usr/bin/env python3
  208. import urllib.request
  209. import time
  210. xpoints = []
  211. ypoints = []
  212. time = 0
  213. newlines = range(0, 100)
  214. for line in newlines:
  215. xpoints.append(time)
  216. time = time + (1 / 60)
  217. ypoints.append(line)
  218. plot1 = linear_plot(line_of_best_fit=True, \
  219. ytitle="Temperature F\u00B0", \
  220. xtitle="Time (hours)", \
  221. title="Temperature in Durham NH Today", \
  222. line_color="#2222ff", \
  223. windowx=1000, \
  224. windowy=700, )
  225. plot1.set_data(xpoints, ypoints)
  226. plot1.plot_data()