Updated draw.py and fetch.py for flexibility

This commit is contained in:
Marcelo Prates
2021-08-15 13:30:02 -03:00
parent e696e53a74
commit dedab1066b
7 changed files with 545 additions and 291 deletions

View File

@@ -1,185 +0,0 @@
# OpenStreetMap Networkx library to download data from OpenStretMap
import osmnx as ox
# Matplotlib-related stuff, for drawing
from matplotlib.path import Path
from matplotlib import pyplot as plt
import matplotlib.patches as patches
from matplotlib.patches import PathPatch
# CV2 & Scipy & Numpy & Pandas
import numpy as np
from numpy.random import choice
# Shapely
from shapely.geometry import *
from shapely.affinity import *
# Geopandas
from geopandas import GeoDataFrame
# etc
import pandas as pd
from functools import reduce
from tabulate import tabulate
from IPython.display import Markdown, display
from collections.abc import Iterable
# Fetch
from fetch import *
# Drawing functions
def show_palette(palette, description = ''):
'''
Helper to display palette in Markdown
'''
colorboxes = [
f'![](https://placehold.it/30x30/{c[1:]}/{c[1:]}?text=)'
for c in palette
]
display(Markdown((description)))
display(Markdown(tabulate(pd.DataFrame(colorboxes), showindex = False)))
def get_patch(shape, **kwargs):
'''
Convert shapely object to matplotlib patch
'''
if type(shape) == Path:
return patches.PathPatch(shape, **kwargs)
elif type(shape) == Polygon and shape.area > 0:
return patches.Polygon(list(zip(*shape.exterior.xy)), **kwargs)
else:
return None
def plot_shape(shape, ax, **kwargs):
'''
Plot shapely object
'''
if isinstance(shape, Iterable):
for shape_ in shape:
plot_shape(shape_, ax, **kwargs)
else:
ax.add_patch(get_patch(shape, **kwargs))
def plot_shapes(shapes, ax, palette = None, **kwargs):
'''
Plot collection of shapely objects (optionally, use a color palette)
'''
if not isinstance(shapes, Iterable):
shapes = [shapes]
for shape in shapes:
if palette is None:
plot_shape(shape, ax, **kwargs)
else:
plot_shape(shape, ax, fc = choice(palette), **kwargs)
def plot_streets(streets, ax, color = '#f5da9f', background_color = 'white', **kwargs):
'''
Plot shapely Polygon (or MultiPolygon) representing streets using matplotlib PathPatches
'''
for s in streets if isinstance(streets, Iterable) else [streets]:
if s is not None:
ax.add_patch(get_patch(pathify(s), facecolor = color, edgecolor = 'black', **kwargs))
def plot(
# Address
query,
# Figure parameters
figsize = (10, 10),
ax = None,
title = None,
# Whether to plot a circle centered around the address; circle params
circle = False,
radius = 1000,
streets_radius = 1000,
# Street params
dilate_streets = 5,
draw_streets = True,
# Color params
background_color = 'white',
background_alpha = 1.,
palette = None,
perimeter_lw = 1,
perimeter_ec = 'black',
water_ec = 'black',
land_ec = 'black',
buildings_ec = 'black',
# Which layers to plot
layers = ['perimeter', 'landuse', 'water', 'building', 'streets'],
# Layer ordering params
zorder_perimeter = None,
zorder_landuse = None,
zorder_water = None,
zorder_streets = None,
zorder_building = None,
# Whether to fetch data using OSM Id
by_osmid = False,
by_coordinates = False,
):
#############
### Fetch ###
#############
# Geocode central point
if by_coordinates:
point = (float(query.split(",")[0].strip()), float(query.split(",")[1].strip()))
elif not by_osmid:
point = ox.geocode(query)
# Fetch perimeter
perimeter = get_perimeter(query, by_osmid = by_osmid) if not circle else None
# Fetch buildings, land, water, streets
layers_dict = {}
for layer in layers:
if layer == 'perimeter':
pass
elif layer == 'streets':
layers_dict[layer], _ = get_streets(
**({'point': point, 'radius': streets_radius} if circle else {'perimeter': perimeter}),
dilate = dilate_streets
)
else:
layers_dict[layer], perimeter_ = get_footprints(
**({'point': point, 'radius': radius} if circle else {'perimeter': perimeter}),
footprint = layer
)
# Project perimeter
if 'perimeter' in layers:
layers_dict['perimeter'] = perimeter_ if circle else union(ox.project_gdf(perimeter).geometry)
############
### Plot ###
############
if ax is None:
# if ax is none, create figure
fig, ax = plt.subplots(figsize = figsize)
# Ajust axis
ax.axis('off')
ax.axis('equal')
ax.autoscale()
# Setup parameters for drawing layers
layer_kwargs = {
'perimeter': {'lw': perimeter_lw, 'ec': perimeter_ec, 'fc': background_color, 'alpha': background_alpha, 'zorder': zorder_perimeter},
'landuse': {'ec': land_ec, 'fc': '#53bd53', 'zorder': zorder_landuse},
'water': {'ec': water_ec, 'fc': '#a1e3ff', 'zorder': zorder_water},
'streets': {'fc': '#f5da9f', 'zorder': zorder_streets},
'building': {'ec': buildings_ec, 'palette': palette, 'zorder': zorder_building},
}
# Draw layers
for layer in ['perimeter', 'landuse', 'water', 'streets', 'building']:
if layer in layers_dict:
plot_shapes(layers_dict[layer], ax, **layer_kwargs[layer])
# Return perimeter
return layers_dict['perimeter']

View File

@@ -1,106 +0,0 @@
# OpenStreetMap Networkx library to download data from OpenStretMap
import osmnx as ox
# CV2 & Scipy & Numpy & Pandas
import numpy as np
# Shapely
from shapely.geometry import *
from shapely.affinity import *
# Geopandas
from geopandas import GeoDataFrame
# Matplotlib
from matplotlib.path import Path
# etc
from collections.abc import Iterable
from functools import reduce
# Helper functions to fetch data from OSM
def ring_coding(ob):
codes = np.ones(len(ob.coords), dtype = Path.code_type) * Path.LINETO
codes[0] = Path.MOVETO
return codes
def pathify(polygon):
vertices = np.concatenate([np.asarray(polygon.exterior)] + [np.asarray(r) for r in polygon.interiors])
codes = np.concatenate([ring_coding(polygon.exterior)] + [ring_coding(r) for r in polygon.interiors])
return Path(vertices, codes)
def union(geometry):
geometry = np.concatenate([[x] if type(x) == Polygon else x for x in geometry if type(x) in [Polygon, MultiPolygon]])
geometry = reduce(lambda x, y: x.union(y), geometry[1:], geometry[0])
return geometry
def get_perimeter(query, by_osmid = False):
return ox.geocode_to_gdf(query, by_osmid = by_osmid)
def get_footprints(perimeter = None, point = None, radius = None, footprint = 'building'):
if perimeter is not None:
# Boundary defined by polygon (perimeter)
footprints = ox.geometries_from_polygon(union(perimeter.geometry), tags = {footprint: True} if type(footprint) == str else footprint)
perimeter = union(ox.project_gdf(perimeter).geometry)
elif (point is not None) and (radius is not None):
# Boundary defined by circle with radius 'radius' around point
footprints = ox.geometries_from_point(point, dist = radius, tags = {footprint: True} if type(footprint) == str else footprint)
perimeter = GeoDataFrame(geometry=[Point(point[::-1])], crs = footprints.crs)
perimeter = ox.project_gdf(perimeter).geometry[0].buffer(radius)
if len(footprints) > 0:
footprints = ox.project_gdf(footprints)
footprints = [
[x] if type(x) == Polygon else x
for x in footprints.geometry if type(x) in [Polygon, MultiPolygon]
]
footprints = list(np.concatenate(footprints)) if len(footprints) > 0 else []
footprints = [pathify(x) for x in footprints if x.within(perimeter)]
return footprints, perimeter
def get_streets(perimeter = None, point = None, radius = None, dilate = 6, custom_filter = None):
if perimeter is not None:
# Boundary defined by polygon (perimeter)
streets = ox.graph_from_polygon(union(perimeter.geometry), custom_filter = custom_filter)
streets = ox.project_graph(streets)
streets = ox.graph_to_gdfs(streets, nodes = False)
#streets = ox.project_gdf(streets)
streets = MultiLineString(list(streets.geometry)).buffer(dilate)
elif (point is not None) and (radius is not None):
# Boundary defined by polygon (perimeter)
streets = ox.graph_from_point(point, dist = radius, custom_filter = custom_filter)
crs = ox.graph_to_gdfs(streets, nodes = False).crs
streets = ox.project_graph(streets)
perimeter = GeoDataFrame(geometry=[Point(point[::-1])], crs = crs)
perimeter = ox.project_gdf(perimeter).geometry[0].buffer(radius)
streets = ox.graph_to_gdfs(streets, nodes = False)
streets = MultiLineString(list(
filter(
# Filter lines with at least 2 points
lambda line: len(line) >= 2,
# Iterate over lines in geometry
map(
# Filter points within perimeter
lambda line: list(filter(lambda xy: Point(xy).within(perimeter), zip(*line.xy))),
streets.geometry
)
)
)).buffer(dilate) # Dilate lines
if not isinstance(streets, Iterable):
streets = [streets]
streets = list(map(pathify, streets))
return streets, perimeter

3
prettymaps/__init__.py Normal file
View File

@@ -0,0 +1,3 @@
# Fetch & Draw
from .draw import *
from .fetch import *

164
prettymaps/curved_text.py Normal file
View File

@@ -0,0 +1,164 @@
from matplotlib import pyplot as plt
from matplotlib import patches
from matplotlib import text as mtext
import numpy as np
import math
class CurvedText(mtext.Text):
"""
A text object that follows an arbitrary curve.
"""
def __init__(self, x, y, text, axes, **kwargs):
super(CurvedText, self).__init__(x[0],y[0],' ', **kwargs)
axes.add_artist(self)
##saving the curve:
self.__x = x
self.__y = y
self.__zorder = self.get_zorder()
##creating the text objects
self.__Characters = []
for c in text:
if c == ' ':
##make this an invisible 'a':
t = mtext.Text(0,0,'a')
t.set_alpha(0.0)
else:
t = mtext.Text(0,0,c, **kwargs)
#resetting unnecessary arguments
t.set_ha('center')
t.set_rotation(0)
t.set_zorder(self.__zorder +1)
self.__Characters.append((c,t))
axes.add_artist(t)
##overloading some member functions, to assure correct functionality
##on update
def set_zorder(self, zorder):
super(CurvedText, self).set_zorder(zorder)
self.__zorder = self.get_zorder()
for c,t in self.__Characters:
t.set_zorder(self.__zorder+1)
def draw(self, renderer, *args, **kwargs):
"""
Overload of the Text.draw() function. Do not do
do any drawing, but update the positions and rotation
angles of self.__Characters.
"""
self.update_positions(renderer)
def update_positions(self,renderer):
"""
Update positions and rotations of the individual text elements.
"""
#preparations
##determining the aspect ratio:
##from https://stackoverflow.com/a/42014041/2454357
##data limits
xlim = self.axes.get_xlim()
ylim = self.axes.get_ylim()
## Axis size on figure
figW, figH = self.axes.get_figure().get_size_inches()
## Ratio of display units
_, _, w, h = self.axes.get_position().bounds
##final aspect ratio
aspect = ((figW * w)/(figH * h))*(ylim[1]-ylim[0])/(xlim[1]-xlim[0])
#points of the curve in figure coordinates:
x_fig,y_fig = (
np.array(l) for l in zip(*self.axes.transData.transform([
(i,j) for i,j in zip(self.__x,self.__y)
]))
)
#point distances in figure coordinates
x_fig_dist = (x_fig[1:]-x_fig[:-1])
y_fig_dist = (y_fig[1:]-y_fig[:-1])
r_fig_dist = np.sqrt(x_fig_dist**2+y_fig_dist**2)
#arc length in figure coordinates
l_fig = np.insert(np.cumsum(r_fig_dist),0,0)
#angles in figure coordinates
rads = np.arctan2((y_fig[1:] - y_fig[:-1]),(x_fig[1:] - x_fig[:-1]))
degs = np.rad2deg(rads)
rel_pos = 10
for c,t in self.__Characters:
#finding the width of c:
t.set_rotation(0)
t.set_va('center')
bbox1 = t.get_window_extent(renderer=renderer)
w = bbox1.width
h = bbox1.height
#ignore all letters that don't fit:
if rel_pos+w/2 > l_fig[-1]:
t.set_alpha(0.0)
rel_pos += w
continue
elif c != ' ':
t.set_alpha(1.0)
#finding the two data points between which the horizontal
#center point of the character will be situated
#left and right indices:
il = np.where(rel_pos+w/2 >= l_fig)[0][-1]
ir = np.where(rel_pos+w/2 <= l_fig)[0][0]
#if we exactly hit a data point:
if ir == il:
ir += 1
#how much of the letter width was needed to find il:
used = l_fig[il]-rel_pos
rel_pos = l_fig[il]
#relative distance between il and ir where the center
#of the character will be
fraction = (w/2-used)/r_fig_dist[il]
##setting the character position in data coordinates:
##interpolate between the two points:
x = self.__x[il]+fraction*(self.__x[ir]-self.__x[il])
y = self.__y[il]+fraction*(self.__y[ir]-self.__y[il])
#getting the offset when setting correct vertical alignment
#in data coordinates
t.set_va(self.get_va())
bbox2 = t.get_window_extent(renderer=renderer)
bbox1d = self.axes.transData.inverted().transform(bbox1)
bbox2d = self.axes.transData.inverted().transform(bbox2)
dr = np.array(bbox2d[0]-bbox1d[0])
#the rotation/stretch matrix
rad = rads[il]
rot_mat = np.array([
[math.cos(rad), math.sin(rad)*aspect],
[-math.sin(rad)/aspect, math.cos(rad)]
])
##computing the offset vector of the rotated character
drp = np.dot(dr,rot_mat)
#setting final position and rotation:
t.set_position(np.array([x,y])+drp)
t.set_rotation(degs[il])
t.set_va('center')
t.set_ha('center')
#updating rel_pos to right edge of character
rel_pos += w-used

204
prettymaps/draw.py Normal file
View File

@@ -0,0 +1,204 @@
# OpenStreetMap Networkx library to download data from OpenStretMap
#from sympy import geometry
import osmnx as ox
# Matplotlib-related stuff, for drawing
from matplotlib.path import Path
from matplotlib import pyplot as plt
import matplotlib.patches as patches
from matplotlib.patches import PathPatch
# CV2 & Scipy & Numpy & Pandas
import numpy as np
from numpy.random import choice
# Shapely
from shapely.geometry import *
from shapely.affinity import *
# Geopandas
from geopandas import GeoDataFrame
# etc
import pandas as pd
from functools import reduce
from tabulate import tabulate
from IPython.display import Markdown, display
from collections.abc import Iterable
# Fetch
from fetch import *
# Helper functions
def get_hash(key):
return frozenset(key.items()) if type(key) == dict else key
# Drawing functions
def show_palette(palette, description = ''):
'''
Helper to display palette in Markdown
'''
colorboxes = [
f'![](https://placehold.it/30x30/{c[1:]}/{c[1:]}?text=)'
for c in palette
]
display(Markdown((description)))
display(Markdown(tabulate(pd.DataFrame(colorboxes), showindex = False)))
def get_patch(shape, **kwargs):
'''
Convert shapely object to matplotlib patch
'''
#if type(shape) == Path:
# return patches.PathPatch(shape, **kwargs)
if type(shape) == Polygon and shape.area > 0:
return PolygonPatch(list(zip(*shape.exterior.xy)), **kwargs)
else:
return None
def plot_shape(shape, ax, vsketch = None, **kwargs):
'''
Plot shapely object
'''
if isinstance(shape, Iterable) and type(shape) != MultiLineString:
for shape_ in shape:
plot_shape(shape_, ax, vsketch = vsketch, **kwargs)
else:
if not shape.is_empty:
if vsketch is None:
ax.add_patch(PolygonPatch(shape, **kwargs))
else:
if ('draw' not in kwargs) or kwargs['draw']:
if ('pen' in kwargs):
vsketch.stroke(kwargs['pen'])
else:
vsketch.stroke(1)
vsketch.geometry(shape)
def plot_shapes(shapes, ax, vsketch = None, palette = None, **kwargs):
'''
Plot collection of shapely objects (optionally, use a color palette)
'''
if not isinstance(shapes, Iterable):
shapes = [shapes]
for shape in shapes:
if palette is None:
plot_shape(shape, ax, vsketch = vsketch, **kwargs)
else:
plot_shape(shape, ax, vsketch = vsketch, fc = choice(palette), **kwargs)
def plot(
# Address
query,
# Whether to use a backup for the layers
backup = None,
# Radius (in case of circular plot)
radius = None,
# Which layers to plot
layers = {'perimeter': {}},
# Drawing params for each layer (matplotlib params such as 'fc', 'ec', 'fill', etc.)
drawing_kwargs = {},
# Figure parameters
figsize = (10, 10), ax = None, title = None,
# Vsketch parameters
vsketch = None,
# Transform (translation & scale) params
x = None, y = None, sf = None, rotation = None,
):
# Interpret query
if type(query) == tuple:
query_mode = 'coordinates'
elif False:
query_mode = 'osmid'
else:
query_mode = 'address'
# Save maximum dilation for later use
dilations = [kwargs['dilate'] for kwargs in layers.values() if 'dilate' in kwargs]
max_dilation = max(dilations) if len(dilations) > 0 else 0
if backup is None:
#############
### Fetch ###
#############
# Define base kwargs
if radius:
base_kwargs = {'point': query if type(query) == tuple else ox.geocode(query), 'radius': radius}
else:
by_osmid = False
base_kwargs = {'perimeter': get_perimeter(query, by_osmid = by_osmid)}
# Fetch layers
layers = {
layer: get_layer(
layer,
**base_kwargs,
**(kwargs if type(kwargs) == dict else {})
)
for layer, kwargs in layers.items()
}
# Transform layers (translate & scale)
k, v = zip(*layers.items())
v = GeometryCollection(v)
if (x is not None) and (y is not None):
v = translate(v, *(np.array([x, y]) - np.concatenate(v.centroid.xy)))
if sf is not None:
v = scale(v, sf, sf)
if rotation is not None:
v = rotate(v, rotation)
layers = dict(zip(k, v))
else:
layers = backup
if vsketch is None:
# Ajust axis
ax.axis('off')
ax.axis('equal')
ax.autoscale()
# Plot background
if 'background' in drawing_kwargs:
xmin, ymin, xmax, ymax = layers['perimeter'].bounds
geom = scale(Polygon([
(xmin, ymin),
(xmin, ymax),
(xmax, ymax),
(xmax, ymin)
]), 2, 2)
if vsketch is None:
ax.add_patch(PolygonPatch(geom, **drawing_kwargs['background']))
else:
vsketch.geometry(geom)
############
### Plot ###
############
# Adjust bounds
xmin, ymin, xmax, ymax = layers['perimeter'].buffer(max_dilation).bounds
if vsketch is None:
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)
# Draw layers
for layer, shapes in layers.items():
kwargs = drawing_kwargs[layer] if layer in drawing_kwargs else {}
if 'hatch_c' in kwargs:
plot_shapes(shapes, ax, vsketch = vsketch, lw = 0, ec = kwargs['hatch_c'], **{k:v for k,v in kwargs.items() if k not in ['lw', 'ec', 'hatch_c']})
plot_shapes(shapes, ax, vsketch = vsketch, fill = False, **{k:v for k,v in kwargs.items() if k not in ['hatch_c', 'hatch', 'fill']})
else:
plot_shapes(shapes, ax, vsketch = vsketch, **kwargs)
# Return perimeter
return layers

153
prettymaps/fetch.py Normal file
View File

@@ -0,0 +1,153 @@
# OpenStreetMap Networkx library to download data from OpenStretMap
from ast import Mult
from operator import ge
import osmnx as ox
# CV2 & Scipy & Numpy & Pandas
import numpy as np
# Shapely
from shapely.geometry import *
from shapely.affinity import *
from shapely.ops import unary_union
# Geopandas
from geopandas import GeoDataFrame
# Matplotlib
from matplotlib.path import Path
# etc
from collections.abc import Iterable
from functools import reduce
from descartes import PolygonPatch
from functools import reduce
# Helper functions to fetch data from OSM
def ring_coding(ob):
codes = np.ones(len(ob.coords), dtype = Path.code_type) * Path.LINETO
codes[0] = Path.MOVETO
return codes
def pathify(polygon):
vertices = np.concatenate([np.asarray(polygon.exterior)] + [np.asarray(r) for r in polygon.interiors])
codes = np.concatenate([ring_coding(polygon.exterior)] + [ring_coding(r) for r in polygon.interiors])
return Path(vertices, codes)
def union(geometry):
geometry = np.concatenate([[x] if type(x) == Polygon else x for x in geometry if type(x) in [Polygon, MultiPolygon]])
geometry = reduce(lambda x, y: x.union(y), geometry[1:], geometry[0])
return geometry
def get_boundary(point, radius, crs, circle = True, dilate = 0):
if circle:
return ox.project_gdf(
GeoDataFrame(geometry = [Point(point[::-1])], crs = crs)
).geometry[0].buffer(radius)
else:
x, y = np.stack(ox.project_gdf(
GeoDataFrame(geometry = [Point(point[::-1])], crs = crs)
).geometry[0].xy)
r = radius
return Polygon([
(x-r, y-r), (x+r, y-r), (x+r, y+r), (x-r, y+r)
]).buffer(dilate)
def get_perimeter(query, by_osmid = False, **kwargs):
return ox.geocode_to_gdf(query, by_osmid = by_osmid, **kwargs, **{x: kwargs[x] for x in ['circle', 'dilate'] if x in kwargs.keys()})
def get_geometries(perimeter = None, point = None, radius = None, tags = {}, perimeter_tolerance = 0, union = True, circle = True, dilate = 0):
if perimeter is not None:
# Boundary defined by polygon (perimeter)
geometries = ox.geometries_from_polygon(
unary_union(perimeter.geometry).buffer(perimeter_tolerance) if perimeter_tolerance > 0 else unary_union(perimeter.geometry),
tags = {tags: True} if type(tags) == str else tags
)
perimeter = unary_union(ox.project_gdf(perimeter).geometry)
elif (point is not None) and (radius is not None):
# Boundary defined by circle with radius 'radius' around point
geometries = ox.geometries_from_point(point, dist = radius+dilate, tags = {tags: True} if type(tags) == str else tags)
perimeter = get_boundary(point, radius, geometries.crs, circle = circle, dilate = dilate)
# Project GDF
if len(geometries) > 0:
geometries = ox.project_gdf(geometries)
# Intersect with perimeter
geometries = geometries.intersection(perimeter)
if union:
geometries = unary_union(reduce(lambda x,y: x+y, [
[x] if type(x) == Polygon else list(x)
for x in geometries if type(x) in [Polygon, MultiPolygon]
], []))
else:
geometries = MultiPolygon(reduce(lambda x,y: x+y, [
[x] if type(x) == Polygon else list(x)
for x in geometries if type(x) in [Polygon, MultiPolygon]
], []))
return geometries
def get_streets(perimeter = None, point = None, radius = None, width = 6, custom_filter = None, circle = True, dilate = 0):
# Boundary defined by polygon (perimeter)
if perimeter is not None:
# Fetch streets data, project & convert to GDF
streets = ox.graph_from_polygon(union(perimeter.geometry), custom_filter = custom_filter)
streets = ox.project_graph(streets)
streets = ox.graph_to_gdfs(streets, nodes = False)
# Boundary defined by polygon (perimeter)
elif (point is not None) and (radius is not None):
# Fetch streets data, save CRS & project
streets = ox.graph_from_point(point, dist = radius+dilate, custom_filter = custom_filter)
crs = ox.graph_to_gdfs(streets, nodes = False).crs
streets = ox.project_graph(streets)
# Compute perimeter from point & CRS
perimeter = get_boundary(point, radius, crs, circle = circle, dilate = dilate)
# Convert to GDF
streets = ox.graph_to_gdfs(streets, nodes = False)
# Intersect with perimeter & filter empty elements
streets.geometry = streets.geometry.intersection(perimeter)
streets = streets[~streets.geometry.is_empty]
if type(width) == dict:
streets = unary_union([
# Dilate streets of each highway type == 'highway' using width 'w'
MultiLineString(
streets[(streets.highway == highway) & (streets.geometry.type == 'LineString')].geometry.tolist() +
list(reduce(lambda x, y: x+y, [
list(lines)
for lines in streets[(streets.highway == highway) & (streets.geometry.type == 'MultiLineString')].geometry
], []))
).buffer(w)
for highway, w in width.items()
])
else:
# Dilate all streets by same amount 'width'
streets = MultiLineString(streets.geometry.tolist()).buffer(width)
return streets
def get_layer(layer, **kwargs):
if layer == 'perimeter':
if 'perimeter' in kwargs:
return unary_union(ox.project_gdf(kwargs['perimeter']).geometry)
elif 'point' in kwargs and 'radius' in kwargs:
# Dummy request to fetch CRS
crs = ox.graph_to_gdfs(ox.graph_from_point(kwargs['point'], dist = kwargs['radius']), nodes = False).crs
perimeter = get_boundary(
kwargs['point'], kwargs['radius'], crs,
**{x: kwargs[x] for x in ['circle', 'dilate'] if x in kwargs.keys()}
)
return perimeter
else:
raise Exception("Either 'perimeter' or 'point' & 'radius' must be provided")
if layer in ['streets', 'railway']:
return get_streets(**kwargs)
else:
return get_geometries(**kwargs)

21
setup.py Normal file
View File

@@ -0,0 +1,21 @@
from setuptools import setup
setup(
name='prettymaps',
version='1.0.0',
description='A simple python library to draw pretty maps from OpenStreetMap data',
url='https://github.com/marceloprates/prettymaps',
author='Marcelo Prates',
author_email='marceloorp@gmail.com',
license='MIT License',
packages=['prettymaps'],
install_requires=[
'osmnx=1.0.1',
'tabulate=0.8.9',
'jupyter=1.0.0',
],
classifiers=[
'Intended Audience :: Science/Research',
],
)