#!/usr/bin/env python """ Export block geometries from the database (PostGIS) to Keyhole Markup Language (KML). The exported polygons will be assigned a color based upon their blocks' average population densities. Output is written to stdout. """ import os import pgdb import site import sys # Basically, add '../src' to our path. # Needed for the imports that follow. site.addsitedir(os.path.dirname(os.path.abspath(sys.argv[0])) + '/../src') import CLI import Configuration.Defaults import ExitCodes import GPS import SummaryFile1 import KML """ Parse the command line options. All of these are optional; defaults are provided for the database information and the output is written to stdout. """ parser = CLI.default_option_parser() # Use this module's docstring as the description. parser.description = __doc__ (options, args) = parser.parse_args() def GenerateRedStyles(alpha='7f'): """ Generate 256 styles (0-255), each corresponding to a shade of red. The RGB values in KML are represented as aabbggrr, where each of 'aa','bb','gg','rr' is a hexadecimal value between 00-ff such that, aa -> Alpha Component bb -> Blue Component gg -> Green Component rr -> Red Component """ styles = [] for rgb_value in range(0, 256): hex_value = "%s0000%02x" % (alpha, rgb_value) s = KML.Style(initial_id=(hex_value)) poly_style = KML.PolyStyle() color = KML.Color(hex_value) poly_style.children.append(color) s.children.append(poly_style) styles.append(s) return styles conn = pgdb.connect(host=options.host, database=options.database, user=options.username) # We'll use this cursor for all of our queries. cursor = conn.cursor() # Here we calculate a bunch of magic parameters. If we try to use a # linear gradient based on the population density, it turns out that # everyone gets assigned the color for "zero." So, we need to massage # the numbers a little bit in order to get some color in our polygons. avg_avg_pop_query = """ SELECT AVG(population_density) FROM blocks WHERE population_density > 0.005; """ cursor.execute(avg_avg_pop_query) rows = cursor.fetchall() avg_avg_population = float(rows[0][0]) stddev_avg_pop_query = """ SELECT stddev_samp(population_density) FROM blocks WHERE population_density > 0.005; """ cursor.execute(stddev_avg_pop_query) rows = cursor.fetchall() stddev_avg_population = float(rows[0][0]) # These parameters were carefully guessed. floor_avg_population = avg_avg_population - (0.5 * stddev_avg_population) ceil_avg_population = avg_avg_population + (2*stddev_avg_population) # We always want to include these styles (defining all 256 colors) in # our document. doc = KML.Document() red_styles = GenerateRedStyles() for style in red_styles: doc.styles.append(style) query = """ SELECT blkidfp00, population_density, AsKML(the_geom) as geom FROM blocks; """ cursor.execute(query) rows = cursor.fetchall() for row in rows: placemark = KML.Placemark() name = KML.Name(row[0]) placemark.children.append(name) avg_pop = float(row[1]) # If the average population is outside of the limits that I have # decreed acceptable, set its value back to the max/min # appropriately. if (avg_pop < floor_avg_population): avg_pop = floor_avg_population if (avg_pop > ceil_avg_population): avg_pop = ceil_avg_population # We can calculate the color as a percentage of "completely red," # which is #ff000ff in hex, or (255, 0, 0, 0) in RGB. We start # with RGB, and then convert it to hex. # # We rely on the KML file containing 256 different styles, each # named after the RGB (Hex) representation of its PolyStyle # color. # red_base10 = (avg_pop / (3*stddev_avg_population)) * 255.0 red_hex = "#7f0000%02x" % red_base10 style = KML.StyleUrl(red_hex) placemark.children.append(style) multigeometry = KML.RawText(row[2]) placemark.children.append(multigeometry) doc.children.append(placemark) doc.print_kml() conn.close()