]> gitweb.michael.orlitzky.com - dead/census-tools.git/blobdiff - src/SummaryFile1.py
Added the linear program solving the midatlantic region.
[dead/census-tools.git] / src / SummaryFile1.py
index 50f23ad0c96a83227137d60c7ff0fd2a4057efe8..75868673aacd02b72482e506aaa665774e68466c 100644 (file)
@@ -1,7 +1,8 @@
-import os, GPS, inspect
+import os
 
-class RecordError(StandardError):
-    pass
+from Errors import RecordError
+import GPS
+import StringUtils
 
 
 class GeoRecord:
@@ -9,37 +10,86 @@ class GeoRecord:
     This class wraps one record in an SF1 geo file.
     """
 
-    MinimumLineLength = 400
+    MINIMUM_LINE_LENGTH = 400
 
 
 class Block:
     """
-    Represents a block (which is a special case of a GeoRecord.
-    All we care about here is the block number, population,
-    area, and coordinates.
+    Represents a block (which is a special case of a GeoRecord).
+    There are some convenience methods tacked on to make computation
+    and querying easier.
     """
 
     def __init__(self, geo_record):
-        """We initialize from a GeoRecord object"""
+        """
+        We initialize from a GeoRecord object. It is important that
+        we raise some kind of error if there is no 'block' field, since
+        that means we weren't passed a block.
+        """
+        if not (StringUtils.is_integer(geo_record.block)):
+            raise RecordError('GeoRecord object does not represent a block.')
+
+        # These need to be stored as strings so they don't
+        # affect the block_identifier() generation.
+        self.state = geo_record.state
+        self.county = geo_record.county
+        self.tract = geo_record.tract
+        self.block = geo_record.block
+
         # All of these int/float conversions will throw a ValueError
         # if the input string cannot be converted o the specified
         # type.
-        self.block_number = int(geo_record.block)
-        self.population = int(geo_record.pop100)
-        self.area_land = float(geo_record.arealand)
-        self.area_water = float(geo_record.areawatr)
-
+        self.pop100 = int(geo_record.pop100)
+        self.arealand = float(geo_record.arealand)
+        self.areawatr = float(geo_record.areawatr)
+
+        # Both latitude and longitude are given to six digits of
+        # precision (i.e. after the decimal point). But, there are no
+        # decimal points in the intptlon/intptlat fields, so we need
+        # to add them.
+        #
+        # By default, the coordinates will be parsed as integers. For
+        # example, +12345678 will be parsed as 12345678.0. So, we need
+        # to "move" that decimal point 6 places to the left. We know
+        # how to do that.
+        #
         self.coordinates = GPS.Coordinates()
-        self.coordinates.latitude = float(geo_record.intptlat)
-        self.coordinates.longitude = float(geo_record.intptlon)
+        self.coordinates.latitude = (float(geo_record.intptlat) / (10**6))
+        self.coordinates.longitude = (float(geo_record.intptlon) / (10**6))
+
+
+    def blkidfp00(self):
+        # From the Tiger/Line shapefile documentation:
+        #
+        #   Current block identifier; a concatenation of Census 2000
+        #   state FIPS code, Census 2000 county FIPS code, Census
+        #   BLKIDFP 16 String 2000 census tract code, Census 2000
+        #   tabulation block number, and current block suffix 1.
+        #
+        return (self.state +
+                self.county +
+                self.tract +
+                self.block)
 
 
     def total_area(self):
-        return (self.area_land + self.area_water)
+        return (self.arealand + self.areawatr)
 
 
     def population_density(self):
-        return (self.population / self.total_area())
+        # There are some unusual cases where a block will have a
+        # total area of zero. It also seems that these unusual blocks
+        # do in fact posess geometries, provided in the Tiger database.
+        # Therefore, we allow them to be parsed.
+        #
+        # The choice to assign these blocks an average density of 0
+        # was arbitrary.
+        #
+        if (self.total_area() == 0):
+            return 0
+        else:
+            return (self.pop100 / self.total_area())
+
 
     
 class GeoRecordParser:
@@ -74,7 +124,11 @@ class GeoRecordParser:
             try:
                 block = Block(record)
                 blocks.append(block)
+            except RecordError:
+                # Ain't a block.
+                continue
             except ValueError:
+                # A value couldn't be converted to the appropriate type.
                 continue
             
         return blocks
@@ -88,8 +142,8 @@ class GeoRecordParser:
         allow the GeoRecord class to parse the data meaningfully and
         throw an error if something doesn't look right.
         """
-        if (len(line) < GeoRecord.MinimumLineLength):
-            raise RecordError("The input line is too short. The SF1 specification requires a line length of %d characters; this line contains only %d characters" % (GeoRecord.MinimumLineLength, len(line)))
+        if (len(line) < GeoRecord.MINIMUM_LINE_LENGTH):
+            raise RecordError("The input line is too short. The SF1 specification requires a line length of %d characters; this line contains only %d characters" % (GeoRecord.MINIMUM_LINE_LENGTH, len(line)))
         
         record = GeoRecord()
 
@@ -354,32 +408,3 @@ class GeoRecordParser:
           
         
         return record
-    
-
-
-def FindClosestBlock(blocks, target_coords):
-    """
-    Find the closest block (from within blocks) to the GPS
-    coordinates given by target_coords.
-    """
-
-    # Empty by default. Hopefully we're passed some blocks.
-    closest_block = None
-    min_distance = 999999999.0 # Don't look at me like that.
-
-    for block in blocks:
-        this_distance = GPS.CalculateDistance(target_coords, block.coordinates)
-        if (this_distance < min_distance):
-            closest_block = block
-            min_distance = this_distance
-
-    return closest_block
-
-
-
-def FindAveragePopulationDensity(coords, geo_file_path):
-    grp = GeoRecordParser()
-    blocks = grp.parse_blocks(geo_file_path)
-    closest_block = FindClosestBlock(blocks, coords)
-    
-    return closest_block.population_density()