Arbeitsverzeichnis für Pendel-Redaktion. Beinhaltet Skripte sowie alle Pendel-Inhalte.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

263 lines
7.2 KiB

import argparse
import csv
import os.path as path
import re
import constants.fields as F
importdir = 'images'
datafile = path.join(importdir, 'gps.csv')
args = None
# Based on imported CSV and added with attributes:
# data with appended attributes as list of lists.
# Items ---> See constants/fields.py <---
# - ID
# - 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 tab-separated CSV file and returns a list of dictionaries, one per row
in the full data item size. Placeholder for unfilled fields is None.
Fields:
0: ID (integer), ascending starting with '1' <-- not in CSV
1: image file name (string)
2: latitude and longitude (floats), separated by space.
3. title (string)
4. description (string)
The items must be sorted chronologically. 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}")
id = 0
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]}'
id += 1
# Order and size must match constants.fields
# values = [id, row[0], float(geo[0]), float(geo[1]), row[2], row[3] ]
values = []
values.insert(F.ID, id)
values.insert(F.FILE, row[0])
values.insert(F.LON, float(geo[0]))
values.insert(F.LAT, float(geo[1]))
values.insert(F.TITLE, row[2])
values.insert(F.DESCRIPTION, row[3])
values.insert(F.POSX, None)
values.insert(F.POSY, None)
data.append(values)
logvv(f"Processed item : {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][F.LON]
latmin = latmax = data[0][F.LAT]
for entry in data[1:]:
if entry[F.LON] < lonmin:
lonmin = entry[F.LON]
if entry[F.LON] > lonmax:
lonmax = entry[F.LON]
if entry[F.LAT] < latmin:
latmin = entry[F.LAT]
if entry[F.LAT] > latmax:
latmax = entry[F.LAT]
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 create_playbook(data):
"""
Returns list of dict [delta1, delta2, ...] with changes (in both direections) for every single tour.
The snapshot holds the complete canvas for the actual step. The playbook contains exclusively
changed values (delta).
Rules to process:
- Loop over all n data items: start with the first (oldest) item (0)
- Copy previous snapshot (to find changes in a later step)
- Run recursively over the the snapshot items: Find an existing item at the position
of the actual image and change its position by the displacement rule.
- Place the image of the actual item (a) at its default position in the snapshot
- Create a item for the following image (a+1) and hide it (for scrolling backwards).
- Store all changes betweeen snapshot and previous snapshot in a new delta (a) and append it
to the return dict.
In error case, None will be returned.
"""
snapshot = prev_snapshot = []
ret = []
for a in data:
prev_snapshot = snapshot
(x, y) = (a[F.POSX], a[F.POSY])
snapshot = shifting_snapshot_items(x,y, snapshot)
return ret
def shifting_snapshot_items(x, y, snapshot):
"""
Recursive change item chain starting with item at position x,y.
Returns updated snapshot
"""
goon = True
(actx, acty) = (x, y)
ret = snapshot
while goon:
goon = False
for i, item in enumerate(ret):
if (item[F.POSX], item[F.POSY]) == (actx, acty):
# ret[i][F.POSX], ret[i][F.POSY] =
goon = True
return ret
def shifting(tup, matrixdims):
"""Strategy to shift. Above and on the horizontal middle line, the position
will be shifted to the top. Positions below the horizontal middle line,
the position will be shifted in bottom direction.
Returns the offset as (x, y) tuple, not the new position itself.
"""
def calculate_orig_pos():
global data
geo_extrema = find_geo_extrema(data)
data = append_tile_pos(data, geo_extrema, matrixdims)
playbook = create_playbook(data)
def main():
global args
args=parse_args()
read_importdata()
calculate_orig_pos()
log("Finished.")
if __name__ == "__main__":
main()