Merge branch 'main' into main

This commit is contained in:
Marcelo de Oliveira Rosa Prates
2021-09-06 09:10:17 -03:00
committed by GitHub
5 changed files with 323 additions and 200 deletions

View File

@@ -8,8 +8,9 @@ class CurvedText(mtext.Text):
""" """
A text object that follows an arbitrary curve. A text object that follows an arbitrary curve.
""" """
def __init__(self, x, y, text, axes, **kwargs): def __init__(self, x, y, text, axes, **kwargs):
super(CurvedText, self).__init__(x[0],y[0],' ', **kwargs) super(CurvedText, self).__init__(x[0], y[0], " ", **kwargs)
axes.add_artist(self) axes.add_artist(self)
@@ -21,22 +22,21 @@ class CurvedText(mtext.Text):
##creating the text objects ##creating the text objects
self.__Characters = [] self.__Characters = []
for c in text: for c in text:
if c == ' ': if c == " ":
##make this an invisible 'a': ##make this an invisible 'a':
t = mtext.Text(0,0,'a') t = mtext.Text(0, 0, "a")
t.set_alpha(0.0) t.set_alpha(0.0)
else: else:
t = mtext.Text(0, 0, c, **kwargs) t = mtext.Text(0, 0, c, **kwargs)
# resetting unnecessary arguments # resetting unnecessary arguments
t.set_ha('center') t.set_ha("center")
t.set_rotation(0) t.set_rotation(0)
t.set_zorder(self.__zorder + 1) t.set_zorder(self.__zorder + 1)
self.__Characters.append((c, t)) self.__Characters.append((c, t))
axes.add_artist(t) axes.add_artist(t)
##overloading some member functions, to assure correct functionality ##overloading some member functions, to assure correct functionality
##on update ##on update
def set_zorder(self, zorder): def set_zorder(self, zorder):
@@ -75,14 +75,17 @@ class CurvedText(mtext.Text):
# points of the curve in figure coordinates: # points of the curve in figure coordinates:
x_fig, y_fig = ( x_fig, y_fig = (
np.array(l) for l in zip(*self.axes.transData.transform([ np.array(l)
(i,j) for i,j in zip(self.__x,self.__y) for l in zip(
])) *self.axes.transData.transform(
[(i, j) for i, j in zip(self.__x, self.__y)]
)
)
) )
# point distances in figure coordinates # point distances in figure coordinates
x_fig_dist = (x_fig[1:]-x_fig[:-1]) x_fig_dist = x_fig[1:] - x_fig[:-1]
y_fig_dist = (y_fig[1:]-y_fig[:-1]) y_fig_dist = y_fig[1:] - y_fig[:-1]
r_fig_dist = np.sqrt(x_fig_dist ** 2 + y_fig_dist ** 2) r_fig_dist = np.sqrt(x_fig_dist ** 2 + y_fig_dist ** 2)
# arc length in figure coordinates # arc length in figure coordinates
@@ -92,12 +95,11 @@ class CurvedText(mtext.Text):
rads = np.arctan2((y_fig[1:] - y_fig[:-1]), (x_fig[1:] - x_fig[:-1])) rads = np.arctan2((y_fig[1:] - y_fig[:-1]), (x_fig[1:] - x_fig[:-1]))
degs = np.rad2deg(rads) degs = np.rad2deg(rads)
rel_pos = 10 rel_pos = 10
for c, t in self.__Characters: for c, t in self.__Characters:
# finding the width of c: # finding the width of c:
t.set_rotation(0) t.set_rotation(0)
t.set_va('center') t.set_va("center")
bbox1 = t.get_window_extent(renderer=renderer) bbox1 = t.get_window_extent(renderer=renderer)
w = bbox1.width w = bbox1.width
h = bbox1.height h = bbox1.height
@@ -108,7 +110,7 @@ class CurvedText(mtext.Text):
rel_pos += w rel_pos += w
continue continue
elif c != ' ': elif c != " ":
t.set_alpha(1.0) t.set_alpha(1.0)
# finding the two data points between which the horizontal # finding the two data points between which the horizontal
@@ -145,10 +147,12 @@ class CurvedText(mtext.Text):
# the rotation/stretch matrix # the rotation/stretch matrix
rad = rads[il] rad = rads[il]
rot_mat = np.array([ rot_mat = np.array(
[
[math.cos(rad), math.sin(rad) * aspect], [math.cos(rad), math.sin(rad) * aspect],
[-math.sin(rad)/aspect, math.cos(rad)] [-math.sin(rad) / aspect, math.cos(rad)],
]) ]
)
##computing the offset vector of the rotated character ##computing the offset vector of the rotated character
drp = np.dot(dr, rot_mat) drp = np.dot(dr, rot_mat)
@@ -157,8 +161,8 @@ class CurvedText(mtext.Text):
t.set_position(np.array([x, y]) + drp) t.set_position(np.array([x, y]) + drp)
t.set_rotation(degs[il]) t.set_rotation(degs[il])
t.set_va('center') t.set_va("center")
t.set_ha('center') t.set_ha("center")
# updating rel_pos to right edge of character # updating rel_pos to right edge of character
rel_pos += w - used rel_pos += w - used

View File

@@ -6,7 +6,7 @@ import pandas as pd
from geopandas import GeoDataFrame from geopandas import GeoDataFrame
import numpy as np import numpy as np
from numpy.random import choice from numpy.random import choice
from shapely.geometry import Polygon, MultiPolygon, MultiLineString, GeometryCollection from shapely.geometry import box, Polygon, MultiLineString, GeometryCollection
from shapely.affinity import translate, scale, rotate from shapely.affinity import translate, scale, rotate
from descartes import PolygonPatch from descartes import PolygonPatch
from tabulate import tabulate from tabulate import tabulate
@@ -19,24 +19,25 @@ from .fetch import get_perimeter, get_layer
def get_hash(key): def get_hash(key):
return frozenset(key.items()) if type(key) == dict else key return frozenset(key.items()) if type(key) == dict else key
# Drawing functions # Drawing functions
def show_palette(palette, description = ''): def show_palette(palette, description=""):
''' """
Helper to display palette in Markdown Helper to display palette in Markdown
''' """
colorboxes = [ colorboxes = [
f'![](https://placehold.it/30x30/{c[1:]}/{c[1:]}?text=)' f"![](https://placehold.it/30x30/{c[1:]}/{c[1:]}?text=)" for c in palette
for c in palette
] ]
display(Markdown((description))) display(Markdown((description)))
display(Markdown(tabulate(pd.DataFrame(colorboxes), showindex=False))) display(Markdown(tabulate(pd.DataFrame(colorboxes), showindex=False)))
def get_patch(shape, **kwargs): def get_patch(shape, **kwargs):
''' """
Convert shapely object to matplotlib patch Convert shapely object to matplotlib patch
''' """
# if type(shape) == Path: # if type(shape) == Path:
# return patches.PathPatch(shape, **kwargs) # return patches.PathPatch(shape, **kwargs)
if type(shape) == Polygon and shape.area > 0: if type(shape) == Polygon and shape.area > 0:
@@ -44,11 +45,12 @@ def get_patch(shape, **kwargs):
else: else:
return None return None
# Plot a single shape # Plot a single shape
def plot_shape(shape, ax, vsketch=None, **kwargs): def plot_shape(shape, ax, vsketch=None, **kwargs):
''' """
Plot shapely object Plot shapely object
''' """
if isinstance(shape, Iterable) and type(shape) != MultiLineString: if isinstance(shape, Iterable) and type(shape) != MultiLineString:
for shape_ in shape: for shape_ in shape:
plot_shape(shape_, ax, vsketch=vsketch, **kwargs) plot_shape(shape_, ax, vsketch=vsketch, **kwargs)
@@ -58,30 +60,31 @@ def plot_shape(shape, ax, vsketch = None, **kwargs):
if vsketch is None: if vsketch is None:
ax.add_patch(PolygonPatch(shape, **kwargs)) ax.add_patch(PolygonPatch(shape, **kwargs))
else: else:
if ('draw' not in kwargs) or kwargs['draw']: if ("draw" not in kwargs) or kwargs["draw"]:
if 'stroke' in kwargs: if "stroke" in kwargs:
vsketch.stroke(kwargs['stroke']) vsketch.stroke(kwargs["stroke"])
else: else:
vsketch.stroke(1) vsketch.stroke(1)
if 'penWidth' in kwargs: if "penWidth" in kwargs:
vsketch.penWidth(kwargs['penWidth']) vsketch.penWidth(kwargs["penWidth"])
else: else:
vsketch.penWidth(0.3) vsketch.penWidth(0.3)
if 'fill' in kwargs: if "fill" in kwargs:
vsketch.fill(kwargs['fill']) vsketch.fill(kwargs["fill"])
else: else:
vsketch.noFill() vsketch.noFill()
vsketch.geometry(shape) vsketch.geometry(shape)
# Plot a collection of shapes # Plot a collection of shapes
def plot_shapes(shapes, ax, vsketch=None, palette=None, **kwargs): def plot_shapes(shapes, ax, vsketch=None, palette=None, **kwargs):
''' """
Plot collection of shapely objects (optionally, use a color palette) Plot collection of shapely objects (optionally, use a color palette)
''' """
if not isinstance(shapes, Iterable): if not isinstance(shapes, Iterable):
shapes = [shapes] shapes = [shapes]
@@ -91,16 +94,18 @@ def plot_shapes(shapes, ax, vsketch = None, palette = None, **kwargs):
else: else:
plot_shape(shape, ax, vsketch=vsketch, fc=choice(palette), **kwargs) plot_shape(shape, ax, vsketch=vsketch, fc=choice(palette), **kwargs)
# Parse query (by coordinates, OSMId or name) # Parse query (by coordinates, OSMId or name)
def parse_query(query): def parse_query(query):
if isinstance(query, GeoDataFrame): if isinstance(query, GeoDataFrame):
return 'polygon' return "polygon"
elif isinstance(query, tuple): elif isinstance(query, tuple):
return 'coordinates' return "coordinates"
elif re.match('''[A-Z][0-9]+''', query): elif re.match("""[A-Z][0-9]+""", query):
return 'osmid' return "osmid"
else: else:
return 'address' return "address"
# Apply transformation (translation & scale) to layers # Apply transformation (translation & scale) to layers
def transform(layers, x, y, scale_x, scale_y, rotation): def transform(layers, x, y, scale_x, scale_y, rotation):
@@ -118,9 +123,11 @@ def transform(layers, x, y, scale_x, scale_y, rotation):
layers = dict(zip(k, v)) layers = dict(zip(k, v))
return layers return layers
def draw_text(ax, text, x, y, **kwargs): def draw_text(ax, text, x, y, **kwargs):
ax.text(x, y, text, **kwargs) ax.text(x, y, text, **kwargs)
# Plot # Plot
def plot( def plot(
# Address # Address
@@ -132,24 +139,30 @@ def plot(
# Radius (in case of circular plot) # Radius (in case of circular plot)
radius=None, radius=None,
# Which layers to plot # Which layers to plot
layers = {'perimeter': {}}, layers={"perimeter": {}},
# Drawing params for each layer (matplotlib params such as 'fc', 'ec', 'fill', etc.) # Drawing params for each layer (matplotlib params such as 'fc', 'ec', 'fill', etc.)
drawing_kwargs={}, drawing_kwargs={},
# OSM Caption parameters # OSM Caption parameters
osm_credit={}, osm_credit={},
# Figure parameters # Figure parameters
figsize = (10, 10), ax = None, title = None, figsize=(10, 10),
ax=None,
title=None,
# Vsketch parameters # Vsketch parameters
vsketch=None, vsketch=None,
# Transform (translation & scale) params # Transform (translation & scale) params
x = None, y = None, scale_x = None, scale_y = None, rotation = None, x=None,
y=None,
scale_x=None,
scale_y=None,
rotation=None,
): ):
# Interpret query # Interpret query
query_mode = parse_query(query) query_mode = parse_query(query)
# Save maximum dilation for later use # Save maximum dilation for later use
dilations = [kwargs['dilate'] for kwargs in layers.values() if 'dilate' in kwargs] dilations = [kwargs["dilate"] for kwargs in layers.values() if "dilate" in kwargs]
max_dilation = max(dilations) if len(dilations) > 0 else 0 max_dilation = max(dilations) if len(dilations) > 0 else 0
#################### ####################
@@ -164,20 +177,20 @@ def plot(
# Define base kwargs # Define base kwargs
if radius: if radius:
base_kwargs = { base_kwargs = {
'point': query if query_mode == 'coordinates' else ox.geocode(query), "point": query if query_mode == "coordinates" else ox.geocode(query),
'radius': radius "radius": radius,
} }
else: else:
base_kwargs = { base_kwargs = {
'perimeter': query if query_mode == 'polygon' else get_perimeter(query, by_osmid = query_mode == 'osmid') "perimeter": query
if query_mode == "polygon"
else get_perimeter(query, by_osmid=query_mode == "osmid")
} }
# Fetch layers # Fetch layers
layers = { layers = {
layer: get_layer( layer: get_layer(
layer, layer, **base_kwargs, **(kwargs if type(kwargs) == dict else {})
**base_kwargs,
**(kwargs if type(kwargs) == dict else {})
) )
for layer, kwargs in layers.items() for layer, kwargs in layers.items()
} }
@@ -196,27 +209,21 @@ def plot(
# Matplot-specific stuff (only run if vsketch mode isn't activated) # Matplot-specific stuff (only run if vsketch mode isn't activated)
if vsketch is None: if vsketch is None:
# Ajust axis # Ajust axis
ax.axis('off') ax.axis("off")
ax.axis('equal') ax.axis("equal")
ax.autoscale() ax.autoscale()
# Plot background # Plot background
if 'background' in drawing_kwargs: if "background" in drawing_kwargs:
xmin, ymin, xmax, ymax = layers['perimeter'].bounds geom = scale(box(*layers["perimeter"].bounds), 2, 2)
geom = scale(Polygon([
(xmin, ymin),
(xmin, ymax),
(xmax, ymax),
(xmax, ymin)
]), 2, 2)
if vsketch is None: if vsketch is None:
ax.add_patch(PolygonPatch(geom, **drawing_kwargs['background'])) ax.add_patch(PolygonPatch(geom, **drawing_kwargs["background"]))
else: else:
vsketch.geometry(geom) vsketch.geometry(geom)
# Adjust bounds # Adjust bounds
xmin, ymin, xmax, ymax = layers['perimeter'].buffer(max_dilation).bounds xmin, ymin, xmax, ymax = layers["perimeter"].buffer(max_dilation).bounds
dx, dy = xmax - xmin, ymax - ymin dx, dy = xmax - xmin, ymax - ymin
if vsketch is None: if vsketch is None:
ax.set_xlim(xmin, xmax) ax.set_xlim(xmin, xmax)
@@ -225,27 +232,58 @@ def plot(
# Draw layers # Draw layers
for layer, shapes in layers.items(): for layer, shapes in layers.items():
kwargs = drawing_kwargs[layer] if layer in drawing_kwargs else {} kwargs = drawing_kwargs[layer] if layer in drawing_kwargs else {}
if 'hatch_c' in kwargs: if "hatch_c" in kwargs:
# Draw hatched shape # Draw hatched shape
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,
lw=0,
ec=kwargs["hatch_c"],
**{k: v for k, v in kwargs.items() if k not in ["lw", "ec", "hatch_c"]},
)
# Draw shape contour only # Draw shape contour only
plot_shapes(shapes, ax, vsketch = vsketch, fill = False, **{k:v for k,v in kwargs.items() if k not in ['hatch_c', 'hatch', 'fill']}) 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: else:
# Draw shape normally # Draw shape normally
plot_shapes(shapes, ax, vsketch=vsketch, **kwargs) plot_shapes(shapes, ax, vsketch=vsketch, **kwargs)
if ((isinstance(osm_credit, dict)) or (osm_credit is True)) and (vsketch is None): if ((isinstance(osm_credit, dict)) or (osm_credit is True)) and (vsketch is None):
x, y = figsize x, y = figsize
d = .8*(x**2+y**2)**.5 d = 0.8 * (x ** 2 + y ** 2) ** 0.5
draw_text( draw_text(
ax, ax,
(osm_credit['text'] if 'text' in osm_credit else 'data © OpenStreetMap contributors\ngithub.com/marceloprates/prettymaps'), (
x = xmin + (osm_credit['x']*dx if 'x' in osm_credit else 0), osm_credit["text"]
y = ymax - 4*d - (osm_credit['y']*dy if 'y' in osm_credit else 0), if "text" in osm_credit
fontfamily = (osm_credit['fontfamily'] if 'fontfamily' in osm_credit else 'Ubuntu Mono'), else "data © OpenStreetMap contributors\ngithub.com/marceloprates/prettymaps"
fontsize = (osm_credit['fontsize']*d if 'fontsize' in osm_credit else d), ),
zorder = (osm_credit['zorder'] if 'zorder' in osm_credit else len(layers)+1), x=xmin + (osm_credit["x"] * dx if "x" in osm_credit else 0),
**{k:v for k,v in osm_credit.items() if k not in ['text', 'x', 'y', 'fontfamily', 'fontsize', 'zorder']} y=ymax - 4 * d - (osm_credit["y"] * dy if "y" in osm_credit else 0),
fontfamily=(
osm_credit["fontfamily"]
if "fontfamily" in osm_credit
else "Ubuntu Mono"
),
fontsize=(osm_credit["fontsize"] * d if "fontsize" in osm_credit else d),
zorder=(
osm_credit["zorder"] if "zorder" in osm_credit else len(layers) + 1
),
**{
k: v
for k, v in osm_credit.items()
if k not in ["text", "x", "y", "fontfamily", "fontsize", "zorder"]
},
) )
# Return perimeter # Return perimeter

View File

@@ -11,21 +11,32 @@ from geopandas import GeoDataFrame, read_file
# Compute circular or square boundary given point, radius and crs # Compute circular or square boundary given point, radius and crs
def get_boundary(point, radius, crs, circle=True, dilate=0): def get_boundary(point, radius, crs, circle=True, dilate=0):
if circle: if circle:
return ox.project_gdf( return (
GeoDataFrame(geometry = [Point(point[::-1])], crs = crs) ox.project_gdf(GeoDataFrame(geometry=[Point(point[::-1])], crs=crs))
).geometry[0].buffer(radius) .geometry[0]
.buffer(radius)
)
else: else:
x, y = np.stack(ox.project_gdf( x, y = np.stack(
GeoDataFrame(geometry = [Point(point[::-1])], crs = crs) ox.project_gdf(GeoDataFrame(geometry=[Point(point[::-1])], crs=crs))
).geometry[0].xy) .geometry[0]
.xy
)
r = radius r = radius
return Polygon([ return Polygon(
(x-r, y-r), (x+r, y-r), (x+r, y+r), (x-r, y+r) [(x - r, y - r), (x + r, y - r), (x + r, y + r), (x - r, y + r)]
]).buffer(dilate) ).buffer(dilate)
# Get perimeter # Get perimeter
def get_perimeter(query, by_osmid=False, **kwargs): 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()}) return ox.geocode_to_gdf(
query,
by_osmid=by_osmid,
**kwargs,
**{x: kwargs[x] for x in ["circle", "dilate"] if x in kwargs.keys()}
)
# Get coastline # Get coastline
def get_coast(perimeter = None, point = None, radius = None, perimeter_tolerance = 0, union = True, buffer = 0, circle = True, dilate = 0, file_location = None): def get_coast(perimeter = None, point = None, radius = None, perimeter_tolerance = 0, union = True, buffer = 0, circle = True, dilate = 0, file_location = None):
@@ -68,20 +79,37 @@ def get_coast(perimeter = None, point = None, radius = None, perimeter_tolerance
return geometries return geometries
# Get geometries # Get geometries
def get_geometries(perimeter = None, point = None, radius = None, tags = {}, perimeter_tolerance = 0, union = True, circle = True, dilate = 0): def get_geometries(
perimeter=None,
point=None,
radius=None,
tags={},
perimeter_tolerance=0,
union=True,
circle=True,
dilate=0,
):
if perimeter is not None: if perimeter is not None:
# Boundary defined by polygon (perimeter) # Boundary defined by polygon (perimeter)
geometries = ox.geometries_from_polygon( geometries = ox.geometries_from_polygon(
unary_union(perimeter.geometry).buffer(perimeter_tolerance) if perimeter_tolerance > 0 else unary_union(perimeter.geometry), unary_union(perimeter.geometry).buffer(perimeter_tolerance)
tags = {tags: True} if type(tags) == str else tags 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) perimeter = unary_union(ox.project_gdf(perimeter).geometry)
elif (point is not None) and (radius is not None): elif (point is not None) and (radius is not None):
# Boundary defined by circle with radius 'radius' around point # 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) geometries = ox.geometries_from_point(
perimeter = get_boundary(point, radius, geometries.crs, circle = circle, dilate = dilate) 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 # Project GDF
if len(geometries) > 0: if len(geometries) > 0:
@@ -91,34 +119,70 @@ def get_geometries(perimeter = None, point = None, radius = None, tags = {}, per
geometries = geometries.intersection(perimeter) geometries = geometries.intersection(perimeter)
if union: if union:
geometries = unary_union(reduce(lambda x,y: x+y, [ geometries = unary_union(
reduce(
lambda x, y: x + y,
[
[x] if type(x) == Polygon else list(x) [x] if type(x) == Polygon else list(x)
for x in geometries if type(x) in [Polygon, MultiPolygon] for x in geometries
], [])) if type(x) in [Polygon, MultiPolygon]
],
[],
)
)
else: else:
geometries = MultiPolygon(reduce(lambda x,y: x+y, [ geometries = MultiPolygon(
reduce(
lambda x, y: x + y,
[
[x] if type(x) == Polygon else list(x) [x] if type(x) == Polygon else list(x)
for x in geometries if type(x) in [Polygon, MultiPolygon] for x in geometries
], [])) if type(x) in [Polygon, MultiPolygon]
],
[],
)
)
return geometries return geometries
# Get streets
def get_streets(perimeter = None, point = None, radius = None, layer = 'streets', width = 6, custom_filter = None, buffer = 0, retain_all = False, circle = True, dilate = 0):
if layer == 'streets': # Get streets
layer = 'highway' def get_streets(
perimeter=None,
point=None,
radius=None,
layer="streets",
width=6,
custom_filter=None,
buffer=0,
retain_all=False,
circle=True,
dilate=0,
):
if layer == "streets":
layer = "highway"
# Boundary defined by polygon (perimeter) # Boundary defined by polygon (perimeter)
if perimeter is not None: if perimeter is not None:
# Fetch streets data, project & convert to GDF # Fetch streets data, project & convert to GDF
streets = ox.graph_from_polygon(unary_union(perimeter.geometry).buffer(buffer) if buffer > 0 else unary_union(perimeter.geometry), custom_filter = custom_filter) streets = ox.graph_from_polygon(
unary_union(perimeter.geometry).buffer(buffer)
if buffer > 0
else unary_union(perimeter.geometry),
custom_filter=custom_filter,
)
streets = ox.project_graph(streets) streets = ox.project_graph(streets)
streets = ox.graph_to_gdfs(streets, nodes=False) streets = ox.graph_to_gdfs(streets, nodes=False)
# Boundary defined by polygon (perimeter) # Boundary defined by polygon (perimeter)
elif (point is not None) and (radius is not None): elif (point is not None) and (radius is not None):
# Fetch streets data, save CRS & project # Fetch streets data, save CRS & project
streets = ox.graph_from_point(point, dist = radius+dilate+buffer, retain_all = retain_all, custom_filter = custom_filter) streets = ox.graph_from_point(
point,
dist=radius + dilate + buffer,
retain_all=retain_all,
custom_filter=custom_filter,
)
crs = ox.graph_to_gdfs(streets, nodes=False).crs crs = ox.graph_to_gdfs(streets, nodes=False).crs
streets = ox.project_graph(streets) streets = ox.project_graph(streets)
# Compute perimeter from point & CRS # Compute perimeter from point & CRS
@@ -130,36 +194,53 @@ def get_streets(perimeter = None, point = None, radius = None, layer = 'streets'
streets = streets[~streets.geometry.is_empty] streets = streets[~streets.geometry.is_empty]
if type(width) == dict: if type(width) == dict:
streets = unary_union([ streets = unary_union(
[
# Dilate streets of each highway type == 'highway' using width 'w' # Dilate streets of each highway type == 'highway' using width 'w'
MultiLineString( MultiLineString(
streets[(streets[layer] == highway) & (streets.geometry.type == 'LineString')].geometry.tolist() + streets[
list(reduce(lambda x, y: x+y, [ (streets[layer] == highway)
& (streets.geometry.type == "LineString")
].geometry.tolist()
+ list(
reduce(
lambda x, y: x + y,
[
list(lines) list(lines)
for lines in streets[(streets[layer] == highway) & (streets.geometry.type == 'MultiLineString')].geometry for lines in streets[
], [])) (streets[layer] == highway)
& (streets.geometry.type == "MultiLineString")
].geometry
],
[],
)
)
).buffer(w) ).buffer(w)
for highway, w in width.items() for highway, w in width.items()
]) ]
)
else: else:
# Dilate all streets by same amount 'width' # Dilate all streets by same amount 'width'
streets = MultiLineString(streets.geometry.tolist()).buffer(width) streets = MultiLineString(streets.geometry.tolist()).buffer(width)
return streets return streets
# Get any layer # Get any layer
def get_layer(layer, **kwargs): def get_layer(layer, **kwargs):
# Fetch perimeter # Fetch perimeter
if layer == 'perimeter': if layer == "perimeter":
# If perimeter is already provided: # If perimeter is already provided:
if 'perimeter' in kwargs: if "perimeter" in kwargs:
return unary_union(ox.project_gdf(kwargs['perimeter']).geometry) return unary_union(ox.project_gdf(kwargs["perimeter"]).geometry)
# If point and radius are provided: # If point and radius are provided:
elif 'point' in kwargs and 'radius' in kwargs: elif "point" in kwargs and "radius" in kwargs:
crs = "EPSG:4326" crs = "EPSG:4326"
perimeter = get_boundary( perimeter = get_boundary(
kwargs['point'], kwargs['radius'], crs, kwargs["point"],
**{x: kwargs[x] for x in ['circle', 'dilate'] if x in kwargs.keys()} kwargs["radius"],
crs,
**{x: kwargs[x] for x in ["circle", "dilate"] if x in kwargs.keys()}
) )
return perimeter return perimeter
else: else:

View File

@@ -4,18 +4,18 @@ from pathlib import Path
parent_dir = Path(__file__).resolve().parent parent_dir = Path(__file__).resolve().parent
setup( setup(
name='prettymaps', name="prettymaps",
version='1.0.0', version="1.0.0",
description='A simple python library to draw pretty maps from OpenStreetMap data', description="A simple python library to draw pretty maps from OpenStreetMap data",
long_description=parent_dir.joinpath("README.md").read_text(), long_description=parent_dir.joinpath("README.md").read_text(),
long_description_content_type="text/markdown", long_description_content_type="text/markdown",
url='https://github.com/marceloprates/prettymaps', url="https://github.com/marceloprates/prettymaps",
author='Marcelo Prates', author="Marcelo Prates",
author_email='marceloorp@gmail.com', author_email="marceloorp@gmail.com",
license='MIT License', license="MIT License",
packages=find_packages(exclude=("assets", "notebooks", "prints", "script")), packages=find_packages(exclude=("assets", "notebooks", "prints", "script")),
install_requires=parent_dir.joinpath("requirements.txt").read_text().splitlines(), install_requires=parent_dir.joinpath("requirements.txt").read_text().splitlines(),
classifiers=[ classifiers=[
'Intended Audience :: Science/Research', "Intended Audience :: Science/Research",
], ],
) )