public
Last active

Snapshot of the script I wrote to generate http://vimeo.com/26157684

  • Download Gist
database.sql
SQL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
CREATE TABLE `stations` (
`id` varchar(9) COLLATE latin1_general_ci NOT NULL,
`postalcode` varchar(5) COLLATE latin1_general_ci NOT NULL,
`name` varchar(255) COLLATE latin1_general_ci NOT NULL,
`longitude` decimal(5,2) NOT NULL,
`latitude` decimal(5,2) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci COMMENT='BfS Sensor Stations';
 
CREATE TABLE `values_2h` (
`station_id` varchar(9) NOT NULL,
`datetime_utc` datetime NOT NULL,
`dose` decimal(6,3) NOT NULL,
`status` tinyint(4) NOT NULL,
UNIQUE KEY `uniq` (`station_id`,`datetime_utc`),
KEY `station_id` (`station_id`),
KEY `datetime_utc` (`datetime_utc`),
KEY `status` (`status`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='2 hour unverified values';
 
 
INSERT IGNORE INTO `stations` (`id`, `postalcode`, `name`, `longitude`, `latitude`)
VALUES ('010010001', '24941', 'Flensburg', 9.38, 54.78);
 
INSERT IGNORE INTO `values_2h` (`station_id`, `datetime_utc`, `dose`, `status`)
VALUES ('010010001', '2011-06-15 00:00:00', 0.078, 0);
draw_radiation_stills.py
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
#!/bin/python
# -*- coding: utf-8 -*-
 
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
import sys
import MySQLdb
import math
from time import sleep
import pygame
from pygame.locals import *
import pygame.gfxdraw
import gradients
 
# globals
title = "My Game"
screen=None
clock = None
framerate = 25
black = [ 0, 0, 0]
white = [255,255,255]
almostwhite = [230,230,230]
blue = [ 0, 0,255]
green = [ 0,255, 0]
red = [255, 0, 0]
data = None
dates = None
date_cursor = 0
dotradius = 4
value_color_cache = {}
 
class MySQLDataStore:
def __init__(self, dbname, host='localhost', user='root', password=''):
try:
self.conn = MySQLdb.connect (host = host, user = user, passwd = password, db = dbname)
self.cursor = self.conn.cursor(MySQLdb.cursors.DictCursor)
except MySQLdb.Error, e:
print "Error %d: %s" % (e.args[0], e.args[1])
sys.exit (1)
def get_rows(self, sql):
try:
self.cursor.execute(sql)
rows = []
while (1):
row = self.cursor.fetchone()
if row == None:
break
rows.append(row)
return rows
except MySQLdb.Error, e:
print "Error %d: %s" % (e.args[0], e.args[1])
 
class WeatherData(MySQLDataStore):
def __init__(self, dbname, host='localhost', user='root', password=''):
MySQLDataStore.__init__(self, dbname, host, user, password)
self.read_stations()
def read_stations(self):
stations = self.get_rows('SELECT * FROM weather_stations')
self.stations = {}
for station in stations:
self.stations[int(station['id'])] = station
def get_values(self, date, station_id):
return self.get_rows('SELECT * FROM weather WHERE station_id="'+ str(station_id) +'" AND date="'+ date.strftime('%Y%m%d') +'"')[0]
 
class RadiationData(MySQLDataStore):
"""Access to radiation data via the local MySQL database"""
def __init__(self, dbname, host='localhost', user='root', password=''):
MySQLDataStore.__init__(self, dbname, host, user, password)
self.read_stats()
self.init_map()
self.read_stations()
#self.read_values()
def init_map(self):
self.bmap = Basemap(width=1200000, # width of map in meters
height=900000, # height of map in meters
projection='aeqd',
lat_0=51.1, lon_0=9.9, # center point of map
resolution='l',
area_thresh=10000.)
def read_stats(self):
stats = self.get_rows('SELECT MIN(dose) AS mindose, AVG(dose) AS avgdose, STD(dose) AS stddevdose, MAX(dose) AS maxdose FROM values_2h')[0]
self.mindose = float(stats['mindose'])
self.avgdose = float(stats['avgdose'])
self.stddevdose = float(stats['stddevdose'])
self.maxdose = float(stats['maxdose'])
def read_stations(self):
stations_flat = self.get_rows('SELECT id, latitude, longitude FROM stations')
self.stations = {}
self.stations_min_x = None
self.stations_min_y = None
self.stations_max_x = 0
self.stations_max_y = 0
for station in stations_flat:
x,y = self.bmap(station['longitude'], station['latitude'])
self.stations[station['id']] = {
'lat': station['latitude'],
'long': station['longitude'],
'x': x,
'y': y
}
if self.stations_min_x is None:
self.stations_min_x = x
if self.stations_min_y is None:
self.stations_min_y = x
self.stations_min_x = min(self.stations_min_x, x)
self.stations_min_y = min(self.stations_min_y, y)
self.stations_max_x = max(self.stations_max_x, x)
self.stations_max_y = max(self.stations_max_y, y)
self.stations_xspan = self.stations_max_x - self.stations_min_x
self.stations_yspan = self.stations_max_y - self.stations_min_y
print "Horizontal span:", self.stations_xspan, "meters"
print "Vertical span:", self.stations_yspan, "meters"
def set_dimensions(self, width, height):
smallside = min(width, height) * 0.9
largedimension = max(self.stations_xspan, self.stations_yspan)
self.position_factor = largedimension / smallside
self.screen_height = height
print "Resolution: ", self.position_factor, "meters per pixel"
# set colorfactor
#print "span between min", self.mindose, "and 3x average", (3 * self.avgdose), "is", (3 * self.avgdose - self.mindose)
self.colorfactor = 1.0 / ( 2 * self.avgdose - self.mindose )
print "colorfactor:", self.colorfactor
def get_dates(self, limit=None):
sql = 'SELECT DISTINCT datetime_utc FROM values_2h '
if limit is not None:
sql = sql + ' WHERE datetime_utc >= "2011-07-06 02:00:00"'
sql = sql + ' ORDER BY datetime_utc'
rows = self.get_rows(sql)
dates = []
for row in rows:
dates.append(row['datetime_utc'])
return dates
def get_values_for_date(self, date):
rows = self.get_rows('SELECT station_id, dose FROM values_2h WHERE datetime_utc = "'+ date.strftime('%Y-%m-%d %H-%M-%S') +'"')
values = {}
for row in rows:
values[row['station_id']] = row['dose']
return values
def value_color(self, val):
global value_color_cache
color = None
strval = str(val)
if strval not in value_color_cache:
scaled_value = (float(val) - self.mindose) * self.colorfactor * 255.0
color = int(min(scaled_value, 255.0))
value_color_cache[strval] = color
else:
color = value_color_cache[strval]
return [color, color, color, 255]
def scale_position(self, x, y):
return [(x - self.stations_min_x)/self.position_factor, self.screen_height-((y - self.stations_min_y)/self.position_factor)-(self.screen_height*0.15)]
def average(self, values):
return float(sum(values) / len(values))
def median(self, values):
return np.percentile(values, 50.0, None)
 
def quartile1(self, values):
return np.percentile(values, 25.0, None)
 
def quartile3(self, values):
return np.percentile(values, 75.0, None)
 
def percentile95(self, values):
return np.percentile(values, 95.0, None)
 
class MyGame:
"""The Main PyMan Class - This class handles the main
initialization and creating of the Game."""
def __init__(self, width=1024, height=768):
global screen, clock, title, data, dates
"""Initialize Game"""
data = RadiationData('radiationgermany')
dates = data.get_dates()
pygame.init()
clock=pygame.time.Clock()
self.width = width
self.height = height
self.screen = pygame.display.set_mode((self.width, self.height))
self.wdata = WeatherData('radiationgermany')
data.set_dimensions(self.width, self.height)
pygame.display.set_caption(title)
 
def MainLoop(self):
"""This is the Main Loop of the Game"""
global clock, black, green, white, blue, red, almostwhite, data, dates, date_cursor, dotradius
myriad_light_big = pygame.font.Font('fonts/myriad/MyriadPro-Light.otf', 38)
myriad_regular_big = pygame.font.Font('fonts/myriad/MyriadPro-Regular.otf', 38)
myriad_bold_big = pygame.font.Font('fonts/myriad/MyriadPro-Bold.otf', 38)
myriad_light_small = pygame.font.Font('fonts/myriad/MyriadPro-Light.otf', 18)
myriad_light_tiny = pygame.font.Font('fonts/myriad/MyriadPro-Light.otf', 14)
# explanation text
text1 = 'Displaying gamma radiation dose values over time,'
text2 = 'brighter colors indicate higher dose values.'
infoline1 = myriad_light_small.render(text1.decode('utf-8').encode('latin'), 1, [200, 200, 200])
infoline2 = myriad_light_small.render(text2.decode('utf-8').encode('latin'), 1, [200, 200, 200])
# imprint text
text1 = "Visualization by Marian Steinbach - www.sendung.de"
text2 = "Data courtesy of Bundesamt für Strahlenschutz (bfs) - www.bfs.de"
imprint1 = myriad_light_small.render(text1.decode('utf-8').encode('latin'), 1, [200, 200, 200])
imprint2 = myriad_light_small.render(text2.decode('utf-8').encode('latin'), 1, [200, 200, 200])
# scale gradient
scalegradient_height = 150.0
scalegradient = gradients.vertical((10, int(scalegradient_height)), (255,255,255,255), (0,0,0,255))
lastframe_values = {}
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == KEYDOWN:
# escape
if event.key == 27:
sys.exit()
datestring = dates[date_cursor].strftime('%Y-%m-%d %H:%M')
daystring = dates[date_cursor].strftime('%Y%m%d')
# fetch values
values = data.get_values_for_date(dates[date_cursor])
# 10400 = Düsseldorf,
#weather = self.wdata.get_values(dates[date_cursor], '10400');
#if weather['precipitation_height'] > 0:
# print daystring, "precipitation_height", weather['precipitation_height']
rawvalues = []
for val in values.values():
rawvalues.append(float(val))
# calculate average value
#average = data.average(rawvalues)
#averagelabel_yoffset = int(scalegradient_height - (scalegradient_height * (average - data.mindose)))
#averagetext = "Average: " + ("%.3f" % average) + " µSv/h"
#averagelabel = myriad_light_tiny.render(averagetext.decode('utf-8').encode('latin'), 1, [200, 200, 200])
# min label
minlabel_yoffset = int(scalegradient_height)
minlabeltext = "Min: " + ("%.3f" % data.mindose) + " µSv/h"
minlabel = myriad_light_tiny.render(minlabeltext.decode('utf-8').encode('latin'), 1, [200, 200, 200])
# median label
median = data.median(rawvalues);
medianlabel_yoffset = int(scalegradient_height - (scalegradient_height * (median - data.mindose) * data.colorfactor))
mediantext = "Median: " + ("%.3f" % median) + " µSv/h"
medianlabel = myriad_light_tiny.render(mediantext.decode('utf-8').encode('latin'), 1, [200, 200, 200])
perc25 = data.quartile1(rawvalues)
perc75 = data.quartile3(rawvalues)
perc95 = data.percentile95(rawvalues)
perc25label_yoffset = int(scalegradient_height - (scalegradient_height * (perc25 - data.mindose) * data.colorfactor))
perc75label_yoffset = int(scalegradient_height - (scalegradient_height * (perc75 - data.mindose) * data.colorfactor))
perc95label_yoffset = int(scalegradient_height - (scalegradient_height * (perc95 - data.mindose) * data.colorfactor))
perc25text = "1. Quartile: " + ("%.3f" % perc25) + " µSv/h"
perc75text = "3. Quartile: " + ("%.3f" % perc75) + " µSv/h"
perc95text = "95th Percentile: " + ("%.3f" % perc95) + " µSv/h"
perc25label = myriad_light_tiny.render(perc25text.decode('utf-8').encode('latin'), 1, [200, 200, 200])
perc75label = myriad_light_tiny.render(perc75text.decode('utf-8').encode('latin'), 1, [200, 200, 200])
perc95label = myriad_light_tiny.render(perc95text.decode('utf-8').encode('latin'), 1, [200, 200, 200])
# background
self.screen.fill([47, 40, 54])
# draw date label
#datelabel = myriad_light_big.render(datestring, 1, [200, 200, 200])
daylabel = myriad_bold_big.render(dates[date_cursor].strftime('%B %d, %Y'), 1, [200, 200, 200])
hourlabel = myriad_light_big.render(dates[date_cursor].strftime('%Hh'), 1, [200, 200, 200])
self.screen.blit(daylabel, [int(self.width * 0.55), int(self.height * 0.2)])
self.screen.blit(hourlabel, [int(self.width * 0.55), int(self.height * 0.2)+50])
# explanation
self.screen.blit(infoline1, [int(self.width * 0.55), int(self.height * 0.7)])
self.screen.blit(infoline2, [int(self.width * 0.55), int(self.height * 0.7)+30])
# draw info text
self.screen.blit(imprint1, [int(self.width * 0.55), int(self.height * 0.7) + 90])
self.screen.blit(imprint2, [int(self.width * 0.55), int(self.height * 0.7) + 120])
# scale gradient
self.screen.blit(scalegradient, [int(self.width * 0.55), int(self.height * 0.4)])
# min label
pygame.gfxdraw.hline(self.screen, int(self.width * 0.55), int(self.width * 0.55)+15, int(self.height * 0.4)+minlabel_yoffset, [200, 200, 200])
self.screen.blit(minlabel, [int(self.width * 0.55) + 20, int(self.height * 0.4) + minlabel_yoffset - 7])
# median label
pygame.gfxdraw.hline(self.screen, int(self.width * 0.55), int(self.width * 0.55)+15, int(self.height * 0.4)+medianlabel_yoffset, [200, 200, 200])
self.screen.blit(medianlabel, [int(self.width * 0.55) + 20, int(self.height * 0.4) + medianlabel_yoffset - 7])
# 25 percentile line
#pygame.gfxdraw.hline(self.screen, int(self.width * 0.55), int(self.width * 0.55)+15, int(self.height * 0.4)+perc25label_yoffset, [200, 200, 200])
# 75 percentile line
#pygame.gfxdraw.hline(self.screen, int(self.width * 0.55), int(self.width * 0.55)+15, int(self.height * 0.4)+perc75label_yoffset, [200, 200, 200])
# 95 percentile line
pygame.gfxdraw.hline(self.screen, int(self.width * 0.55), int(self.width * 0.55)+15, int(self.height * 0.4)+perc95label_yoffset, [200, 200, 200])
self.screen.blit(perc95label, [int(self.width * 0.55) + 20, int(self.height * 0.4) + perc95label_yoffset - 7])
# draw values
for station_id in data.stations.keys():
val = None
if station_id in values:
val = values[station_id]
elif station_id in lastframe_values:
val = lastframe_values[station_id]
if val is not None:
color = data.value_color(val)
#print color
[xcoord, ycoord] = data.scale_position(data.stations[station_id]['x'], data.stations[station_id]['y'])
x = int(xcoord + self.width/7)
y = int(ycoord + self.height/10)
pygame.gfxdraw.filled_circle(self.screen, x, y, dotradius, color)
pygame.gfxdraw.aacircle(self.screen, x, y, dotradius, color)
lastframe_values[station_id] = val
# debugging with a random stations 032510411
#debugstations = ['032510411', '095751521']
#for debugstation in debugstations:
# [xcoord, ycoord] = data.scale_position(data.stations[debugstation]['x'], data.stations[debugstation]['y'])
# x = int(xcoord + self.width/7)
# y = int(ycoord + self.height/10)
# pygame.gfxdraw.hline(self.screen, x+dotradius, x+10, y, [200, 200, 200])
# stationtext = ("%.3f" % values[debugstation]) + " µSv/h"
# stationlabel = myriad_light_tiny.render(stationtext.decode('utf-8').encode('latin'), 1, [200, 200, 200])
# self.screen.blit(stationlabel, [x + 15, y-7])
# save buffer
pygame.image.save(self.screen, 'pngs/png_' + ('%05d' % date_cursor) + '.png')
# end drawing
#print date_cursor, datestring, "median:", median, "perc25:", perc25, "perc75:", perc75, "perc95:", perc95
if len(dates) > (date_cursor + 1):
date_cursor += 1
#else:
# sys.exit() # game pause
clock.tick(15)
pygame.display.flip()
 
if __name__ == "__main__":
MainWindow = MyGame(1280, 720)
MainWindow.MainLoop()

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.