import argparse import csv import os.path as path import re importdir = 'images' datafile = path.join(importdir, 'gps.csv') args = None # Imported data with appended attributes as list of lists # Items: # - Image file name # - lon, lat # - title, description # - x, y: Default tile position data = None # Has message in error case error = None # How many tiles on the canvas in (x, y) matrixdims = (6, 3) def parse_args(): parser = argparse.ArgumentParser(description="Site Builder Main Program") parser.add_argument('-i', '--importdir', type=str, help=f'Path to images directory, default={importdir}', default=importdir) parser.add_argument('-d', '--datafile', type=str, help=f'Filename of CSV data file, default={datafile}', default=datafile) parser.add_argument('-v', '--verbose', action='store_true', help='Enable verbose output') parser.add_argument('-vv', '--veryverbose', action='store_true', help='Enable very verbose output') return parser.parse_args() def log(message): if args.verbose or args.veryverbose: print(message) def logvv(message): if args.veryverbose: print(message) def exit_with_error(message): print(message) exit(1) def read_csv_to_dict(csv_file): """ Reads a CSV file and returns a list of dictionaries, one per row. Assumes the first row contains headers. In error case, returns None """ global message data = [] with open(csv_file, newline='', encoding='utf-8') as f: reader = csv.reader(f, skipinitialspace=False, delimiter='\t') log(f"Read lines from {csv_file}") for row in reader: logvv(f"Processing row: {row}") if not row or len(row) < 4: message = f"Invalid row: {row}" return None if not ' ' in row[1].strip(): message = f'Invalid location: {row[1]}' return None geo = re.findall(r'[\d]*[.][\d]+', row[1]) if len(geo) != 2: message = f'Invalid loc/lon: {row[1]}' values = [ row[0], float(geo[0]), float(geo[1]), row[2], row[3] ] data.append(values) logvv(f"Read row : {values}") if len(data) == 0: message = f'Empty file: {csv_file}' return None else: log(f'Imported {len(data)} records.') return data def read_importdata(): global data if not path.exists(args.importdir): exit_with_error(f"Error: Import directory '{args.importdir}' does not exist.") if not path.exists(args.datafile): exit_with_error(f"Error: Data file '{args.datafile}' does not exist.") log(f"Import directory: {args.importdir}") log(f"Data file: {args.datafile}") data = read_csv_to_dict(args.datafile) if data is None: exit_with_error(message) log("Read successful") def find_geo_extrema(data): """ Find min and max location values for lon and lat. Returns (lonmin, lonmax, latmin, latmax) or None in error case. """ if len(data) == 0: return None lonmin = lonmax = data[0][1] latmin = latmax = data[0][2] for entry in data[1:]: if entry[1] < lonmin: lonmin = entry[1] if entry[1] > lonmax: lonmax = entry[1] if entry[2] < latmin: latmin = entry[2] if entry[2] > latmax: latmax = entry[2] return (lonmin, lonmax, latmin, latmax) def translate_pos(min, max, pos, size): """Values must be numbers.""" return round((pos - min) / (max - min) * size) def append_tile_pos(data, geo_extrema, matrixdims): """ Adds two columns with x and y position (integer) of the tile in the canvas """ ret = [] for e in data: x = translate_pos(geo_extrema[2], geo_extrema[3], e[2], matrixdims[0]) y = translate_pos(geo_extrema[0], geo_extrema[1], e[1], matrixdims[1]) e.append(x) e.append(y) ret.append(e) logvv(f'Data updated: {e}') return ret def calculate_orig_pos(): global data geo_extrema = find_geo_extrema(data) data = append_tile_pos(data, geo_extrema, matrixdims) def main(): global args args=parse_args() read_importdata() calculate_orig_pos() log("Finished.") if __name__ == "__main__": main()