#!/usr/bin/env python
# -*- coding: utf-8 -*-
from math import sqrt
import shapefile
from matplotlib import pyplot
from descartes import PolygonPatch
from shapely.geometry import Polygon, LineString, Point
# used to import dictionary data to shapely
from shapely.geometry import asShape
from shapely.geometry import mapping
# calculate the size of our matplotlib output
GM = (sqrt(5) - 1.0) / 2.0
W = 8.0
H = W * GM
SIZE = (W, H)
# colors for our plots as hex
GRAY = '#00b700'
BLUE = '#6699cc'
YELLOW = '#ffe680'
# functions slightly modified from Sean Gilles http://toblerity.org/shapely/
# used for drawing our results using matplotlib
def plot_coords_line(axis, object, color='#00b700'):
x, y = object.xy
ax.plot(x, y, 'o', color=color, zorder=1)
def plot_coords_lines(axis, object, color='#999999'):
for linestring in object:
x, y = linestring.xy
ax.plot(x, y, 'o', color=color, zorder=2)
def plot_line(axis, object, color='#00b700'):
x, y = object.xy
ax.plot(x, y, color=color, linewidth=3, zorder=1)
def plot_lines(axis, object, color='#00b700'):
for line in object:
x, y = line.xy
ax.plot(x, y, color=color, alpha=0.4, linewidth=1, solid_capstyle='round', zorder=2)
def set_plot_bounds(object, offset=1.0):
"""
Creates the limits for x and y axis plot
:param object: input shapely geometry
:param offset: amount of space around edge of features
:return: dictionary of x-range and y-range values for
"""
bounds = object.bounds
x_min = bounds[0]
y_min = bounds[1]
x_max = bounds[2]
y_max = bounds[3]
x_range = [x_min - offset, x_max + offset]
y_range = [y_min - offset, y_max + offset]
return {'xrange': x_range, 'yrange': y_range}
# open roads Shapefile that we want to clip with pyshp
roads_london = shapefile.Reader(r"../geodata/roads_london_3857.shp")
# open circle polygon with pyshp
clip_area = shapefile.Reader(r"../geodata/clip_area_3857.shp")
# access the geometry of the clip area circle
clip_feature = clip_area.shape()
# convert pyshp object to shapely
clip_shply = asShape(clip_feature)
# create a list of all roads features and attributes
roads_features = roads_london.shapeRecords()
# variables to hold new geometry
roads_clip_list = []
roads_shply = []
# run through each geometry, convert to shapely geom and intersect
for feature in roads_features:
roads_london_shply = asShape(feature.shape.__geo_interface__)
roads_shply.append(roads_london_shply)
roads_intersect = roads_london_shply.intersection(clip_shply)
# only export linestrings, shapely also created points
if roads_intersect.geom_type == "LineString":
roads_clip_list.append(roads_intersect)
# open writer to write our new shapefile too
pyshp_writer = shapefile.Writer()
# create new field
pyshp_writer.field("name")
# convert our shapely geometry back to pyshp, record for record
for feature in roads_clip_list:
geojson = mapping(feature)
# create empty pyshp shape
record = shapefile._Shape()
# shapeType 3 is linestring
record.shapeType = 3
record.points = geojson["coordinates"]
record.parts = [0]
pyshp_writer._shapes.append(record)
# add a list of attributes to go along with the shape
pyshp_writer.record(["empty record"])
# save to disk
pyshp_writer.save(r"../geodata/roads_clipped.shp")
# setup matplotlib figure that will display the results
fig = pyplot.figure(1, figsize=SIZE, dpi=90, facecolor="white")
# add a little more space around subplots
fig.subplots_adjust(hspace=.5)
# ###################################
# first plot
# display sample line and circle
# ###################################
# first figure upper left drawing
# 222 represents the number_rows, num_cols, subplot number
ax = fig.add_subplot(221)
# our demonstration geometries to see the details
line = LineString([(0, 1), (3, 1), (0, 0)])
polygon = Polygon(Point(1.5, 1).buffer(1))
# use of descartes to create polygon in matplotlib
# input circle and color fill and outline in blue with transparancy
patch1 = PolygonPatch(polygon, fc=BLUE, ec=BLUE, alpha=0.5, zorder=1)
# add circle to axis in figure
ax.add_patch(patch1)
# add line using our function above
plot_line(ax, line)
# draw the line nodes using our function
plot_coords_line(ax, line)
# subplot title text
ax.set_title('Input line and circle')
# define axis ranges as list [x-min, x-max]
# added 1.5 units around object so not touching the sides
x_range = [polygon.bounds[0] - 1.5, polygon.bounds[2] + 1.5]
# y-range [y-min, y-max]
y_range = [polygon.bounds[1] - 1.0, polygon.bounds[3] + 1.0]
# set the x and y axis limits
ax.set_xlim(x_range)
ax.set_ylim(y_range)
# assing the aspect ratio
ax.set_aspect(1)
# ##########################################
# second plot
# display original input circle and roads
# ##########################################
ax = fig.add_subplot(222)
# draw our original input road lines and circle
plot_lines(ax, roads_shply, color='#3C3F41')
patch2 = PolygonPatch(clip_shply, fc=BLUE, ec=BLUE, alpha=0.5, zorder=1)
ax.add_patch(patch2)
# write title of second plot
ax.set_title('Input roads and circle')
# define the area that plot will fit into plus 600m space around
x_range = set_plot_bounds(clip_shply, 600)['xrange']
y_range = set_plot_bounds(clip_shply, 600)['yrange']
ax.set_xlim(*x_range)
ax.set_ylim(*y_range)
ax.set_aspect(1)
# remove the x,y axis labels by setting empty list
ax.set_xticklabels([])
ax.set_yticklabels([])
# ###################################
# third plot
# display sample intersection
# ###################################
ax = fig.add_subplot(223)
patch2 = PolygonPatch(polygon, fc=BLUE, ec=BLUE, alpha=0.5, zorder=1)
ax.add_patch(patch2)
# run the intersection detail view
intersect_line = line.intersection(polygon)
# plot the lines and the line vertex to plot
plot_lines(ax, intersect_line, color='#3C3F41')
plot_coords_lines(ax, intersect_line, color='#3C3F41')
# write title of second plot
ax.set_title('Line intersects circle')
# define the area that plot will fit into
x_range = set_plot_bounds(polygon, 1.5)['xrange']
y_range = set_plot_bounds(polygon, 1)['yrange']
ax.set_xlim(*x_range)
ax.set_ylim(*y_range)
ax.set_aspect(1)
# ###################################
# fourth plot
# showing results of clipped roads
# ###################################
ax = fig.add_subplot(224)
# plot the lines and the line vertex to plot
plot_lines(ax, roads_clip_list, color='#3C3F41')
# write title of second plot
ax.set_title('Roads intersect circle')
# define the area that plot will fit into
x_range = set_plot_bounds(clip_shply, 200)['xrange']
y_range = set_plot_bounds(clip_shply, 200)['yrange']
ax.set_xlim(x_range)
ax.set_ylim(y_range)
ax.set_aspect(1)
# remove the x,y axis labels by setting empty list
ax.set_xticklabels([])
ax.set_yticklabels([])
# draw the plots to the screen
pyplot.show()