mappersons/mappersons.py
2023-05-21 14:58:14 +02:00

151 lines
5.3 KiB
Python

import math
import json
import yaml
from PIL import Image, ImageDraw, ImageFilter
from pathlib import Path
from datetime import datetime, timedelta
def circular_mask(img):
center_x = int(img.width / 2)
center_y = int(img.height / 2)
mask = Image.new('L', img.size, 0)
draw = ImageDraw.Draw(mask)
draw.ellipse((2, 2 ,img.width-3,img.height-3),fill=255)
img.putalpha(mask.filter(ImageFilter.GaussianBlur(radius=1)))
return img
def arrange_images_in_circle(images, spacing=5, min_radius=0):
d = images[0].size[0]
dis = d + spacing
theta = 2*math.pi / len(images)
radius = max(dis/2/math.sin(theta/2), min_radius)
imgWidth, imgHeight = (int(d+radius*2), int(d+radius*2))
masterImage = Image.new("RGBA", (imgWidth, imgHeight), (255,255,255,0))
#we want the circle to be as large as possible.
#but the circle shouldn't extend all the way to the edge of the image.
#If we do that, then when we paste images onto the circle, those images will partially fall over the edge.
#so we reduce the diameter of the circle by the width/height of the widest/tallest image.
circleCenterX = imgWidth / 2
circleCenterY = imgHeight / 2
for i, curImg in enumerate(images):
angle = i * theta
dx = int(radius * math.cos(angle))
dy = int(radius * math.sin(angle))
#dx and dy give the coordinates of where the center of our images would go.
#so we must subtract half the height/width of the image to find where their top-left corners should be.
pos = (
int(circleCenterX + dx - curImg.size[0]/2),
int(circleCenterY + dy - curImg.size[1]/2)
)
curMask = curImg.split()[-1]
masterImage.paste(curImg, pos, curMask)
return masterImage
def create_circle(image_paths):
# Resize all images to 64x64 pixels
shrunk_images = [Image.open(path).resize((64, 64)) for path in image_paths]
# Cut out a circle from each image
circular_images = [circular_mask(img) for img in shrunk_images]
if len(circular_images) == 1:
return circular_images[0]
if len(circular_images) < 7:
output_img = arrange_images_in_circle(circular_images)
ow, oh = output_img.size
else:
inner_img = arrange_images_in_circle(circular_images[:4])
iw, ih = inner_img.size
mask = inner_img.split()[-1]
dr = circular_images[0].size[0]//2 + iw//2
output_img = arrange_images_in_circle(circular_images[4:],min_radius=dr)
ow, oh = output_img.size
output_img.paste(inner_img,((ow-iw)//2,(oh-ih)//2),mask)
draw = ImageDraw.Draw(output_img)
dr = 3
draw.ellipse((ow//2-dr, oh//2-dr, ow//2+dr, oh//2+dr),fill=(255,0,0),outline=False)
return output_img
def generateWeek(week, persons, locations, output_path=Path('.')):
loc = {}
for person in persons:
if week in person['weeks'].keys():
cur_name = person['name']
cur_image = person['image']
cur_base = person['base']
for task in person['weeks'][week]:
cur_loc = task['location']
cur_tasks = task['tasks']
if cur_loc not in loc.keys():
loc[cur_loc] = {}
if cur_name not in loc[cur_loc].keys():
loc[cur_loc][cur_name] = {
'image': cur_image,
'base': cur_base,
'tasks': []
}
loc[cur_loc][cur_name]['tasks'] += cur_tasks
week_json = []
w,y = week.split('/')
json_file = "cw_%s_%s.json" % (w,y)
for cur_loc, cur_persons in loc.items():
png_file = "%s_%s_%s.png" % (cur_loc.lower().replace(' ','_'),w,y)
image_paths = [("persons/%s" % x['image']) for n,x in cur_persons.items()]
img = create_circle(image_paths)
img.save(output_path / Path(png_file))
bases = []
base_names = []
popup = "<h2>%s</h2>" % cur_loc
for cur_per, cur_info in cur_persons.items():
popup += "<h3>%s</h3>" % cur_per
popup += "</br>".join(cur_info['tasks'])
if cur_info['base'] != cur_loc:
base_names.append(cur_info['base'])
base_names = list(set(base_names))
for base in base_names:
l = [x for x in locations if x['name'] == base][0]
bases.append({
"city": base,
"lat": l['lat'],
"lng": l['lng'],
})
l = [x for x in locations if x['name'] == cur_loc][0]
week_json.append({
"city": cur_loc,
"lat": l['lat'],
"lng": l['lng'],
"icon": png_file,
"anchor": [x//2 for x in img.size],
"popup": popup,
"bases": bases
})
with open(output_path / Path(json_file),"w") as j:
json.dump(week_json, j)
if __name__ == "__main__":
before = 4
after = 12
with open("persons.yml","r") as fp:
persons = yaml.safe_load(fp)
with open("locations.yml","r") as fp:
locations = yaml.safe_load(fp)
start = datetime.now() -timedelta(weeks=before)
for i in range(before+1+after):
cur_date = start + timedelta(weeks=i)
generateWeek(cur_date.strftime("%V/%G"), persons, locations)