1   
   2   
   3   
   4  """A viewer for the MoSP simulator. 
   5   
   6  It uses a socket to receive geometric objects from the simulation and draws these onto a layer of OpenStreetMap-map-tiles. 
   7   
   8  @todo: draw triangles!!! 
   9  @todo: Zoom fit to BBox (Hmm...maybe trial and error is possible...might be tricky...) 
  10  @todo: Zoom to double click position (double clicks should be easy, accurate centering on coordinates is not supportet right now...could be hard) 
  11  """ 
  12   
  13  import time 
  14  import os 
  15  import struct 
  16  import math 
  17   
  18  import socket 
  19   
  20  import pyglet 
  21   
  22  pyglet.options['debug_gl'] = False 
  23  from pyglet import gl 
  24  from pyglet.window import key, mouse 
  25   
  26  from lib import tilenames as tiles 
  27  from lib.tileloader2 import TileLoader 
  28  from lib.calculations import * 
  29   
  30  __author__ = "P. Tute" 
  31  __maintainer__ = "B. Henne" 
  32  __contact__ = "henne@dcsec.uni-hannover.de" 
  33  __copyright__ = "(c) 2011-2012, DCSec, Leibniz Universitaet Hannover, Germany" 
  34  __license__ = "GPLv3" 
  35   
  36   
  37  TILE_SIZE = 256      
  38  KEYBOARD_SCROLL_VALUE = 50  
  39   
  40   
  41  MESSAGE_TYPES = {'\x00': 'coords', 
  42                   '\x01': 'point', 
  43                   '\x02': 'rectangle', 
  44                   '\x03': 'circle', 
  45                   '\x04': 'triangle', 
  46                   '\x05': 'text', 
  47                   '\x06': 'heatmap', 
  48                   '\x07': 'direct-text', 
  49                   '\xFD': 'delete', 
  50                   '\xFE': 'draw', 
  51                   '\xFF': 'simulation_ended', 
  52                   }   
  53   
  54   
  55  MESSAGE_SIZE = {'\x00': struct.calcsize('!dd'), 
  56                  '\x01': struct.calcsize('!iddi4dd'), 
  57                  '\x02': struct.calcsize('!i4di?4dd'), 
  58                  '\x03': struct.calcsize('!iddi?4dd'), 
  59                  '\x04': struct.calcsize('!i2d2d2d?4dd'), 
  60                  '\x05': struct.calcsize('!iddiii4did'), 
  61                  '\x06': struct.calcsize('!ddi4d'), 
  62                  '\x07': struct.calcsize('!iiii4did'), 
  63                  '\xFD': struct.calcsize('!i'), 
  64                  }    
  65   
  66   
  67  ID_TYPE_PREFIX = {'\x00': 0, 
  68                    '\x01': 0, 
  69                    '\x02': 100000, 
  70                    '\x03': 200000, 
  71                    '\x04': 300000, 
  72                    '\x05': 0, 
  73                    }  
  74   
  75   
  76  PIXEL_DISTANCE = {}  
  77  for i in xrange(19): 
  78      left = tiles.xy2latlon(0, 0, i)[1] 
  79      right =  tiles.xy2latlon(1, 0, i)[1] 
  80      PIXEL_DISTANCE[i] = (right - left) / TILE_SIZE 
  81   
  83   
  84      """A tool for displaying mosp-simulations. 
  85       
  86      @author: P. Tute 
  87       
  88      """ 
  89   
  90 -    def __init__(self, lat=0, lon=0, zoom=15, host='localhost', port=60001, ugly_drag=False, **kwargs): 
   91          """Initialize the viewer. 
  92   
  93          @param lat: Latitude to center view on (default 0). 
  94          @type lat: float 
  95          @param lon: Longitude to center view on (default 0). 
  96          @type lon: float 
  97          @param zoom: OSM-zomm-level to start with (default 16). 
  98          @type zoom: int in range [0, 18] 
  99          @param host: The host running the simulation (default 'localhost'). 
 100          @type host: string 
 101          @param port: The port used by the simulation (default 60001). 
 102          @type port: int 
 103          @param ugly_drag: If set to True, dragging the map with the mouse will look uglier, but run faster (default False). 
 104          @param kwargs: @see http://pyglet.org/doc/api/pyglet.window.Window-class.html#__init__ 
 105           
 106          """ 
 107   
 108          super(SimViewer, self).__init__(**kwargs) 
 109   
 110           
 111          self.number_of_tiles = -1 
 112           
 113           
 114          self.drawing_offset = 0 
 115          self.zoom = zoom 
 116          self.center_x, self.center_y = tiles.latlon2xy(lat, lon, self.zoom) 
 117           
 118          self.center_lat, self.center_lon = tiles.xy2latlon(self.center_x, self.center_y, self.zoom) 
 119          self.default_lat, self.default_lon = self.center_lat, self.center_lon 
 120          self.offset_x , self.offset_y = 0, 0 
 121          self.ugly_drag = ugly_drag 
 122   
 123          self.cache_dir = '.cache' 
 124          self.data_dir = 'data' 
 125          self.screenshot_dir = 'screenshots' 
 126          self.not_found_image = os.path.join(self.data_dir, 'image_not_found.png') 
 127          try: 
 128              os.mkdir(self.cache_dir) 
 129              print 'No cache folder found. Creating it.' 
 130          except OSError: 
 131              print 'Found cache folder.' 
 132   
 133          try: 
 134              os.mkdir(self.screenshot_dir) 
 135          except OSError: 
 136              pass 
 137          timestamp = time.localtime() 
 138          self.current_sc_dir = str(timestamp.tm_year) + '.' + str(timestamp.tm_mon) + '.' + str(timestamp.tm_mday) 
 139          try: 
 140              os.mkdir(os.path.join(self.screenshot_dir, self.current_sc_dir)) 
 141          except OSError: 
 142              pass 
 143   
 144           
 145          pyglet.gl.glClearColor(0.8,0.8,0.8,1.0) 
 146           
 147          self.mouse_drag = False 
 148          self.draw_fps = True 
 149           
 150          self.ended = False 
 151          self.draw_end_overlay = False 
 152          self.end_text1 = 'End of simulation.' 
 153          self.end_label1 = pyglet.text.Label(self.end_text1, 
 154                                             font_name='Times New Roman', 
 155                                             font_size=36, 
 156                                             color=(255, 255, 255, 255), 
 157                                             x=self.width/2, y=self.height/2, 
 158                                             anchor_x='center', anchor_y='center') 
 159          self.end_text2 = '(C)onnect to a new one? Show (l)ast screen? (Q)uit?' 
 160          self.end_label2 = pyglet.text.Label(self.end_text2, 
 161                                             font_name='Times New Roman', 
 162                                             font_size=18, 
 163                                             color=(255, 255, 255, 255), 
 164                                             x=self.width/2, y=self.height/2-80, 
 165                                             anchor_x='center', anchor_y='center') 
 166   
 167          self.copyright_text = u'Maps \xa9 OpenStreetMap contributors, CC-BY-SA' 
 168          self.copyright_label = pyglet.text.Label(self.copyright_text, 
 169                                                   font_name='Times New Roman', 
 170                                                   font_size=10, 
 171                                                   color=(0, 0, 0, 255), 
 172                                                   x=0, y=0, 
 173                                                   anchor_x='right', anchor_y='bottom') 
 174          self.fps = 0 
 175          self.last_draw = 0 
 176          self.fps_label = pyglet.text.Label(str(int(self.fps)), 
 177                                             font_name='Times New Roman', 
 178                                             font_size=14, 
 179                                             color=(0, 0, 0, 255), 
 180                                             x=20, y=20, 
 181                                             anchor_x='center', anchor_y='center') 
 182           
 183          self.tiles = {} 
 184          self.tiles_used = {} 
 185          self.tileloader = TileLoader(tile_list=self.tiles, cache_dir=self.cache_dir) 
 186   
 187           
 188          self.drawing_batch = pyglet.graphics.Batch() 
 189          self.points = {} 
 190          self.rectangles = {} 
 191          self.circles = {} 
 192          self.triangles = {} 
 193          self.text_data = {} 
 194          self.text_objects = {} 
 195          self.direct_text_objects = {} 
 196          self.point_coords = {} 
 197          self.point_coords_offset = [] 
 198          self.point_colors = {} 
 199          self.point_colors_all = [] 
 200          self.point_vertex_list = self.drawing_batch.add(1, gl.GL_POINTS, None,  
 201                                                          ('v2i/stream', (0, 0)), 
 202                                                          ('c4d/stream', (0, 0, 0, 0))) 
 203          self.quad_coords = {} 
 204          self.quad_coords_offset = [] 
 205          self.quad_colors = {} 
 206          self.quad_colors_all = [] 
 207          self.quad_vertex_list = self.drawing_batch.add(1, gl.GL_QUADS, None, 
 208                                                         ('v2i/stream', (0, 0)), 
 209                                                         ('c4d/stream', (0, 0, 0, 0))) 
 210          self.triangle_coords = {} 
 211          self.triangle_coords_offset = [] 
 212          self.triangle_colors = {} 
 213          self.triangle_colors_all = [] 
 214          self.triangle_vertex_list = self.drawing_batch.add(1, gl.GL_TRIANGLES, None, 
 215                                                         ('v2i/stream', (0, 0)), 
 216                                                         ('c4d/stream', (0, 0, 0, 0))) 
 217          self.line_loop_coords = {} 
 218          self.line_loop_colors = {} 
 219          self.line_loop_vertex_lists = {} 
 220          self.polygon_coords = {} 
 221          self.polygon_colors = {} 
 222          self.polygon_vertex_lists = {} 
 223          self.heatmap_batch = pyglet.graphics.Batch() 
 224          self.heatmap_data = [] 
 225          self.heatmap_point_coords = [] 
 226          self.heatmap_point_coords_offset = [] 
 227          self.heatmap_point_colors = [] 
 228          self.heatmap_point_list = self.heatmap_batch.add(1, gl.GL_POINTS, None,  
 229                                                          ('v2i/stream', (0, 0)), 
 230                                                          ('c4d/stream', (0, 0, 0, 0))) 
 231          self.heatmap_quad_coords = [] 
 232          self.heatmap_quad_coords_offset = [] 
 233          self.heatmap_quad_colors = [] 
 234          self.heatmap_quad_list = self.heatmap_batch.add(1, gl.GL_QUADS, None,  
 235                                                          ('v2i/stream', (0, 0)), 
 236                                                          ('c4d/stream', (0, 0, 0, 0))) 
 237   
 238          self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
 239          self.socket.setblocking(0) 
 240          self.host = host 
 241          self.port = port 
 242          try: 
 243              print 'connecting' 
 244              self.socket.connect((self.host, self.port)) 
 245              print 'connected' 
 246          except socket.error as (errno, message): 
 247              if errno == 115:     
 248                   
 249                   
 250                   
 251                  pass 
 252              elif errno == 36:    
 253                  pass 
 254              else: 
 255                  print 'error while connecting' 
 256                  print '\t', errno, message 
 257                  raise 
 258   
 259          pyglet.clock.schedule_interval(self.receive, 1/30.0) 
 260          pyglet.clock.schedule_interval(self.on_draw, 1/60.0) 
 261          pyglet.clock.schedule_interval(self.reload_tiles, 0.5) 
  262   
 264          """Receive data from a given port and parse it to create drawable objects. 
 265           
 266          @param dt: Time since last call. Necessary for pyglet-scheduling, ignored here. 
 267          @author: P. Tute 
 268           
 269          """ 
 270   
 271          new_points = {} 
 272          new_rects = {} 
 273          new_circles = {} 
 274          new_triangles = {} 
 275          new_texts = {} 
 276          new_heatmap = [] 
 277          while True: 
 278              try: 
 279                  message_type = self.socket.recv(1) 
 280                  if message_type == '': 
 281                       
 282                      break 
 283                  if MESSAGE_TYPES[message_type] == 'delete': 
 284                      type = self.socket.recv(1) 
 285                      id = struct.unpack('!i', self.socket.recv(MESSAGE_SIZE[message_type]))[0] 
 286                      self.remove_drawing(0, type, id) 
 287                  elif MESSAGE_TYPES[message_type] == 'coords': 
 288                      lat, lon = struct.unpack('!dd', self.socket.recv(MESSAGE_SIZE[message_type])) 
 289                      self.default_lat, self.default_lon = lat, lon 
 290                      self.center_x, self.center_y = tiles.latlon2xy(lat, lon, self.zoom) 
 291                      self.center_x -= 1 
 292                      self.center_lat, self.center_lon = tiles.xy2latlon(self.center_x, self.center_y, self.zoom) 
 293                      self.offset_x , self.offset_y = 0, 0 
 294                      self.update_tiles() 
 295                  elif MESSAGE_TYPES[message_type] == 'point': 
 296                       
 297                      data = self.socket.recv(MESSAGE_SIZE[message_type]) 
 298                      point = struct.unpack('!iddi4dd', data) 
 299                      new_points[point[0] + ID_TYPE_PREFIX[message_type]] = point[1:] 
 300                      if point[8] > 0: 
 301                          pyglet.clock.schedule_once(self.remove_drawing, point[8], message_type, point[0]) 
 302                  elif MESSAGE_TYPES[message_type] == 'rectangle': 
 303                       
 304                      data = self.socket.recv(MESSAGE_SIZE[message_type]) 
 305                      rect = struct.unpack('!i4di?4dd', data) 
 306                      new_rects[rect[0] + ID_TYPE_PREFIX[message_type]] = rect[1:] 
 307                      if rect[11] > 0: 
 308                          pyglet.clock.schedule_once(self.remove_drawing, rect[11], message_type, rect[0]) 
 309                  elif MESSAGE_TYPES[message_type] == 'circle': 
 310                       
 311                      data = self.socket.recv(MESSAGE_SIZE[message_type]) 
 312                      circle = struct.unpack('!iddi?4dd', data) 
 313                      new_circles[circle[0] + ID_TYPE_PREFIX[message_type]] = circle[1:] 
 314                      if circle[9] > 0: 
 315                          pyglet.clock.schedule_once(self.remove_drawing, circle[9], message_type, circle[0]) 
 316                  elif MESSAGE_TYPES[message_type] == 'triangle': 
 317                       
 318                      data = self.socket.recv(MESSAGE_SIZE[message_type]) 
 319                      triangle = struct.unpack('!i2d2d2d?4dd', data) 
 320                      new_triangles[triangle[0] + ID_TYPE_PREFIX[message_type]] = triangle[1:] 
 321                      print triangle[12] 
 322                      if triangle[12] > 0: 
 323                          pyglet.clock.schedule_once(self.remove_drawing, triangle[12], message_type, triangle[0]) 
 324                  elif MESSAGE_TYPES[message_type] =='text': 
 325                       
 326                      data = self.socket.recv(MESSAGE_SIZE[message_type]) 
 327                      text_data = struct.unpack('!iddiii4did', data) 
 328                      text_content = struct.unpack('!' + 'c' * text_data[10], self.socket.recv(struct.calcsize('!' + 'c' * text_data[10]))) 
 329                      text_data_list = list(text_data[1:]) 
 330                      text_data_list.append(text_content) 
 331                      new_texts[text_data[0]] = text_data_list 
 332                      if text_data[11] > 0: 
 333                          pyglet.clock.schedule_once(self.remove_drawing, text_data[11], message_type, text_data[0]) 
 334                  elif MESSAGE_TYPES[message_type] == 'heatmap': 
 335                       
 336                      data = self.socket.recv(MESSAGE_SIZE[message_type]) 
 337                      hm = struct.unpack('!ddi4d', data) 
 338                      self.heatmap_data.append(hm) 
 339                      new_heatmap.append(hm) 
 340                  elif MESSAGE_TYPES[message_type] == 'direct-text': 
 341                       
 342                      data = self.socket.recv(MESSAGE_SIZE[message_type]) 
 343                      id, x, y, fsize, r, g, b, a, tsize, ttl = struct.unpack('!iiii4did', data) 
 344                      if x < 0: 
 345                          x = self.width + x  
 346                      if y < 0: 
 347                          y = self.height + y  
 348                      text_content = struct.unpack('!' + 'c' * tsize, self.socket.recv(struct.calcsize('!' + 'c' * tsize))) 
 349                      self.direct_text_objects[id] = pyglet.text.Label("".join(text_content), 
 350                                                                       font_name='Times New Roman', 
 351                                                                       font_size=fsize, 
 352                                                                       color = tuple([int(i * 255) for i in (r, g, b, a)]), 
 353                                                                       x=x, y=y, 
 354                                                                       anchor_x='left', anchor_y='bottom') 
 355                  elif MESSAGE_TYPES[message_type] == 'draw': 
 356                      self.update_coordinates(new_points, new_rects, new_circles, new_triangles, new_texts, new_heatmap) 
 357                      self.update_vertex_lists() 
 358                      break 
 359                  elif MESSAGE_TYPES[message_type] == 'simulation_ended': 
 360                      self.ended = True 
 361                      self.draw_end_overlay = True 
 362                  else: 
 363                       
 364                      print '\twtf', repr(message_type) 
 365                      break 
 366              except socket.error as (errno, msg): 
 367                  if errno != 11: 
 368                      raise 
 369                  else: 
 370                       
 371                       
 372                      break 
  373   
 374 -    def update_coordinates(self, points=None, rects=None, circles=None, triangles=None, texts=None, hms=None, all=False): 
  375          """Calculate all coordinates necessary for drawing received points, rectangles and circles. 
 376           
 377          @param points: Points whose coordinates need to be calculated 
 378          @type points: dict 
 379          @param rects: Rectangles whose coordinates need to be calculated 
 380          @type rects: dict 
 381          @param circles: Circles whose coordinates need to be calculated 
 382          @type circles: dict 
 383          @param texts: Texts whose coordinates need to be calculated 
 384          @type texts: dict 
 385          @param hms: Heatmap-blips whose coordinates need to be calculated 
 386          @type hms: list 
 387          @param all: If all is True, all known coordinates will be redrawn. Other passed arguments will be ignored. 
 388          @type all: boolean 
 389          @author: P. Tute 
 390           
 391          """ 
 392   
 393           
 394          if not points: 
 395               
 396              points = {} 
 397          if all: 
 398               
 399              points = self.points 
 400          for point in points: 
 401              self.points[point] = points[point] 
 402              (lat, lon, rad, r, g, b, a, ttl) = points[point] 
 403              x, y = latlon_to_xy(lat, lon, self.zoom, self) 
 404              if rad > 0: 
 405                   
 406                  coords = [] 
 407                  coords.append(x - rad) 
 408                  coords.append(y - rad) 
 409                  coords.append(x + rad) 
 410                  coords.append(y - rad) 
 411                  coords.append(x + rad) 
 412                  coords.append(y + rad) 
 413                  coords.append(x - rad) 
 414                  coords.append(y + rad) 
 415                  self.quad_coords[point] = coords 
 416                  self.quad_colors[point] =  [r, g, b, a] * 4 
 417              else: 
 418                  self.point_coords[point] = [x, y] 
 419                  self.point_colors[point] = [r, g, b, a] 
 420   
 421           
 422          if not rects: 
 423              rects = {} 
 424          if all: 
 425              rects = self.rectangles 
 426          for rect in rects: 
 427              self.rectangles[rect] = rects[rect] 
 428              (minlat, minlon, maxlat, maxlon, line_width, filled, r, g, b, a, ttl) = rects[rect] 
 429              x_left, y_bottom = latlon_to_xy(minlat, minlon, self.zoom, self) 
 430              x_right, y_top = latlon_to_xy(maxlat, maxlon, self.zoom, self) 
 431              coords = [] 
 432              if filled: 
 433                   
 434                  coords.append(x_left) 
 435                  coords.append(y_bottom) 
 436                  coords.append(x_right) 
 437                  coords.append(y_bottom) 
 438                  coords.append(x_right) 
 439                  coords.append(y_top) 
 440                  coords.append(x_left) 
 441                  coords.append(y_top) 
 442                  self.quad_coords[rect] = coords 
 443                  self.quad_colors[rect] = [r, g, b, a] * 4 
 444              else: 
 445                   
 446                   
 447                   
 448   
 449                   
 450                  rad = 1 if line_width < 1 else line_width 
 451                  self.line_loop_colors[rect] = [] 
 452                  for i in xrange(rad): 
 453                      coords.extend((x_left + i, 
 454                                     y_bottom + i, 
 455                                     x_right - i, 
 456                                     y_bottom + i, 
 457                                     x_right - i, 
 458                                     y_top - i, 
 459                                     x_left + i, 
 460                                     y_top - i)) 
 461                      self.line_loop_colors[rect].extend((r, b, g, a) * 4) 
 462                  self.line_loop_coords[rect] = coords 
 463                  if not rect in self.line_loop_vertex_lists: 
 464                      self.line_loop_vertex_lists[rect] = pyglet.graphics.vertex_list(len(coords) / 2, 
 465                                                                                 'v2i', 'c4d') 
 466          if not circles: 
 467              circles = {} 
 468          if all: 
 469              circles = self.circles 
 470          for circle in circles: 
 471              self.circles[circle] = circles[circle] 
 472              (lat, lon, rad, filled, r, g, b, a, ttl) = circles[circle] 
 473              rad = self.meters_to_pixels(rad) 
 474              x, y = latlon_to_xy(lat, lon, self.zoom, self) 
 475              coords = bresenham_circle(x, y, rad) 
 476              colors = [r, g, b, a] * (len(coords) / 2) 
 477   
 478              if filled: 
 479                  self.polygon_coords[circle] = coords 
 480                  self.polygon_colors[circle] = colors 
 481                  if not circle in self.polygon_vertex_lists: 
 482                      self.polygon_vertex_lists[circle] = pyglet.graphics.vertex_list(len(coords) / 2, 
 483                                                                                 'v2i', 'c4d') 
 484              else: 
 485                  self.point_coords[circle] = coords 
 486                  self.point_colors[circle] = colors 
 487   
 488          if not triangles: 
 489              triangles = {} 
 490          if all: 
 491              triangles = self.triangles 
 492          for tri in triangles: 
 493              self.triangles[tri] = triangles[tri] 
 494              (lat1, lon1, lat2, lon2, lat3, lon3, filled, r, g, b, a, ttl) = triangles[tri] 
 495              x_1, y_1 = latlon_to_xy(lat1, lon1, self.zoom, self) 
 496              x_2, y_2 = latlon_to_xy(lat2, lon2, self.zoom, self) 
 497              x_3, y_3 = latlon_to_xy(lat3, lon3, self.zoom, self) 
 498              coords = [x_1, y_1, x_2, y_2, x_3, y_3] 
 499              if filled: 
 500                   
 501                  self.triangle_coords[tri] = coords 
 502                  self.triangle_colors[tri] = [r, g, b, a] * 3 
 503              else: 
 504                   
 505                  self.line_loop_colors[tri] = [r, g, b, a] * 3 
 506                  self.line_loop_coords[tri] = coords 
 507                  if not tri in self.line_loop_vertex_lists: 
 508                      self.line_loop_vertex_lists[tri] = pyglet.graphics.vertex_list(len(coords) / 2, 
 509                                                                                 'v2i', 'c4d') 
 510   
 511          if not texts: 
 512              texts = {} 
 513          if all: 
 514              texts = self.text_data 
 515          for text in texts: 
 516              self.text_data[text] = texts[text] 
 517              (lat, lon, x_off, y_off, fsize, r, g, b, a, tsize, ttl, content) = texts[text] 
 518              x, y = latlon_to_xy(lat, lon, self.zoom, self) 
 519              x += self.meters_to_pixels(x_off) + self.offset_x + self.drawing_offset 
 520              y += self.meters_to_pixels(y_off) + self.offset_y + self.drawing_offset 
 521              self.text_objects[text] = pyglet.text.Label("".join(content), 
 522                                                          font_name='Times New Roman', 
 523                                                          font_size=fsize, 
 524                                                          color = tuple([int(i * 255) for i in (r, g, b, a)]), 
 525                                                          x=x, y=y, 
 526                                                          anchor_x='left', anchor_y='bottom') 
 527   
 528           
 529          if not hms: 
 530              hms = [] 
 531          if all: 
 532              hms = self.heatmap_data 
 533              self.heatmap_point_coords = [] 
 534              self.heatmap_point_colors = [] 
 535              self.heatmap_quad_coords = [] 
 536              self.heatmap_quad_colors = [] 
 537          for hm in hms: 
 538              x, y = latlon_to_xy(hm[0], hm[1], self.zoom, self) 
 539              rad = hm[2] 
 540              color = hm[3:] 
 541              if rad > 0: 
 542                  self.heatmap_quad_coords.append(x - rad) 
 543                  self.heatmap_quad_coords.append(y - rad) 
 544                  self.heatmap_quad_coords.append(x + rad) 
 545                  self.heatmap_quad_coords.append(y - rad) 
 546                  self.heatmap_quad_coords.append(x + rad) 
 547                  self.heatmap_quad_coords.append(y + rad) 
 548                  self.heatmap_quad_coords.append(x - rad) 
 549                  self.heatmap_quad_coords.append(y + rad) 
 550                  self.heatmap_quad_colors.extend(color * 4) 
 551              else: 
 552                  self.heatmap_point_coords.append(x) 
 553                  self.heatmap_point_coords.append(y) 
 554                  self.heatmap_point_colors.extend(color) 
  555   
 557          """Add offsets to coordinates and update the used VertexLists.""" 
 558   
 559           
 560          if len(self.point_coords) > 0: 
 561              coords = [] 
 562              self.point_colors_all = [] 
 563              for point in self.point_coords: 
 564                  coords.extend(self.point_coords[point]) 
 565                  self.point_colors_all.extend(self.point_colors[point]) 
 566              self.point_coords_offset = [] 
 567              for i, coord in enumerate(coords): 
 568                  if not i%2: 
 569                       
 570                      self.point_coords_offset.append(coord + self.offset_x + self.drawing_offset) 
 571                  else: 
 572                      self.point_coords_offset.append(coord + self.offset_y + self.drawing_offset) 
 573   
 574           
 575          if len(self.quad_coords) > 0: 
 576              coords = [] 
 577              self.quad_colors_all = [] 
 578              for quad in self.quad_coords: 
 579                  coords.extend(self.quad_coords[quad]) 
 580                  self.quad_colors_all.extend(self.quad_colors[quad]) 
 581              self.quad_coords_offset = [] 
 582              for i, coord in enumerate(coords): 
 583                  if not i%2: 
 584                       
 585                      self.quad_coords_offset.append(coord + self.offset_x + self.drawing_offset) 
 586                  else: 
 587                      self.quad_coords_offset.append(coord + self.offset_y + self.drawing_offset) 
 588   
 589           
 590          if len(self.triangle_coords) > 0: 
 591              coords = [] 
 592              self.triangle_colors_all = [] 
 593              for triangle in self.triangle_coords: 
 594                  coords.extend(self.triangle_coords[triangle]) 
 595                  self.triangle_colors_all.extend(self.triangle_colors[triangle]) 
 596              self.triangle_coords_offset = [] 
 597              for i, coord in enumerate(coords): 
 598                  if not i%2: 
 599                       
 600                      self.triangle_coords_offset.append(coord + self.offset_x + self.drawing_offset) 
 601                  else: 
 602                      self.triangle_coords_offset.append(coord + self.offset_y + self.drawing_offset) 
 603   
 604           
 605          if len(self.line_loop_coords) > 0: 
 606              for line in self.line_loop_coords: 
 607                  coords = self.line_loop_coords[line] 
 608                  colors = self.line_loop_colors[line] 
 609                  coords_updated = [] 
 610                  for i, coord in enumerate(coords): 
 611                      if not i%2: 
 612                           
 613                          coords_updated.append(coord + self.offset_x + self.drawing_offset) 
 614                      else: 
 615                          coords_updated.append(coord + self.offset_y + self.drawing_offset) 
 616                  if self.line_loop_vertex_lists[line].get_size() != len(coords_updated) / 2: 
 617                      self.line_loop_vertex_lists[line].resize(len(coords_updated) / 2) 
 618                  self.line_loop_vertex_lists[line].vertices = coords_updated 
 619                  self.line_loop_vertex_lists[line].colors = colors 
 620   
 621           
 622          if len(self.polygon_coords) > 0: 
 623              for tri in self.polygon_coords: 
 624                  coords = self.polygon_coords[tri] 
 625                  colors = self.polygon_colors[tri] 
 626                  coords_updated = [] 
 627                  for i, coord in enumerate(coords): 
 628                      if not i%2: 
 629                           
 630                          coords_updated.append(coord + self.offset_x + self.drawing_offset) 
 631                      else: 
 632                          coords_updated.append(coord + self.offset_y + self.drawing_offset) 
 633                  if self.polygon_vertex_lists[tri].get_size() != len(coords_updated) / 2: 
 634                      self.polygon_vertex_lists[tri].resize(len(coords_updated) / 2) 
 635                  self.polygon_vertex_lists[tri].vertices = coords_updated 
 636                  self.polygon_vertex_lists[tri].colors = colors 
 637   
 638           
 639          if len(self.heatmap_point_coords) > 0: 
 640              self.heatmap_point_coords_offset = [] 
 641              for i, coord in enumerate(self.heatmap_point_coords): 
 642                  if not i%2: 
 643                       
 644                      self.heatmap_point_coords_offset.append(coord + self.offset_x + self.drawing_offset) 
 645                  else: 
 646                      self.heatmap_point_coords_offset.append(coord + self.offset_y + self.drawing_offset) 
 647          if len(self.heatmap_quad_coords) > 0: 
 648              self.heatmap_quad_coords_offset = [] 
 649              for i, coord in enumerate(self.heatmap_quad_coords): 
 650                  if not i%2: 
 651                       
 652                      self.heatmap_quad_coords_offset.append(coord + self.offset_x + self.drawing_offset) 
 653                  else: 
 654                      self.heatmap_quad_coords_offset.append(coord + self.offset_y + self.drawing_offset) 
  655   
 660   
 661 -    def get_image(self, x, y, z, layer='mapnik'): 
  662          """Load an image from the cache folder or download it. 
 663   
 664          Try to load from cache-folder first, download and cache if no image was found. 
 665          The image is placed in self.tiles by this method or by the TileLoader.load_images() after downloading. 
 666   
 667          @param x: OSM-tile number in x-direction 
 668          @type x: int 
 669          @param y: OSM-tile number in y-direction 
 670          @type y: int 
 671          @param z: OSM-zoom 
 672          @type z: int in range [0, 18] 
 673          @param layer: The used map layer (default 'mapnik') 
 674          @type layer: string (one of 'tah', 'oam' and 'mapnik') 
 675           
 676          """ 
 677   
 678          url = tiles.tileURL(x, y, z, layer) 
 679          parts = url.split('/')[-4:] 
 680   
 681          if not os.path.exists(os.path.join(self.cache_dir, *parts)): 
 682               
 683              self.tileloader.enqueue_tile(x, y, z, layer) 
 684              return 
 685           
 686          try: 
 687              image = pyglet.image.load(os.path.join(self.cache_dir, *parts)) 
 688          except: 
 689              image = pyglet.resource.image(self.not_found_image) 
 690          image.anchor_x = image.width / 2 
 691          image.anchor_y = image.height / 2 
 692          self.tiles[(x, y, z)] = image 
  693   
 695          """Recalculate drawing_offset and number_of_tiles if necessary, update tiles. 
 696   
 697          This is called by pyglet when the viewer is started or resized. 
 698   
 699          @see: http://pyglet.org/doc/api/pyglet.window.Window-class.html#on_resize 
 700   
 701          """ 
 702   
 703          super(SimViewer, self).on_resize(width, height) 
 704          number_of_tiles = ((max(self.width, self.height) / TILE_SIZE) / 2) + 2 
 705          if number_of_tiles != self.number_of_tiles: 
 706              size_of_combined_map = (2 * number_of_tiles + 1) * TILE_SIZE 
 707              self.drawing_offset = (max(self.width, self.height) - size_of_combined_map) / 2 
 708              self.number_of_tiles = number_of_tiles 
 709              self.update_tiles() 
  710   
 712          """Update the self.tiles and self.tiles_used dicts after changes. 
 713           
 714          When necessary load new images and delete old ones. 
 715          This should not be called to often since it causes all coordinates of all drawings to be recalculated. 
 716           
 717          """ 
 718   
 719          for y in xrange(-self.number_of_tiles, self.number_of_tiles + 1): 
 720              for x in xrange(-self.number_of_tiles, self.number_of_tiles + 1): 
 721                   
 722                  absolute_x = self.center_x + x 
 723                  absolute_y = self.center_y - y 
 724                  if (absolute_x, absolute_y, self.zoom) not in self.tiles: 
 725                       
 726                      self.get_image(absolute_x, absolute_y, self.zoom) 
 727                   
 728                   
 729                  self.tiles_used[(x + self.number_of_tiles, y + self.number_of_tiles)] = (absolute_x, absolute_y, self.zoom) 
 730   
 731           
 732          for coord in self.tiles.keys(): 
 733               
 734              if (not (self.center_x - 2 * self.number_of_tiles < coord[0] < self.center_x + 2 * self.number_of_tiles 
 735                 and self.center_y - 2 * self.number_of_tiles < coord[1] < self.center_y + 2 * self.number_of_tiles) 
 736                 and coord[2] == self.zoom): 
 737                  del self.tiles[coord] 
 738               
 739              if not (self.zoom - 4 < coord[2] < self.zoom + 4): 
 740                  del self.tiles[coord] 
 741   
 742          self.update_coordinates(all=True) 
 743          self.update_vertex_lists() 
 744          self.on_draw() 
  745   
 747          """Calculate the number of pixels that equal the given distance in. 
 748   
 749          @see: http://wiki.openstreetmap.org/wiki/Zoom_levels 
 750   
 751          @param m: Distance in meter 
 752          @type m: int 
 753          @returns: Distance in pixels 
 754          @rtype: int 
 755           
 756          """ 
 757   
 758          earth_cirumference_meters = 637813.70  
 759          lat = math.radians(self.center_lat) 
 760          distance_per_pixel = earth_cirumference_meters*math.degrees(math.cos(lat))/2**(self.zoom+8) 
 761          numer_of_pixels = m / distance_per_pixel 
 762          return int(numer_of_pixels) 
  763   
 765          """Draw the screen. 
 766   
 767          This is periodically called by pyglet. 
 768   
 769          @param dt: Time since last call. Necessary for scheduling but ignored here. 
 770   
 771          """ 
 772   
 773          self.tileloader.load_images() 
 774          self.clear() 
 775           
 776          for coord in self.tiles_used: 
 777              image = self.tiles[self.tiles_used[coord]] 
 778              x = coord[0] 
 779              y = coord[1] 
 780              image.blit(x * TILE_SIZE + self.offset_x + self.drawing_offset, 
 781                         y * TILE_SIZE + self.offset_y + self.drawing_offset, 
 782                         0) 
 783           
 784           
 785          gl.glEnable(gl.GL_BLEND) 
 786   
 787           
 788          if len(self.point_coords_offset) > 0: 
 789              if self.point_vertex_list.get_size() != len(self.point_coords_offset) / 2: 
 790                  self.point_vertex_list.resize(len(self.point_coords_offset) / 2) 
 791              self.point_vertex_list.vertices = self.point_coords_offset 
 792              self.point_vertex_list.colors = self.point_colors_all 
 793          if len(self.quad_coords_offset) > 0: 
 794              if self.quad_vertex_list.get_size() != len(self.quad_coords_offset) / 2: 
 795                  self.quad_vertex_list.resize(len(self.quad_coords_offset) / 2) 
 796              self.quad_vertex_list.vertices = self.quad_coords_offset 
 797              self.quad_vertex_list.colors = self.quad_colors_all 
 798          if len(self.triangle_coords_offset) > 0: 
 799              if self.triangle_vertex_list.get_size() != len(self.triangle_coords_offset) / 2: 
 800                  self.triangle_vertex_list.resize(len(self.triangle_coords_offset) / 2) 
 801              self.triangle_vertex_list.vertices = self.triangle_coords_offset 
 802              self.triangle_vertex_list.colors = self.triangle_colors_all 
 803          self.drawing_batch.draw() 
 804          if len(self.heatmap_point_coords_offset) > 0: 
 805              if self.heatmap_point_list.get_size() != len(self.heatmap_point_coords_offset) / 2: 
 806                  self.heatmap_point_list.resize(len(self.heatmap_point_coords_offset) / 2) 
 807              self.heatmap_point_list.vertices = self.heatmap_point_coords_offset 
 808              self.heatmap_point_list.colors = self.heatmap_point_colors 
 809          if len(self.heatmap_quad_coords_offset) > 0: 
 810              if self.heatmap_quad_list.get_size() != len(self.heatmap_quad_coords_offset) / 2: 
 811                  self.heatmap_quad_list.resize(len(self.heatmap_quad_coords_offset) / 2) 
 812              self.heatmap_quad_list.vertices = self.heatmap_quad_coords_offset 
 813              self.heatmap_quad_list.colors = self.heatmap_quad_colors 
 814          self.heatmap_batch.draw() 
 815          for line in self.line_loop_vertex_lists.values(): 
 816              line.draw(gl.GL_LINE_LOOP) 
 817          for poly in self.polygon_vertex_lists.values(): 
 818              poly.draw(gl.GL_POLYGON) 
 819          for text in self.text_objects.values(): 
 820              text.draw() 
 821          for text in self.direct_text_objects.values(): 
 822              text.draw() 
 823   
 824           
 825          self.copyright_label.x = self.width 
 826          pyglet.graphics.draw(4, gl.GL_QUADS, 
 827                               ('v2i', (self.copyright_label.x+2, self.copyright_label.y-2, 
 828                                        self.copyright_label.x-self.copyright_label.content_width-2, self.copyright_label.y-2, 
 829                                        self.copyright_label.x-self.copyright_label.content_width-2, self.copyright_label.y+self.copyright_label.content_height-1, 
 830                                        self.copyright_label.x+2, self.copyright_label.y+self.copyright_label.content_height-1)), 
 831                               ('c4d', (1, 1, 1, 0.7) * 4)) 
 832          self.copyright_label.draw() 
 833   
 834           
 835          if self.draw_fps: 
 836              now = time.time() 
 837              self.fps = self.fps*0.8 +  0.2 / (now - self.last_draw) 
 838              self.last_draw = now 
 839              self.fps_label.text = str(int(self.fps)) 
 840              self.fps_label.draw() 
 841   
 842          if self.draw_end_overlay: 
 843              pyglet.graphics.draw(4, gl.GL_QUADS, 
 844                                   ('v2i', (0, 0, 0, self.height, self.width, self.height, self.width, 0)), 
 845                                   ('c4d', (0, 0, 0, 0.5) * 4)) 
 846              self.end_label1.x = self.width / 2 
 847              self.end_label1.y = self.height / 2 
 848              self.end_label1.draw() 
 849              self.end_label2.x = self.width / 2 
 850              self.end_label2.y = self.height / 2 - 80 
 851              self.end_label2.draw() 
  852               
 854          """Zooms in or out of map by given factor. 
 855           
 856          @param factor: The factor to zoom by in OSM-zoom levels. 
 857          @type factor: int 
 858           
 859          """ 
 860   
 861          if factor > 0 and self.zoom + factor <= 18 or factor < 0 and self.zoom - factor >= 0: 
 862              self.zoom += factor 
 863              self.center_x, self.center_y = tiles.latlon2xy(self.center_lat, self.center_lon, self.zoom) 
 864              self.center_lat, self.center_lon = tiles.xy2latlon(self.center_x, self.center_y, self.zoom) 
 865              self.update_tiles() 
  866   
 868          """This is called by pyglet whenever a key is pressed. 
 869           
 870          @see: http://pyglet.org/doc/api/pyglet.window.Window-class.html#on_key_press 
 871           
 872          """ 
 873   
 874          super(SimViewer, self).on_key_press(symbol, modifiers) 
 875          if modifiers & key.MOD_CTRL: 
 876              if symbol == key.UP: 
 877                  self.change_zoom(1) 
 878              elif symbol == key.DOWN: 
 879                  self.change_zoom(-1) 
 880              elif symbol == key.F: 
 881                  self.set_fullscreen(not self.fullscreen) 
 882              elif symbol == key.S: 
 883                  self.take_screenshot() 
 884          elif symbol == key.UP: 
 885              self.offset_y -= KEYBOARD_SCROLL_VALUE 
 886              self.handle_offset() 
 887          elif symbol == key.DOWN: 
 888              self.offset_y += KEYBOARD_SCROLL_VALUE 
 889              self.handle_offset() 
 890          elif symbol == key.LEFT: 
 891              self.offset_x += KEYBOARD_SCROLL_VALUE 
 892              self.handle_offset() 
 893          elif symbol == key.RIGHT: 
 894              self.offset_x -= KEYBOARD_SCROLL_VALUE 
 895              self.handle_offset() 
 896          elif symbol == key.PLUS: 
 897              self.change_zoom(1) 
 898          elif symbol == key.MINUS: 
 899              self.change_zoom(-1) 
 900          elif symbol == key.F: 
 901              self.draw_fps = not self.draw_fps 
 902          elif symbol == key.Q: 
 903              self.close() 
 904          elif symbol == key.L: 
 905              if self.ended: 
 906                  self.draw_end_overlay = not self.draw_end_overlay 
 907          elif symbol == key.C: 
 908              if self.ended: 
 909                  self.ended = False 
 910                  self.draw_end_overlay = False 
 911                  self.reset_drawings() 
 912                  self.reconnect() 
 913          elif symbol == key.SPACE: 
 914              self.center_x, self.center_y = tiles.latlon2xy(self.default_lat, self.default_lon, self.zoom) 
 915              self.center_x -= 1 
 916              self.center_lat, self.center_lon = tiles.xy2latlon(self.center_x, self.center_y, self.zoom) 
 917              self.offset_x , self.offset_y = 0, 0 
 918              self.update_tiles() 
  919   
 921          """Take a screenshot of the current simulation and save it with the current timestamp as it's name.""" 
 922          shot = pyglet.image.get_buffer_manager().get_color_buffer() 
 923          time_of_shot = str(int(time.time())) 
 924          path = os.path.join(self.screenshot_dir, self.current_sc_dir) 
 925          tries = 0 
 926          filename = time_of_shot + '_' + str(tries) + '.png' 
 927          while os.path.exists(os.path.join(path, filename)): 
 928              tries += 1 
 929              filename = time_of_shot + '_' + str(tries) + '.png' 
 930          shot.save(os.path.join(path, filename)) 
  931   
 933          """Called, when mouse-dragging is recognized. 
 934   
 935          @see: http://pyglet.org/doc/api/pyglet.window.Window-class.html#on_mouse_drag 
 936           
 937          """ 
 938   
 939          if buttons & mouse.LEFT: 
 940              self.mouse_drag = True 
 941              self.offset_x = self.offset_x + dx 
 942              self.offset_y = self.offset_y + dy 
 943              if not self.ugly_drag: 
 944                  self.update_vertex_lists() 
  945   
 947          """This is called by pyglet when a mouse button is released. 
 948           
 949          @see: http://pyglet.org/doc/api/pyglet.window.Window-class.html#on_mouse_release 
 950           
 951          """ 
 952   
 953          if buttons & mouse.LEFT and self.mouse_drag: 
 954              self.mouse_drag = False 
 955              self.handle_offset(update_tiles=self.ugly_drag) 
  956   
 965   
 967          """Check, if new tiles need to be loaded because of the current offset. 
 968   
 969          If the offset is bigger than one tile, the appropriate tile 
 970          will become the new center and the offset is changed accordingly. 
 971   
 972          @param update_tiles: If True, self.update_tiles() will be called after adjusting for offset (default). 
 973   
 974          """ 
 975           
 976           
 977           
 978          self.center_x -= int(self.offset_x / float(TILE_SIZE)) 
 979          self.offset_x = (self.offset_x % TILE_SIZE if self.offset_x >= 0 
 980                           else -(-self.offset_x % TILE_SIZE)) 
 981          self.center_y += int(self.offset_y / float(TILE_SIZE)) 
 982          self.offset_y = (self.offset_y % TILE_SIZE if self.offset_y >= 0 
 983                           else -(-self.offset_y % TILE_SIZE)) 
 984          self.center_lat, self.center_lon = tiles.xy2latlon(self.center_x, self.center_y, self.zoom) 
 985          if update_tiles: 
 986              self.update_tiles() 
  987   
 989          """Remove a drawing-object from the viewer. 
 990   
 991          The object is specified by it's type (in hexadecimal, see MESSAGE_TYPES) and it's unique id. 
 992           
 993          @param dt: Necessary for scheduling, ignored here 
 994          @type dt: int 
 995          @param type: Specifies which kind of object should be removed. Must be one of the types in MESSAGE_TYPES (in hexadecimal). 
 996          @type type: string 
 997          @param id: ID of the removed object 
 998          @type id: int 
 999           
1000          """ 
1001   
1002          id = id + ID_TYPE_PREFIX[type] 
1003          type = MESSAGE_TYPES[type] 
1004   
1005          if type == 'point' and id in self.points: 
1006              (lat, lon, rad, r, g, b, a, ttl) = self.points[id] 
1007              del self.points[id] 
1008              if rad > 0: 
1009                  del self.quad_coords[id] 
1010                  del self.quad_colors[id] 
1011              else: 
1012                  del self.point_coords[id] 
1013                  del self.point_colors[id] 
1014               
1015               
1016              self.point_vertex_list.delete() 
1017              self.point_vertex_list = self.drawing_batch.add(1, gl.GL_POINTS, None,  
1018                                                              ('v2i', (0, 0)), 
1019                                                             ('c4d', (0, 0, 0, 0))) 
1020          elif type == 'rectangle' and id in self.rectangles: 
1021              (minlat, minlon, maxlat, maxlon, line_width, filled, r, g, b, a, ttl) = self.rectangles[id] 
1022              del self.rectangles[id] 
1023              if filled: 
1024                  del self.quad_coords[id] 
1025                  del self.quad_colors[id] 
1026              else: 
1027                  del self.line_loop_coords[id] 
1028                  del self.line_loop_colors[id] 
1029                  del self.line_loop_vertex_lists[id] 
1030          elif type == 'circle' and id in self.circles: 
1031              (lat, lon, rad, filled, r, g, b, a, ttl) = self.circles[id] 
1032              del self.circles[id] 
1033              if filled: 
1034                  del self.polygon_coords[id] 
1035                  del self.polygon_colors[id] 
1036                  del self.polygon_vertex_lists[id] 
1037              else: 
1038                  del self.point_coords[id] 
1039                  del self.point_colors[id] 
1040                  self.point_coords_offset = [] 
1041                  self.point_colors_all = [] 
1042               
1043               
1044              self.point_vertex_list.delete() 
1045              self.point_vertex_list = self.drawing_batch.add(1, gl.GL_POINTS, None,  
1046                                                              ('v2i', (0, 0)), 
1047                                                             ('c4d', (0, 0, 0, 0))) 
1048          if type == 'triangle' and id in self.triangles: 
1049              (lat1, lon1, lat2, lon2, lat3, lon3, filled, r, g, b, a, ttl) = self.triangles[id] 
1050              del self.triangles[id] 
1051              if filled: 
1052                  del self.triangle_coords[id] 
1053                  del self.triangle_colors[id] 
1054                  self.triangle_vertex_list.delete() 
1055                  self.triangle_vertex_list = self.drawing_batch.add(1, gl.GL_POINTS, None, 
1056                                                                     ('v2i', (0, 0)), 
1057                                                                     ('c4d', (0, 0, 0, 0))) 
1058              else: 
1059                  del self.line_loop_coords[id] 
1060                  del self.line_loop_colors[id] 
1061                  del self.line_loop_vertex_lists[id] 
1062          elif type == 'text' and id in self.text_objects: 
1063              del self.text_data[id] 
1064              del self.text_objects[id] 
1065               
1066              return 
1067          elif type == 'direct-text' and id in self.direct_text_objects: 
1068              del self.direct_text_objects[id] 
1069              return 
1070   
1071          self.update_vertex_lists() 
 1072   
1074          """Empty all vertex lists and set up new ones.""" 
1075          self.points = {} 
1076          self.rectangles = {} 
1077          self.circles = {} 
1078   
1079          self.point_coords = {} 
1080          self.point_coords_offset = [] 
1081          self.point_colors = {} 
1082          self.point_colors_all = [] 
1083          self.point_vertex_list.delete() 
1084          self.point_vertex_list = self.drawing_batch.add(1, gl.GL_POINTS, None,  
1085                                                          ('v2i/stream', (0, 0)), 
1086                                                         ('c4d/stream', (0, 0, 0, 0))) 
1087   
1088          self.quad_coords = {} 
1089          self.quad_coords_offset = [] 
1090          self.quad_colors = {} 
1091          self.quad_colors_all = [] 
1092          self.quad_vertex_list.delete() 
1093          self.quad_vertex_list = self.drawing_batch.add(1, gl.GL_QUADS, None, 
1094                                                         ('v2i/stream', (0, 0)), 
1095                                                         ('c4d/stream', (0, 0, 0, 0))) 
1096          self.line_loop_coords = {} 
1097          self.line_loop_colors = {} 
1098          self.line_loop_vertex_lists = {} 
1099          self.polygon_coords = {} 
1100          self.polygon_colors = {} 
1101          self.polygon_vertex_lists = {} 
1102   
1103          self.heatmap_data = [] 
1104          self.heatmap_point_coords = [] 
1105          self.heatmap_point_coords_offset = [] 
1106          self.heatmap_point_colors = [] 
1107          self.heatmap_point_list.delete() 
1108          self.heatmap_point_list = self.heatmap_batch.add(1, gl.GL_POINTS, None,  
1109                                                          ('v2i/stream', (0, 0)), 
1110                                                          ('c4d/stream', (0, 0, 0, 0))) 
1111          self.heatmap_quad_coords = [] 
1112          self.heatmap_quad_coords_offset = [] 
1113          self.heatmap_quad_colors = [] 
1114          self.heatmap_quad_list.delete() 
1115          self.heatmap_quad_list = self.heatmap_batch.add(1, gl.GL_QUADS, None,  
1116                                                          ('v2i/stream', (0, 0)), 
1117                                                          ('c4d/stream', (0, 0, 0, 0))) 
 1118   
1120          """Try to establish a new connection to the host and ports used when initialising. 
1121   
1122          This is mainly used when a new simulation was started and the viewer is supposed to be restarted. 
1123           
1124          """ 
1125   
1126          self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
1127          self.socket.setblocking(0) 
1128          try: 
1129              print 'connecting' 
1130              self.socket.connect((self.host, self.port)) 
1131              print 'connected' 
1132          except socket.error as (errno, message): 
1133              if errno == 115: 
1134                   
1135                   
1136                   
1137                  pass 
1138              else: 
1139                  print 'error while connecting' 
1140                  print '\t', errno, message 
1141                  raise socket.error, (errno, message) 
 1142   
1144          """This is called by pyglet when the viewer is closed. 
1145   
1146          The screenshot folder is deleted, if no screenshots were taken. 
1147           
1148          """ 
1149           
1150          super(SimViewer, self).close() 
1151          try: 
1152              os.rmdir(os.path.join(self.screenshot_dir, self.current_sc_dir)) 
1153          except OSError: 
1154               
1155              pass 
1156          try: 
1157              os.rmdir(self.screenshot_dir) 
1158          except OSError: 
1159               
1160              pass 
  1161   
1162   
1163  if __name__ == '__main__': 
1164      viewer = SimViewer(52.382463, 9.717836, width=800, height=600, resizable=True, caption='MoSP-Simulation Viewer') 
1165      pyglet.app.run() 
1166