diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..ed752d6 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,9 @@ +[MASTER] + +extension-pkg-whitelist=math + +[MESSAGES CONTROL] + +[TYPECHECK] + +good-names=i,x,y,n diff --git a/lib/helpers.py b/lib/helpers.py index beebad7..1ca2608 100644 --- a/lib/helpers.py +++ b/lib/helpers.py @@ -1,15 +1,16 @@ import math -""" -Rounds all numbers in a list of coordinates -""" def round_point( point, accuracy=8 ): - return tuple( [ round(x,accuracy) for x in point ] ) + """ + Rounds all numbers in a list of coordinates + """ + return (round(point[0], accuracy), round(point[1], accuracy)) -""" -Returns true if two segments are connected at their beginnings or ends -""" def adjacent( left, right ): + """ + Returns true if two segments are connected at their beginnings or ends + """ + left_left = round_point(left[0]) left_right = round_point(left[-1]) right_left = round_point(right[0]) @@ -20,11 +21,12 @@ def adjacent( left, right ): left_right == right_left or left_right == right_right ) -""" -Returns the combination of two segments. Might reverse one or the other -to match the adjacent point of both. -""" def glom( left, right ): + """ + Returns the combination of two segments. Might reverse one or the other + to match the adjacent point of both. + """ + left = list( left ) right = list( right ) @@ -49,13 +51,13 @@ def glom( left, right ): raise 'segments are not adjacent' -""" -Takes a list of segments, looks at the last and tries to find an adjacent -segment in the remaining. If found combines them. -Returns a list of (now combined) segments and a list of still uncombined -segments. -""" def glom_once( segments ): + """ + Takes a list of segments, looks at the last and tries to find an adjacent + segment in the remaining. If found combines them. + Returns a list of (now combined) segments and a list of still uncombined + segments. + """ if len(segments)==0: return segments @@ -78,11 +80,11 @@ def glom_once( segments ): return x, unsorted -""" -Takes a list of segments and combines as many as possible together. Returns -a list of (now combined) segments. -""" def glom_all( segments ): + """ + Takes a list of segments and combines as many as possible together. Returns + a list of (now combined) segments. + """ unsorted = segments chunks = [] @@ -97,13 +99,13 @@ def length(segment, nodelist): '''Returns the length (in feet) of a segment''' first = True distance = 0 - lat_feet = 364613 #The approximate number of feet in one degree of latitude + lat_feet = 364613 # The approximate number of feet in one degree of latitude for point in segment: _pointid, (lat, lon) = nodelist[ round_point( point ) ] if first: first = False else: - #The approximate number of feet in one degree of longitute + # The approximate number of feet in one degree of longitude lrad = math.radians(lat) lon_feet = 365527.822 * math.cos(lrad) - 306.75853 * math.cos(3 * lrad) + 0.3937 * math.cos(5 * lrad) distance += math.sqrt(((lat - previous[0])*lat_feet)**2 + ((lon - previous[1])*lon_feet)**2) @@ -112,19 +114,27 @@ def length(segment, nodelist): def check_if_integers(numbers): + """ + Returns true if all members of lists are integers. + """ for number in numbers: if not number: return False - try: int(number) - except: + try: + int(number) + except ValueError: print("Non integer address: %s" % number) return False return True def interpolation_type(this_from, this_to, other_from, other_to): + """ + Check road side (e.g. left) and other side (right) if number range is 'even' + or 'odd'. If in doubt 'all'. + """ if not check_if_integers([this_from, this_to]): - return + return None if check_if_integers([other_from, other_to]): if (int(this_from) % 2) == 0 and (int(this_to) % 2) == 0: @@ -138,6 +148,9 @@ def interpolation_type(this_from, this_to, other_from, other_to): return "all" def create_wkt_linestring(segment): + """ + Create well known text LINESTRING() + """ coord_pairs = [] for _i, point in segment: coord_pairs.append( "%f %f" % (point[1], point[0]) ) diff --git a/lib/parse.py b/lib/parse.py index b1f86f7..98bd2d0 100644 --- a/lib/parse.py +++ b/lib/parse.py @@ -8,15 +8,19 @@ try: from osgeo import ogr -except: +except ImportError: import ogr # https://www.census.gov/geo/reference/codes/cou.html # tiger_county_fips.json was generated from the following: # wget https://www2.census.gov/geo/docs/reference/codes/files/national_county.txt -# cat national_county.txt | perl -F, -naE'($F[0] ne 'AS') && $F[3] =~ s/ ((city|City|County|District|Borough|City and Borough|Municipio|Municipality|Parish|Island|Census Area)(?:, |\Z))+//; say qq( "$F[1]$F[2]": "$F[3], $F[0]",)' -json_fh = open(os.path.dirname(__file__) + "/../tiger_county_fips.json") -county_fips_data = json.load(json_fh) +# cat national_county.txt | \ +# perl -F, -naE'($F[0] ne 'AS') && $F[3] =~ s/ ((city|City|County|District|Borough|City and Borough| +# Municipio|Municipality|Parish|Island|Census Area)(?:, |\Z))+//; +# say qq( "$F[1]$F[2]": "$F[3], $F[0]",)' + +with open(os.path.dirname(__file__) + "/../tiger_county_fips.json", encoding="utf8") as json_file: + county_fips_data = json.load(json_file) def parse_shp_for_geom_and_tags(filename): # ogr.RegisterAll() @@ -70,8 +74,7 @@ def get_tags_from_feature(po_feature): if (statefp is not None) and (countyfp is not None): county_and_state = county_fips_data.get(statefp + '' + countyfp) if county_and_state: # e.g. 'Perquimans, NC' - - result = re.match('^(.+), ([A-Z][A-Z])', county_and_state) + result = re.match('^(.+), ([A-Z][A-Z])', county_and_state) tags["tiger:county"] = result[1] tags["tiger:state"] = result[2] diff --git a/lib/project.py b/lib/project.py index 7363b57..ebb7ec3 100644 --- a/lib/project.py +++ b/lib/project.py @@ -4,7 +4,7 @@ try: from osgeo import osr -except: +except ImportError: import osr # Same as the contents of the *_edges.prj files diff --git a/tiger_address_convert.py b/tiger_address_convert.py index bdac914..e3713e3 100755 --- a/tiger_address_convert.py +++ b/tiger_address_convert.py @@ -11,7 +11,6 @@ """ import sys -import re import csv import math @@ -32,8 +31,8 @@ -def addressways(waylist, nodelist, first_id): - id = first_id +def addressways(waylist, nodelist, first_way_id): + way_id = first_way_id lat_feet = 364613 #The approximate number of feet in one degree of latitude distance = float(ADDRESS_DISTANCE) csv_lines = [] @@ -43,7 +42,7 @@ def addressways(waylist, nodelist, first_id): for segment in segments: lsegment = [] rsegment = [] - lastpoint = None + lastpoint = [] # Don't pull back the ends of very short ways too much seglength = length(segment, nodelist) @@ -70,21 +69,21 @@ def addressways(waylist, nodelist, first_id): for point in segment: pointid, (lat, lon) = nodelist[ round_point( point ) ] - #The approximate number of feet in one degree of longitude + # The approximate number of feet in one degree of longitude lrad = math.radians(lat) lon_feet = 365527.822 * math.cos(lrad) - 306.75853 * math.cos(3 * lrad) + 0.3937 * math.cos(5 * lrad) - #Calculate the points of the offset ways - if lastpoint is not None: - #Skip points too close to start + # Calculate the points of the offset ways + if lastpoint: + # Skip points too close to start if math.sqrt((lat * lat_feet - firstpoint[0] * lat_feet)**2 + (lon * lon_feet - firstpoint[1] * lon_feet)**2) < pullback: #Preserve very short ways (but will be rendered backwards) if pointid != finalpointid: continue - #Skip points too close to end + # Skip points too close to end if math.sqrt((lat * lat_feet - finalpoint[0] * lat_feet)**2 + (lon * lon_feet - finalpoint[1] * lon_feet)**2) < pullback: - #Preserve very short ways (but will be rendered backwards) - if (pointid != firstpointid) and (pointid != finalpointid): + # Preserve very short ways (but will be rendered backwards) + if pointid not in (firstpointid, finalpointid): continue X = (lon - lastpoint[1]) * lon_feet @@ -111,12 +110,12 @@ def addressways(waylist, nodelist, first_id): dY = (Xp * (pullback / distance)) / lat_feet if left: lpoint = (lastpoint[0] + (Yp / lat_feet) - dY, lastpoint[1] + (Xp / lon_feet) - dX) - lsegment.append( (id, lpoint) ) - id += 1 + lsegment.append( (way_id, lpoint) ) + way_id += 1 if right: rpoint = (lastpoint[0] - (Yp / lat_feet) - dY, lastpoint[1] - (Xp / lon_feet) - dX) - rsegment.append( (id, rpoint) ) - id += 1 + rsegment.append( (way_id, rpoint) ) + way_id += 1 else: #round the curves @@ -130,13 +129,13 @@ def addressways(waylist, nodelist, first_id): r = 1 + abs(math.tan(theta/2)) if left: lpoint = (lastpoint[0] + (Yp + delta[0]) * r / (lat_feet * 2), lastpoint[1] + (Xp + delta[1]) * r / (lon_feet * 2)) - lsegment.append( (id, lpoint) ) - id += 1 + lsegment.append( (way_id, lpoint) ) + way_id += 1 if right: rpoint = (lastpoint[0] - (Yp + delta[0]) * r / (lat_feet * 2), lastpoint[1] - (Xp + delta[1]) * r / (lon_feet * 2)) - rsegment.append( (id, rpoint) ) - id += 1 + rsegment.append( (way_id, rpoint) ) + way_id += 1 delta = (Yp, Xp) @@ -148,12 +147,12 @@ def addressways(waylist, nodelist, first_id): dY = (Xp * (pullback / distance)) / lat_feet if left: lpoint = (lastpoint[0] + (Yp + delta[0]) / (lat_feet * 2) + dY, lastpoint[1] + (Xp + delta[1]) / (lon_feet * 2) + dX ) - lsegment.append( (id, lpoint) ) - id += 1 + lsegment.append( (way_id, lpoint) ) + way_id += 1 if right: rpoint = (lastpoint[0] - Yp / lat_feet + dY, lastpoint[1] - Xp / lon_feet + dX) - rsegment.append( (id, rpoint) ) - id += 1 + rsegment.append( (way_id, rpoint) ) + way_id += 1 # Generate the tags for ways and nodes zipr = tags.get("tiger:zip_right", '') @@ -231,7 +230,9 @@ def compile_waylist( parsed_gisdata ): def shape_to_csv( shp_filename, csv_filename ): - + """ + Main feature: reads a file, writes a file + """ print("parsing shpfile %s" % shp_filename) parsed_features = parse_shp_for_geom_and_tags( shp_filename ) @@ -246,8 +247,8 @@ def shape_to_csv( shp_filename, csv_filename ): print("writing %s" % csv_filename) fieldnames = ['from', 'to', 'interpolation', 'street', 'city', 'state', 'postcode', 'geometry'] - with open(csv_filename, 'w') as f: - csv_writer = csv.DictWriter(f, delimiter=';', fieldnames=fieldnames) + with open(csv_filename, 'w', encoding="utf8") as csv_file: + csv_writer = csv.DictWriter(csv_file, delimiter=';', fieldnames=fieldnames) csv_writer.writeheader() csv_writer.writerows(csv_lines)