Skip to content

Instantly share code, notes, and snippets.

@andrewxhill
Last active June 25, 2016 13:57
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrewxhill/f5b86469705d484bb4a46c833959a59c to your computer and use it in GitHub Desktop.
Save andrewxhill/f5b86469705d484bb4a46c833959a59c to your computer and use it in GitHub Desktop.
Create small multiples through the Static Maps API
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import sys\n",
"import urllib\n",
"import urllib2\n",
"import json\n",
"import requests\n",
"\n",
"basemap = {\n",
" \"type\": \"http\",\n",
" \"options\": {\n",
" \"urlTemplate\": \"http://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png\"\n",
" , \"subdomains\": [\"a\", \"b\", \"c\"]\n",
" } \n",
"}\n",
"\n",
"\n",
"def getMapConfig(viz_json, sql=None, target=None): \n",
" r = requests.get(viz_json)\n",
" layers = []\n",
" assert r.status_code == 200\n",
" data = r.json()\n",
" bounds = data['bounds'] \n",
" center = json.loads(data['center'])\n",
"\n",
" zoom = data['zoom']\n",
" clayer = 0\n",
" for layer in data['layers']:\n",
" \n",
" if layer['type'] == 'tiled':\n",
" newlayer = {\n",
" \"type\": \"http\",\n",
" \"options\": {\n",
" \"urlTemplate\": layer['options']['urlTemplate']\n",
" , \"subdomains\": list(layer['options']['subdomains'])\n",
" } \n",
" }\n",
" layers.append(newlayer)\n",
" clayer+=1\n",
" elif layer['type'] == 'layergroup':\n",
" # print layer\n",
" cur_data = None\n",
" checkedLayers = 0\n",
" #f.write(\",\"+str(len(layer['options']['layer_definition']['layers'])))\n",
" for la in layer['options']['layer_definition']['layers']:\n",
" if la['visible']==True:\n",
" if target==clayer:\n",
" la['options']['sql'] = sql\n",
" layers.append({'type': 'mapnik', 'options': la['options']})\n",
" \n",
" clayer+=1\n",
" return layers, center, bounds, zoom\n",
"\n",
"\n",
"def getNamedMap(username, api_key, map_config):\n",
" config = {\n",
" \"version\": \"1.3.0\",\n",
" \"layers\": map_config\n",
" }\n",
" r = requests.get('https://'+username+'.cartodb.com/api/v1/map',\n",
" headers={'content-type':'application/json'},\n",
" params={'config': json.dumps(config)}\n",
" )\n",
" return r.json()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Get the default map as a static png"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"https://andrew.cartodb.com/api/v1/map/static/center/ca91e89e2abe4b43c1aeeb5e089c8810:1413466141591/4/38.8225909762/-104.194335937/1600/800.png\n"
]
}
],
"source": [
"# Example to just get a complete layer\n",
"username='andrew'\n",
"api_key='{{cartodb api key}}'\n",
"vj = 'https://team.cartodb.com/u/andrew/api/v2/viz/00e22baa-3da6-11e4-89ee-0e230854a1cb/viz.json'\n",
"\n",
"# Get config from API\n",
"mapconfig,center, bounds, zoom = getMapConfig(vj)\n",
"\n",
"# Get named map\n",
"namedmap = getNamedMap(username, api_key, mapconfig)\n",
" \n",
"# Print final URL\n",
"print 'https://'+username+'.cartodb.com/api/v1/map/static/center/'+namedmap['layergroupid']+'/'+str(zoom)+'/'+str(center[0])+'/'+str(center[1])+'/1600/800.png'\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, get the same map but apply a custom filter to the target layer (I only have a basemap plus one layer, so the target layer for the sql is target=1)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"https://andrew.cartodb.com/api/v1/map/static/center/40b07ced85e1082ca1fd535c68e0a0d1:1413466141591/4/38.8225909762/-104.194335937/1600/800.png\n"
]
}
],
"source": [
"# filter by column\n",
"sql = \"select * from usdm_september_2014 where dm=1\"\n",
"\n",
"# Get config from API\n",
"mapconfig,center, bounds, zoom = getMapConfig(vj, sql=sql, target=1)\n",
"\n",
"# Get named map\n",
"namedmap = getNamedMap(username, api_key, mapconfig)\n",
" \n",
"# Print final URL\n",
"print 'https://'+username+'.cartodb.com/api/v1/map/static/center/'+namedmap['layergroupid']+'/'+str(zoom)+'/'+str(center[0])+'/'+str(center[1])+'/1600/800.png'\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next, we'll supply a list of custom sql statements. You could also generate these filters programatically by reading what the possible values in the dm column is. You would do that by just asking the SQL API first and then build the list from the results."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"5\n"
]
}
],
"source": [
"sqls = [\n",
" \"select * from usdm_september_2014 where dm=0\",\n",
" \"select * from usdm_september_2014 where dm=1\",\n",
" \"select * from usdm_september_2014 where dm=2\",\n",
" \"select * from usdm_september_2014 where dm=3\",\n",
" \"select * from usdm_september_2014 where dm=4\"\n",
"]\n",
"urls = []\n",
"for sql in sqls:\n",
" # Get config from API\n",
" mapconfig,center, bounds, zoom = getMapConfig(vj, sql=sql, target=1)\n",
" # Get named map\n",
" namedmap = getNamedMap(username, api_key, mapconfig)\n",
" urls.append('https://'+username+'.cartodb.com/api/v1/map/static/center/'+namedmap['layergroupid']+'/'+str(zoom)+'/'+str(center[0])+'/'+str(center[1])+'/400/300.png'\n",
")\n",
"print len(urls)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We'll use PIL to stitch our 5 images together into 1"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import urllib, cStringIO\n",
"from PIL import Image, ImageOps\n",
"\n",
"url1 = urls.pop(0)\n",
"file1 = cStringIO.StringIO(urllib.urlopen(url1).read())\n",
"image1 = ImageOps.expand(Image.open(file1),border=10,fill='white')\n",
"\n",
"for url2 in urls:\n",
" file2 = cStringIO.StringIO(urllib.urlopen(url2).read())\n",
"\n",
" image2 = ImageOps.expand(Image.open(file2),border=10,fill='white')\n",
"\n",
" (width1, height1) = image1.size\n",
" (width2, height2) = image2.size\n",
"\n",
" result_width = width1 + width2\n",
" result_height = max(height1, height2)\n",
"\n",
" result = Image.new('RGB', (result_width, result_height))\n",
" result.paste(im=image1, box=(0, 0))\n",
" result.paste(im=image2, box=(width1, 0))\n",
"\n",
" image1 = result\n",
"image1.save('test-small-multiple.png')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here is the result http://i.imgur.com/JL44Fvp.png"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.11"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment