diff --git a/extract-and-generate b/extract-and-generate index 266f972..002eaf8 100755 --- a/extract-and-generate +++ b/extract-and-generate @@ -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" diff --git a/layer2img.py b/layer2img.py new file mode 100644 index 0000000..c27d1e3 --- /dev/null +++ b/layer2img.py @@ -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='') + 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() diff --git a/mj-msc.tex b/mj-msc.tex index 43ca233..1c2aa42 100644 --- a/mj-msc.tex +++ b/mj-msc.tex @@ -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}