Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions makeLawrenceMap.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,13 +195,13 @@ def expand(image, fill = '#e0474c', bottom = 50, left = None, right = None, top
"""


if left == None:
if left is None:
left = 0
if right == None:
if right is None:
right = 0
if top == None:
if top is None:
top = 0

Comment on lines -198 to +204
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function expand refactored with the following changes:

width = left + image.size[0] + right
height = top + image.size[1] + bottom
out = Image.new(image.mode, (width, height), _color(fill, image.mode))
Expand Down Expand Up @@ -231,13 +231,13 @@ def add_border(input_image, output_image, fill = '#e0474c', bottom = 50, left =
"""


if left == None:
if left is None:
left = 0
if right == None:
if right is None:
right = 0
if top == None:
if top is None:
top = 0

Comment on lines -234 to +240
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function add_border refactored with the following changes:

img = Image.open(input_image)
bimg = expand(img, bottom = bottom, left = left, right = right, top = top, fill= '#e0474c')
bimg.save(output_image)
Expand Down
340 changes: 160 additions & 180 deletions osmnx/core.py

Large diffs are not rendered by default.

21 changes: 15 additions & 6 deletions osmnx/elevation.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@


def add_node_elevations(G, api_key, max_locations_per_batch=350,
pause_duration=0.02): # pragma: no cover
pause_duration=0.02): # pragma: no cover
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function add_node_elevations refactored with the following changes:

"""
Get the elevation (meters) of each node in the network and add it to the
node as an attribute.
Expand Down Expand Up @@ -47,7 +47,10 @@ def add_node_elevations(G, api_key, max_locations_per_batch=350,
# round coorindates to 5 decimal places (approx 1 meter) to be able to fit
# in more locations per API call
node_points = pd.Series({node:'{:.5f},{:.5f}'.format(data['y'], data['x']) for node, data in G.nodes(data=True)})
log('Requesting node elevations from the API in {} calls.'.format(math.ceil(len(node_points) / max_locations_per_batch)))
log(
f'Requesting node elevations from the API in {math.ceil(len(node_points) / max_locations_per_batch)} calls.'
)


# break the series of coordinates into chunks of size max_locations_per_batch
# API format is locations=lat,lng|lat,lng|lat,lng|lat,lng...
Expand All @@ -64,23 +67,29 @@ def add_node_elevations(G, api_key, max_locations_per_batch=350,
else:
try:
# request the elevations from the API
log('Requesting node elevations: {}'.format(url))
log(f'Requesting node elevations: {url}')
time.sleep(pause_duration)
response = requests.get(url)
response_json = response.json()
save_to_cache(url, response_json)
except Exception as e:
log(e)
log('Server responded with {}: {}'.format(response.status_code, response.reason))
log(f'Server responded with {response.status_code}: {response.reason}')

# append these elevation results to the list of all results
results.extend(response_json['results'])

# sanity check that all our vectors have the same number of elements
if not (len(results) == len(G.nodes()) == len(node_points)):
raise Exception('Graph has {} nodes but we received {} results from the elevation API.'.format(len(G.nodes()), len(results)))
raise Exception(
f'Graph has {len(G.nodes())} nodes but we received {len(results)} results from the elevation API.'
)

else:
log('Graph has {} nodes and we received {} results from the elevation API.'.format(len(G.nodes()), len(results)))
log(
f'Graph has {len(G.nodes())} nodes and we received {len(results)} results from the elevation API.'
)


# add elevation as an attribute to the nodes
df = pd.DataFrame(node_points, columns=['node_points'])
Expand Down
86 changes: 41 additions & 45 deletions osmnx/footprints.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,13 @@ def osm_footprints_download(polygon=None, north=None, south=None, east=None, wes
# check if we're querying by polygon or by bounding box based on which
# argument(s) where passed into this function
by_poly = polygon is not None
by_bbox = not (north is None or south is None or east is None or west is None)
by_bbox = (
north is not None
and south is not None
and east is not None
and west is not None
)

Comment on lines -70 to +76
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function osm_footprints_download refactored with the following changes:

if not (by_poly or by_bbox):
raise ValueError('You must pass a polygon or north, south, east, and west')

Expand All @@ -77,11 +83,7 @@ def osm_footprints_download(polygon=None, north=None, south=None, east=None, wes
# if None, pass nothing so the server will use its default allocation size
# otherwise, define the query's maxsize parameter value as whatever the
# caller passed in
if memory is None:
maxsize = ''
else:
maxsize = '[maxsize:{}]'.format(memory)

maxsize = '' if memory is None else f'[maxsize:{memory}]'
# define the query to send the API
if by_bbox:
# turn bbox into a polygon and project to local UTM
Expand All @@ -95,18 +97,18 @@ def osm_footprints_download(polygon=None, north=None, south=None, east=None, wes
log('Requesting footprints data within bounding box from API in {:,} request(s)'.format(len(geometry)))
start_time = time.time()

query_template = ('[out:json][timeout:{timeout}]{maxsize};'
'((way["{footprint_type}"]({south:.8f},{west:.8f},{north:.8f},{east:.8f});'
'(._;>;););'
'(relation["{footprint_type}"]({south:.8f},{west:.8f},{north:.8f},{east:.8f});'
'(._;>;);););out;')
# loop through each polygon rectangle in the geometry (there will only
# be one if original bbox didn't exceed max area size)
for poly in geometry:
# represent bbox as south,west,north,east and round lat-longs to 8
# decimal places (ie, within 1 mm) so URL strings aren't different
# due to float rounding issues (for consistent caching)
west, south, east, north = poly.bounds
query_template = ('[out:json][timeout:{timeout}]{maxsize};'
'((way["{footprint_type}"]({south:.8f},{west:.8f},{north:.8f},{east:.8f});'
'(._;>;););'
'(relation["{footprint_type}"]({south:.8f},{west:.8f},{north:.8f},{east:.8f});'
'(._;>;);););out;')
query_str = query_template.format(north=north, south=south, east=east, west=west, timeout=timeout,
maxsize=maxsize, footprint_type=footprint_type)
response_json = overpass_request(data={'data':query_str}, timeout=timeout)
Expand All @@ -115,7 +117,7 @@ def osm_footprints_download(polygon=None, north=None, south=None, east=None, wes
'API in {:,} request(s) and {:,.2f} seconds')
log(msg.format(len(geometry), time.time()-start_time))

elif by_poly:
else:
# project to utm, divide polygon up into sub-polygons if area exceeds a
# max size (in meters), project back to lat-long, then get a list of polygon(s) exterior coordinates
geometry_proj, crs_proj = project_geometry(polygon)
Expand All @@ -125,12 +127,12 @@ def osm_footprints_download(polygon=None, north=None, south=None, east=None, wes
log('Requesting footprint data within polygon from API in {:,} request(s)'.format(len(polygon_coord_strs)))
start_time = time.time()

query_template = ('[out:json][timeout:{timeout}]{maxsize};('
'way(poly:"{polygon}")["{footprint_type}"];(._;>;);'
'relation(poly:"{polygon}")["{footprint_type}"];(._;>;););out;')
# pass each polygon exterior coordinates in the list to the API, one at
# a time
for polygon_coord_str in polygon_coord_strs:
query_template = ('[out:json][timeout:{timeout}]{maxsize};('
'way(poly:"{polygon}")["{footprint_type}"];(._;>;);'
'relation(poly:"{polygon}")["{footprint_type}"];(._;>;););out;')
query_str = query_template.format(polygon=polygon_coord_str, timeout=timeout, maxsize=maxsize,
footprint_type=footprint_type)
response_json = overpass_request(data={'data':query_str}, timeout=timeout)
Expand Down Expand Up @@ -185,7 +187,7 @@ def create_footprints_gdf(polygon=None, north=None, south=None, east=None, west=
# create a complex Shapely Polygon or MultiPolygon for each relation
for relation_key, relation_val in relations.items():
relation_val['geometry'] = create_relation_geometry(relation_key, relation_val, footprints)

Comment on lines -188 to +190
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function create_footprints_gdf refactored with the following changes:

# merge relations into the footprints dictionary
footprints.update(relations)

Expand All @@ -194,7 +196,7 @@ def create_footprints_gdf(polygon=None, north=None, south=None, east=None, west=
try:
del footprints[untagged_way]
except KeyError:
log('untagged_way {} not found in footprints dict'.format(untagged_way))
log(f'untagged_way {untagged_way} not found in footprints dict')

# Convert footprints dictionary to a GeoDataFrame
gdf = gpd.GeoDataFrame.from_dict(footprints, orient='index')
Expand All @@ -206,7 +208,7 @@ def create_footprints_gdf(polygon=None, north=None, south=None, east=None, west=
filter2 = (gdf['geometry'].geom_type == 'Polygon') | (gdf['geometry'].geom_type == 'MultiPolygon')
filter_combined = filter1 & filter2
gdf = gdf[filter_combined]

return gdf


Expand Down Expand Up @@ -257,7 +259,6 @@ def responses_to_dicts(responses, footprint_type):
if 'type' in element and element['type']=='node':
vertices[element['id']] = {'lat' : element['lat'],
'lon' : element['lon']}
# WAYS - both open and closed
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function responses_to_dicts refactored with the following changes:

This removes the following comments ( why? ):

# RELATIONS
# WAYS - both open and closed
# Log any other Elements found in the response

elif 'type' in element and element['type']=='way':
footprint = {'nodes' : element['nodes']}
if 'tags' in element:
Expand All @@ -267,7 +268,6 @@ def responses_to_dicts(responses, footprint_type):
# add ways not individually tagged with footprint_type to the untagged_footprints set
if ('tags' not in element) or (footprint_type not in element['tags']):
untagged_footprints.add(element['id'])
# RELATIONS
elif 'type' in element and element['type']=='relation':
relation = {'members' : {}}
for member in element['members']:
Expand All @@ -280,9 +280,8 @@ def responses_to_dicts(responses, footprint_type):
# add relations not individually tagged with footprint_type to the untagged_footprints set
if ('tags' not in element) or (footprint_type not in element['tags']):
untagged_footprints.add(element['id'])
# Log any other Elements found in the response
else:
log('Element {} is not a node, way or relation'.format(element['id']))
log(f"Element {element['id']} is not a node, way or relation")

return vertices, footprints, relations, untagged_footprints

Expand Down Expand Up @@ -312,13 +311,12 @@ def create_footprint_geometry(footprint_key, footprint_val, vertices):
try:
footprint_geometry = Polygon([(vertices[node]['lon'], vertices[node]['lat']) for node in footprint_val['nodes']])
except Exception:
log('Polygon has invalid geometry: {}'.format(footprint_key))
# OPEN WAYS
log(f'Polygon has invalid geometry: {footprint_key}')
else:
try:
footprint_geometry = LineString([(vertices[node]['lon'], vertices[node]['lat']) for node in footprint_val['nodes']])
except Exception:
log('LineString has invalid geometry: {}'.format(footprint_key))
log(f'LineString has invalid geometry: {footprint_key}')
Comment on lines -315 to +319
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function create_footprint_geometry refactored with the following changes:

This removes the following comments ( why? ):

# OPEN WAYS


return footprint_geometry

Expand Down Expand Up @@ -365,41 +363,40 @@ def create_relation_geometry(relation_key, relation_val, footprints):

# add each members geometry to a list according to its role and geometry type
for member_id, member_role in relation_val['members'].items():
if member_role == 'outer':
if footprints[member_id]['geometry'].geom_type == 'Polygon':
outer_polys.append(footprints[member_id]['geometry'])
elif footprints[member_id]['geometry'].geom_type == 'LineString':
outer_lines.append(footprints[member_id]['geometry'])
elif member_role == 'inner':
if footprints[member_id]['geometry'].geom_type == 'Polygon':
inner_polys.append(footprints[member_id]['geometry'])
elif footprints[member_id]['geometry'].geom_type == 'LineString':
if member_role == 'inner':
if footprints[member_id]['geometry'].geom_type == 'LineString':
inner_lines.append(footprints[member_id]['geometry'])

elif footprints[member_id]['geometry'].geom_type == 'Polygon':
inner_polys.append(footprints[member_id]['geometry'])
elif member_role == 'outer':
if footprints[member_id]['geometry'].geom_type == 'LineString':
outer_lines.append(footprints[member_id]['geometry'])
elif footprints[member_id]['geometry'].geom_type == 'Polygon':
outer_polys.append(footprints[member_id]['geometry'])
# try to polygonize open outer ways and concatenate them to outer_polys
if len(outer_lines) > 0:
if outer_lines:
try:
result = list(polygonize(outer_lines))
except Exception:
log("polygonize failed for 'outer' ways in relation: {}".format(relation_key))
log(f"polygonize failed for 'outer' ways in relation: {relation_key}")
else:
outer_polys += result

# try to polygonize open inner ways and concatenate them to inner_polys
if len(inner_lines) > 0:
if inner_lines:
try:
result = list(polygonize(inner_lines))
except Exception:
log("polygonize failed for 'inner' ways in relation: {}".format(relation_key))
log(f"polygonize failed for 'inner' ways in relation: {relation_key}")
else:
inner_polys += result

# filter out relations missing both 'outer' and 'inner' polygons or just 'outer'
if len(outer_polys + inner_polys) == 0:
log("Relation {} missing 'outer' and 'inner' closed ways".format(relation_key))
elif len(outer_polys) == 0:
log("Relation {} missing 'outer' closed ways".format(relation_key))
# process the others to multipolygons
log(f"Relation {relation_key} missing 'outer' and 'inner' closed ways")
elif not outer_polys:
log(f"Relation {relation_key} missing 'outer' closed ways")
Comment on lines -368 to +399
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function create_relation_geometry refactored with the following changes:

This removes the following comments ( why? ):

# process the others to multipolygons

else:
for outer_poly in outer_polys:
temp_poly = outer_poly
Expand All @@ -414,7 +411,7 @@ def create_relation_geometry(relation_key, relation_val, footprints):
elif len(multipoly) > 1:
return MultiPolygon(multipoly)
else:
log('relation {} could not be converted to a complex footprint'.format(relation_key))
log(f'relation {relation_key} could not be converted to a complex footprint')


def footprints_from_point(point, distance, footprint_type='building', retain_invalid=False):
Expand Down Expand Up @@ -579,8 +576,7 @@ def plot_footprints(gdf, fig=None, ax=None, figsize=None, color='#333333', bgcol
if isinstance(geometry, Polygon):
patches.append(PolygonPatch(geometry))
elif isinstance(geometry, MultiPolygon):
for subpolygon in geometry: #if geometry is multipolygon, go through each constituent subpolygon
patches.append(PolygonPatch(subpolygon))
patches.extend(PolygonPatch(subpolygon) for subpolygon in geometry)
Comment on lines -582 to +579
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function plot_footprints refactored with the following changes:

This removes the following comments ( why? ):

#if geometry is multipolygon, go through each constituent subpolygon

pc = PatchCollection(patches, facecolor=color, edgecolor=color, linewidth=0, alpha=1)
ax.add_collection(pc)

Expand Down
Loading