151 lines
5.3 KiB
Python
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)
|