Permalink
Please sign in to comment.
Browse files
Add in a dotcloud-based thumbnailing service; most of this code is ea…
…sily reusable, but it's specifically all you need to get set up with dotcloud.
- Loading branch information...
Showing
with
148 additions
and 0 deletions.
@@ -0,0 +1,19 @@ | ||
+if [ ! -f $HOME/env/bin/curl-config ]; then | ||
+ wget -c http://curl.haxx.se/download/curl-7.21.6.tar.gz | ||
+ tar -zvxf curl-7.21.6.tar.gz | ||
+ cd curl-7.21.6 | ||
+ ./configure --prefix=$HOME --bindir=$HOME/env/bin | ||
+ make | ||
+ make install | ||
+ cd .. | ||
+fi | ||
+ | ||
+if [ ! -f $HOME/env/bin/gdal-config ]; then | ||
+ wget -c http://download.osgeo.org/gdal/gdal-1.8.0.tar.gz | ||
+ tar -zvxf gdal-1.8.0.tar.gz | ||
+ cd gdal-1.8.0 | ||
+ LIBS="`curl-config --libs`" ./configure --prefix=$HOME --bindir=$HOME/env/bin --with-curl --without-libtool --with-python | ||
+ make | ||
+ make install | ||
+ cd .. | ||
+fi |
@@ -0,0 +1,2 @@ | ||
+Pil | ||
+redis |
@@ -0,0 +1,5 @@ | ||
+# the following information is available via 'dotcloud info yourhost.redis' | ||
+redis_host="redis.yourhost.dotcloud.com" | ||
+redis_port=None # port | ||
+redis_password=None # password | ||
+ |
@@ -0,0 +1,9 @@ | ||
+[program:uwsgi] | ||
+directory=/home/dotcloud/current | ||
+command=/usr/bin/uwsgi --pidfile /var/dotcloud/uwsgi.pid -s /var/dotcloud/uwsgi.sock --chmod-socket=660 --master --processes 1 --home /home/dotcloud/env --pythonpath /home/dotcloud/current --disable-logging | ||
+autostart=true | ||
+autorestart=true | ||
+stdout_logfile=/var/log/supervisor/uwsgi.log | ||
+stderr_logfile=/var/log/supervisor/uwsgi.log | ||
+stopsignal=INT | ||
+environment=LD_LIBRARY_PATH="/home/dotcloud/lib" |
@@ -0,0 +1,113 @@ | ||
+import os | ||
+import urllib | ||
+import json | ||
+import gdal | ||
+import Image | ||
+import gdal | ||
+import math | ||
+from StringIO import StringIO | ||
+import cgi | ||
+import redis | ||
+import settings | ||
+ | ||
+OAM_HOST = "oam.osgeo.org" | ||
+ | ||
+# The following two functions were provided by | ||
+# Michael Migurski, as part of the OpenAerialMap | ||
+# server code. | ||
+ | ||
+def image_size_small(im_width, im_height, size): | ||
+ """ Determine a good thumbnail size based on desired named size. | ||
+ """ | ||
+ aspect = float(im_width) / float(im_height) | ||
+ | ||
+ if size == 'thumbnail': | ||
+ if aspect > 1.6: | ||
+ return 440, int(440 / aspect) | ||
+ elif aspect < .3: | ||
+ return 98, int(98 / aspect) | ||
+ else: | ||
+ return 196, int(196 / aspect) | ||
+ | ||
+ elif size == 'preview': | ||
+ area = 320 * 320 | ||
+ elif size == 'large': | ||
+ area = 640 * 640 | ||
+ else: | ||
+ raise Exception('Don\'t know about "%s"' % size) | ||
+ | ||
+ th_height = int(math.sqrt(area / aspect)) | ||
+ th_width = int(aspect * th_height) | ||
+ | ||
+ return th_width, th_height | ||
+ | ||
+def image_made_smaller(im, size="preview"): | ||
+ """ | ||
+ """ | ||
+ os.environ['GDAL_DISABLE_READDIR_ON_OPEN'] = 'YES' | ||
+ #environ['CPL_DEBUG'] = 'On' | ||
+ | ||
+ ds = gdal.Open('/vsicurl/' + im) | ||
+ th_width, th_height = image_size_small(ds.RasterXSize, ds.RasterYSize, size) | ||
+ | ||
+ # | ||
+ # Extract the best-sized overview from the source image | ||
+ # | ||
+ interps = dict([(getattr(gdal, gci), gci[4:-4]) for gci in dir(gdal) if gci.startswith('GCI')]) | ||
+ bands = dict([(interps[ds.GetRasterBand(i).GetColorInterpretation()], ds.GetRasterBand(i)) for i in range(1, 1 + ds.RasterCount)]) | ||
+ chans = [] | ||
+ | ||
+ for (chan, interp) in enumerate(('Red', 'Green', 'Blue')): | ||
+ assert interp in bands, '%s missing from bands - bad news.' % interp | ||
+ | ||
+ band = bands[interp] | ||
+ overviews = [band.GetOverview(i) for i in range(band.GetOverviewCount())] | ||
+ overviews = [(ov.XSize, ov.YSize, ov) for ov in overviews] | ||
+ | ||
+ for (ov_width, ov_height, overview) in sorted(overviews): | ||
+ if ov_width > th_width: | ||
+ data = overview.ReadRaster(0, 0, ov_width, ov_height) | ||
+ chan = Image.fromstring('L', (ov_width, ov_height), data) | ||
+ chan = chan.resize((th_width, th_height), Image.ANTIALIAS) | ||
+ | ||
+ chans.append(chan) | ||
+ break | ||
+ | ||
+ # | ||
+ # Return an image | ||
+ # | ||
+ thumb = Image.merge('RGB', chans) | ||
+ | ||
+ return thumb | ||
+ | ||
+ | ||
+def application(environ, start_response): | ||
+ red = redis.Redis(host=settings.redis_host, port=settings.redis_port, db=0, password=settings.redis_password) | ||
+ form = cgi.FieldStorage(fp=environ['wsgi.input'], | ||
+ environ=environ) | ||
+ if 'id' in form: | ||
+ id = form["id"].value | ||
+ if 'size' in form: | ||
+ size = form['size'].value | ||
+ else: | ||
+ size = 'thumbnail' | ||
+ output = red.get("%s-%s" % (id, size)) | ||
+ if not output: | ||
+ image = urllib.urlopen("http://%s/api/image/%s/" % (OAM_HOST, id)) | ||
+ data = json.loads(image.read()) | ||
+ preview = image_made_smaller(data['url'], size) | ||
+ buffer = StringIO() | ||
+ preview.save(buffer, 'JPEG') | ||
+ output = buffer.getvalue() | ||
+ red.set("%s-%s" % (id, size), output) | ||
+ status = '200 OK' | ||
+ response_headers = [('Content-type', 'image/jpeg'), | ||
+ ('Content-Length', str(len(output)))] | ||
+ start_response(status, response_headers) | ||
+ else: | ||
+ output = "OAM Thumbnailing code." | ||
+ response_headers = [('Content-type', 'text/plain'), | ||
+ ('Content-Length', str(len(output)))] | ||
+ start_response(status, response_headers) | ||
+ | ||
+ return [output] |
0 comments on commit
810348b