Index: src/django_gheat/gheat/models.py
===================================================================
--- src/django_gheat/gheat/models.py	(revision 9183)
+++ src/django_gheat/gheat/models.py	(revision 9184)
@@ -1,5 +1,55 @@
-from django.db import models
 from gheat import managers
 import datetime
+import binascii
+import hashlib
+
+from django.core import validators
+from django.db import models
+from django.dispatch import receiver
+from django.utils.encoding import smart_unicode
+from django.utils.translation import ugettext_lazy as _
+
+class BinaryField(models.Field):
+  MAGIC = '###MAGIC###'
+  description = _('Binary object using base64 (up to %(max_length)s)')
+
+  __metaclass__ = models.SubfieldBase
+
+  def __init__(self, *args, **kwargs):
+    # base64 roughly max the binary size with with 4 times
+    kwargs['max_length'] = kwargs['max_length'] * 4 + len(self.MAGIC)
+    super(BinaryField, self).__init__(*args, **kwargs)
+    self.validators.append(validators.MaxLengthValidator(self.max_length))
+
+  def to_python(self,value):
+    if value.startswith(self.MAGIC):
+      return binascii.a2b_base64(value[len(self.MAGIC):])
+    else:
+      return value
+
+  def get_db_prep_value(self, value, connection, prepared=False):
+    target = self.MAGIC + binascii.b2a_base64(value)
+    if len(target) > self.max_length:
+      raise ValueError(len(target))
+    return target
+
+  def get_prep_lookup(self, lookup_type, value):
+    raise TypeError('Lookup type %r not supported.' % lookup_type)
+
+  def get_internal_type(self):
+    return 'TextField'
+
+class TileCache(models.Model):
+  key = models.CharField(max_length=34,unique=True)
+  data = BinaryField(max_length=10000)
+  creation_time = models.DateTimeField(auto_now_add=True)
+
+  def __unicode__(self):
+    return self.key
+
+  @staticmethod
+  def make_key(source):
+    return hashlib.md5(source).hexdigest()
+
 
 class Accespoint(models.Model):
Index: src/django_gheat/website/tile.py
===================================================================
--- src/django_gheat/website/tile.py	(revision 9183)
+++ src/django_gheat/website/tile.py	(revision 9184)
@@ -12,4 +12,5 @@
 import sys
 import tempfile
+import time
 
 # Rending with PIL and computation with numpy has proven to be to slow to be
@@ -22,5 +23,5 @@
   pass
 
-logging.basicConfig(level=logging.WARNING)
+logging.basicConfig(level=logging.DEBUG)
 log = logging.getLogger('tile')
 
@@ -44,4 +45,10 @@
     f.seek(0)
     fh.write(f.read())
+
+  def get_image(self,format='png'):
+    f = tempfile.NamedTemporaryFile(suffix=format)
+    pygame.image.save(self.surf,f.name)
+    f.seek(0)
+    return f.read()
 
 
@@ -222,5 +229,4 @@
     xcoord = int(dif(nw_deg.lon,meting.longitude) / (resolution_deg.lon))
     ycoord = int(dif(nw_deg.lat,meting.latitude) / (resolution_deg.lat))
-    log.info(meting.accespoint.ssid, meting.latitude, meting.longitude, xcoord, ycoord)
 
     # TODO: Please note that this 'logic' technically does apply to WiFi signals,
@@ -238,4 +244,5 @@
     signal_normalized = MAX_RANGE - (MAX_SIGNAL - meting.signaal)
     im.add_circle((xcoord,ycoord),float(signal_normalized) / meters_per_pixel,colour, MAX_SIGNAL - meting.signaal)
+    #im.add_point((xcoord,ycoord),float(signal_normalized) / meters_per_pixel,colour, MAX_SIGNAL - meting.signaal)
   
   log.info("BoundingBox NW: %s" % nw_deg)
@@ -253,13 +260,25 @@
 # Create your views here.
 def serve_tile(request,zoom,x,y):
-  filter = {}
-  colour = (255,0,0)
-  for key, value in request.GET.iteritems():
-    if key == 'colour':
-      colour = tuple(map(int,value.split(',')))
-    else:
-      filter[key] = value
-  im = make_tile(int(x),int(y),int(zoom),filter=filter,colour=colour)
+  hash_key = TileCache.make_key("%s %s %s %s" % (zoom, x ,y, request.GET.urlencode()))
+  try:
+    d = TileCache.objects.get(key=hash_key)
+    data = d.data
+  except TileCache.DoesNotExist:
+    #log.setLevel(logging.DEBUG)
+    filter = {}
+    colour = (255,0,0)
+    for key, value in request.GET.iteritems():
+      if key == 'colour':
+        colour = tuple(map(int,value.split(',')))
+      else:
+        filter[key] = value
+    now = time.time()
+    im = make_tile(int(x),int(y),int(zoom),filter=filter,colour=colour)
+    log.info("Processing time: %s" % (time.time() - now))
+    data = im.get_image('png')
+    r = TileCache.objects.create(key=hash_key,data=data)
+
   response = HttpResponse(mimetype="image/png")
-  im.write(response,'png')
+  response.write(data)
   return response
+
