Visualizing the Factbook: Difference between revisions

From XPUB & Lens-Based wiki
Line 9: Line 9:
We can use SQL to help:
We can use SQL to help:


<source>
<source lang="mysql">
mysql> select MIN(latitude), MAX(latitude) from countries;
mysql> select MIN(latitude), MAX(latitude) from countries;


Line 20: Line 20:
</source>
</source>


<source>
<source lang="mysql">
mysql> select MIN(longitude), MAX(longitude) from countries;
mysql> select MIN(longitude), MAX(longitude) from countries;
+------------------+----------------+
+------------------+----------------+

Revision as of 16:51, 9 December 2008

Visualizing the Factbook

A simple map

You can make a simple visualisation of the country names using each countries geographic coordinates. Make sure you've patched the factbook data to include latitude and longitude. AttachFile&do=get&target=geocodes.tar.gz geocodes patch

Start by considering what range of values the input data has (here latitude and longitude) has, and the range of the output values (pixel positions to place the text with absolute positioning CSS).

We can use SQL to help:

mysql> select MIN(latitude), MAX(latitude) from countries;

+---------------+---------------+
| MIN(latitude) | MAX(latitude) |
+---------------+---------------+
|           -90 |            90 | 
+---------------+---------------+
1 row in set (0.00 sec)
mysql> select MIN(longitude), MAX(longitude) from countries;
+------------------+----------------+
| MIN(longitude)   | MAX(longitude) |
+------------------+----------------+
| -176.19999694824 |            178 | 
+------------------+----------------+
1 row in set (0.00 sec)

So, looking at these values, it seems first of all that any notion of "north" and "south", or "east" and "west" have been represented using negative and positive numbers. This makes things easy, as you can simply add the value to some "center" position. For instance, you could try:

#!/usr/bin/python

from settings import *
from [[MySQLdb]].cursors import [[DictCursor]]

conn = connect_to_db()
cursor = conn.cursor([[DictCursor]])
cursor.execute("SELECT * F ROM countries ORDER BY name")

print "Content-type: text/html"
print

print """&lt;html&gt;
&lt;head&gt;
&lt;style lang="text/css"&gt;
a { text-decoration: none }
a:hover	{ background: black; color: white }
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;"""

centerx = 400
centery = 200

print "&lt;h1&gt;map&lt;/h1&gt;"
print """&lt;div style="position: relative"&gt;"""
while 1:
	row = cursor.fetchone()
	if (row == None): break
	clat = row['latitude']
	clong = row['longitude']
	
	if (clat and clong):
		cx = int(centerx + (clong * 2))
		cy = int(centery - (clat * 2))
		
		print """&lt;div style="position: absolute; left: %dpx; top: %dpx"&gt;""" % (cx, cy)
		print """&lt;a href="country.cgi?id=%s"&gt;""" % (row['id'])
		print row['name']
		print """&lt;/a&gt;"""
		print """&lt;/div&gt;"""

print "&lt;/div&gt;"
print "&lt;/body&gt;"
print "&lt;/html&gt;"

attachment:factbook_map.png online example

Visualizing an aspect of the data

A simple form of data visualization is simply to translate (or map) one type of data into a visual aspect. For instance, you could translate each countries population into font size, with larger text used to represent larger populations.

A general purpose function is shown below to translate an "input" value from one range (domain) to another. The function performs a linear transformation, meaning that it maps low values to high values in one scale to low values to high values in another.

#!python numbers=off
def translatescales (i, input_low, input_high, output_low, output_high):
	s = (float(i) - input_low) / (input_high - input_low)
	return output_low + (s * (output_high - output_low))

For example, to visualize population,you need to know the range of populations values in the database. These are the variables input_low and input_high. You also need to specify the range of possible "output" values, in this case the CSS font-size values (here shown with pixels as units).

For the input scale, you can once again use MySQL to do the work of finding the minimum and maximum populations:

mysql> select MIN(population), MAX(population) from countries;
+-----------------+-----------------+
| MIN(population) | MAX(population) |
+-----------------+-----------------+
|            0.00 |   1298847624.00 |
+-----------------+-----------------+
1 row in set (0.12 sec)

Below, is a complete script for translating country population into font size. Notice how the above "range check" is done in the script so that range is set dynamically -- in this way if the database were to change, the visualisation would be adjusted automatically.

#!python numbers=off
#!/usr/bin/python

from settings import *
from [[MySQLdb]].cursors import [[DictCursor]]

minfontsize = 8 # (pixels)
maxfontsize = 40

def translatescales (i, input_low, input_high, output_low, output_high):
	s = (float(i) - input_low) / (input_high - input_low)
	return output_low + (s * (output_high - output_low))

conn = connect_to_db()

cursor = conn.cursor()
cursor.execute("SELECT MIN(population), MAX(population) F ROM countries")
row = cursor.fetchone()
minpopulation = int(row[0])
maxpopulation = int(row[1])
cursor.close()

print "Content-type: text/html"
print

print """<html>
<head>
<title>map</title>
<style lang="text/css">
a		{ text-decoration: none }
a:hover	{ background: black; color: white }
p { margin: 0px }
</style>
</head>
<body>"""

centerx = 400
centery = 200

print """<p><b>factbook map</b></p>"""
print """<p>translating population to font size</p>"""
print """<p>(population range: %d - %d)</p>""" % (minpopulation, maxpopulation)
print """<div style="position: relative">"""

cursor = conn.cursor([[DictCursor]])
cursor.execute("SELECT id, name, latitude, longitude, population F ROM countries WHERE population IS NOT NULL ORDER BY name")
while 1:
	row = cursor.fetchone()
	if (row == None): break
	clat = row['latitude']
	clong = row['longitude']
	
	if (clat and clong):
		cx = centerx +  (2 * clong)
		cy = centery - (2 * clat)
		textsize = translatescales(row['population'], minpopulation, maxpopulation, minfontsize, maxfontsize)
		# textsize = 40 - (abs(clat) * .5)
		
		print """<div style="font-size: %dpx; position: absolute; left: %dpx; top: %dpx">""" % (textsize, cx, cy)
		# print """<a href="country_boxes.cgi?id=%s">""" % (row['id'])
		print row['name']
		# print """</a>"""
		print """</div>"""

	# print """<li><a href="country_boxes.cgi?id=%s">%s</a></li>""" % (row[0], row[1])

print "</div>"
print "</body>"
print "</html>"

conn.close()

attachment:countries_population.png

http://pzwart2.wdka.hro.nl/~techday/cgi-bin/factbook/countries_population.cgi

other possibilities

attachment:deathmap.png

Annemieke's "Deathmap" http://pzwart2.wdka.hro.nl/~techday/cgi-bin/factbook/deathmap.cgi

  • ["Factbook Map Table"]