layer2img.py with arrowplots

This commit is contained in:
Motiejus Jakštys 2021-05-19 22:57:46 +03:00 committed by Motiejus Jakštys
parent 3053799975
commit b92ac92c90
3 changed files with 185 additions and 6 deletions

View File

@ -5,8 +5,7 @@ fname=${1:-mj-msc-all.pdf}
echo "Extracting $fname to $dir/" && pdfdetach -saveall -o "$dir" "$fname"
echo "Generating $dir/$fname ..."
make -j $(nproc) -C "$dir" "$fname" &> "$dir/make.log" || {
echo "Generation failed. Here are the last 20 log lines:"
tail -20 "$dir/make.log"
echo "Generation failed. Log extract:" && tail -20 "$dir/make.log"
exit 1
}
echo "Opening $dir/$fname ..." && xdg-open "$dir/$fname"

181
layer2img.py Normal file
View File

@ -0,0 +1,181 @@
#!/usr/bin/python3
import argparse
import geopandas
import psycopg2
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
INCH = 25.4 # mm
BOUNDS = ('xmin', 'ymin', 'xmax', 'ymax')
GREEN, ORANGE, PURPLE = '#1b9e77', '#d95f02', '#7570b3'
PSQL_CREDS="host=127.0.0.1 dbname=osm user=osm password=osm"
def arrowplot(axes, x, y, narrs=30, dspace=0.5, direc='pos', \
hl=0.3, hw=3, c='black'):
''' narrs : Number of arrows that will be drawn along the curve
dspace : Shift the position of the arrows along the curve.
Should be between 0. and 1.
direc : can be 'pos' or 'neg' to select direction of the arrows
hl : length of the arrow head
hw : width of the arrow head
c : color of the edge and face of the arrow head
https://stackoverflow.com/questions/8247973
'''
# r is the distance spanned between pairs of points
r = [0]
for i in range(1,len(x)):
dx = x[i]-x[i-1]
dy = y[i]-y[i-1]
r.append(np.sqrt(dx*dx+dy*dy))
r = np.array(r)
# rtot is a cumulative sum of r, it's used to save time
rtot = []
for i in range(len(r)):
rtot.append(r[0:i].sum())
rtot.append(r.sum())
# based on narrs set the arrow spacing
aspace = r.sum() / narrs
if direc is 'neg':
dspace = -1.*abs(dspace)
else:
dspace = abs(dspace)
arrowData = [] # will hold tuples of x,y,theta for each arrow
arrowPos = aspace*(dspace) # current point on walk along data
# could set arrowPos to 0 if you want
# an arrow at the beginning of the curve
ndrawn = 0
rcount = 1
while arrowPos < r.sum() and ndrawn < narrs:
x1,x2 = x[rcount-1],x[rcount]
y1,y2 = y[rcount-1],y[rcount]
da = arrowPos-rtot[rcount]
theta = np.arctan2((x2-x1),(y2-y1))
ax = np.sin(theta)*da+x1
ay = np.cos(theta)*da+y1
arrowData.append((ax,ay,theta))
ndrawn += 1
arrowPos+=aspace
while arrowPos > rtot[rcount+1]:
rcount+=1
if arrowPos > rtot[-1]:
break
# could be done in above block if you want
for ax,ay,theta in arrowData:
# use aspace as a guide for size and length of things
# scaling factors were chosen by experimenting a bit
dx0 = np.sin(theta)*hl/2. + ax
dy0 = np.cos(theta)*hl/2. + ay
dx1 = -1.*np.sin(theta)*hl/2. + ax
dy1 = -1.*np.cos(theta)*hl/2. + ay
if direc is 'neg' :
ax0 = dx0
ay0 = dy0
ax1 = dx1
ay1 = dy1
else:
ax0 = dx1
ay0 = dy1
ax1 = dx0
ay1 = dy0
axes.annotate('', xy=(ax0, ay0), xycoords='data',
xytext=(ax1, ay1), textcoords='data',
arrowprops=dict( headwidth=hw, frac=1., ec=c, fc=c))
def plt_size(string):
if not string:
return None
try:
w, h = string.split("x")
return float(w) / INCH, float(h) / INCH
except Exception as e:
raise argparse.ArgumentTypeError from e
def parse_args():
parser = argparse.ArgumentParser(
description='Convert geopackage to an image')
group1 = parser.add_mutually_exclusive_group()
group1.add_argument('--group1-infile')
group1.add_argument('--group1-table')
parser.add_argument('-o', '--outfile', metavar='<file>')
parser.add_argument(
'--size', type=plt_size, help='Figure size in mm (WWxHH)')
parser.add_argument( '--clip', type=float, nargs=4, metavar=BOUNDS)
group2 = parser.add_mutually_exclusive_group()
group2.add_argument('--group2-infile', type=str)
group2.add_argument('--group2-table', type=str)
group3 = parser.add_mutually_exclusive_group()
group3.add_argument('--group3-infile', type=str)
group3.add_argument('--group3-table', type=str)
return parser.parse_args()
def read_layer(maybe_table, maybe_file):
if maybe_table:
conn = psycopg2.connect(PSQL_CREDS)
sql = "SELECT way FROM %s" % maybe_table
return geopandas.read_postgis(sql, con=conn, geom_col='way')
elif maybe_file:
return geopandas.read_file(maybe_file)
def add_lines(ax, group):
for g in group.to_dict()['way'].values():
for geom in getattr(g, 'geoms', [g]):
x, y = zip(*geom.coords)
narrs = geom.length / 16
arrowplot(ax, np.array(x), np.array(y), narrs=narrs)
def main():
args = parse_args()
group1 = read_layer(args.group1_table, args.group1_infile)
group2 = read_layer(args.group2_table, args.group2_infile)
group3 = read_layer(args.group3_table, args.group3_infile)
rc('text', usetex=True)
fig, ax = plt.subplots()
if args.size:
fig.set_size_inches(args.size)
if c := args.clip:
ax.set_xlim(left=c[0], right=c[2])
ax.set_ylim(bottom=c[1], top=c[3])
if group1 is not None:
group1.plot(ax=ax, color=PURPLE)
add_lines(ax, group1)
if group2 is not None:
group2.plot(ax=ax, color=ORANGE)
add_lines(ax, group2)
if group3 is not None:
group3.plot(ax=ax, color=GREEN)
add_lines(ax, group3)
ax.axis('off')
ax.margins(0, 0)
#fig.tight_layout(0)
if args.outfile:
fig.savefig(args.outfile, bbox_inches=0, dpi=600)
else:
plt.show()
if __name__ == '__main__':
main()

View File

@ -244,10 +244,9 @@ unexpected regressions have been added while modifying the algorithm.
We strongly believe in the ability to reproduce the results is critical for any
scientific work. To make it possible for this paper, all source files and
accompanying scripts have been attached to the PDF. To preview the code
listings and re-generate this document (also, re-generate the included
graphics), run this script (assuming name of this document is {\tt
mj-msc-all.pdf}):
accompanying scripts have been attached to the PDF. To re-generate this
document and its accompanying graphics, run this script (assuming name of
this document is {\tt mj-msc-all.pdf}):
\inputminted[fontsize=\small]{bash}{extract-and-generate}