def getYearSounds(spriteData, userOptions={}): global items cfg = { "dimension": 2 # use z-axis } yearCol = "year" if yearCol not in items[0]: print("Could not find column %s in items, please add this column to metadata cols with 'type' = 'int'" % yearCol) sys.exit() sprites = spriteCount = None if spriteData is not None: sprites = spriteData["sprites"] spriteCount = len(sprites) years = [item[yearCol] for item in items] minYear = min(years) maxYear = max(years) + 1 nUnit = 1.0 / (maxYear-minYear) sounds = [] for i in range(maxYear-minYear): x = y = 0.5 year = minYear + i z = mu.norm(year, (minYear, maxYear)) + nUnit*0.5 # center sounds += [round(x, PRECISION), round(y, PRECISION), round(z, PRECISION)] if sprites is not None: spriteIndex = mu.roundInt(1.0 * z * (spriteCount-1)) sprite = sprites[spriteIndex] sounds += [sprite["start"], sprite["dur"]] return (cfg, sounds)
def getGeographyBarsLayout(userOptions={}): global items cfg = { "layout": "bars" } latCol = "lat" lonCol = "lon" if latCol not in items[0] or lonCol not in items[0]: print("`latitudeColumn` and `latitudeColumn` need to be set in config yml to support geographyBars layout") return (False, False) # create unique key for lat lon for i, item in enumerate(items): items[i]["lonLatKey"] = (mu.roundInt(item[lonCol]*PRECISION), mu.roundInt(item[latCol]*PRECISION)) latRange = (90.0, -90.0) lonRange = (-180.0, 180.0) dimensions = 3 groups = lu.groupList(items, "lonLatKey") # group by lat lon counts = [group["count"] for group in groups] minCount, maxCount = (min(counts), max(counts)) # assign position values values = np.zeros(len(items) * dimensions) for group in groups: y = mu.norm(group["count"], (minCount, maxCount)) y = mu.lerp((0.01, 1.0), y) for item in group["items"]: itemIndex = item["index"] x = 1.0 - mu.norm(item[lonCol], lonRange) z = 1.0 - mu.norm(item[latCol], latRange) itemY = y # a bit of a hack to ensure highighted items are visible if itemHasStory(item): itemY = y + 1.05 values[itemIndex*dimensions] = round(x, PRECISION) values[itemIndex*dimensions+1] = round(itemY, PRECISION) values[itemIndex*dimensions+2] = round(z, PRECISION) values = values.tolist() return (cfg, values)
def getCategoryTimelineHotspot(view, content): global items global sets year = None if "year" in content: year = content["year"] elif "visibleTimeRange" in content: year = mu.roundInt(mu.lerp(tuple(content["visibleTimeRange"]), 0.5)) if year is None: print("Need to set year or time range in content") return None yearCol = "year" if yearCol not in items[0]: print( "Could not find column %s in items, please add this column to metadata cols with 'type' = 'int'" % yearCol) return None years = [item[yearCol] for item in items] minYear = min(years) maxYear = max(years) + 1 nUnit = 1.0 / (maxYear - minYear) if "category" not in sets: print("Could not find column 'category' in sets") return None categories = sets["category"] if "category" not in content: print("Could not find column 'category' in content") return None if content["category"] not in categories: print("Could not find %s in categories" % content["category"]) return None categoryCount = len(categories) categoryIndex = categories.index(content["category"]) # place at in the center of the year z = mu.norm(year, (minYear, maxYear)) + nUnit * 0.5 # place at center of region x = 1.0 - 1.0 * categoryIndex / (categoryCount - 1) # place at top of bounds y = 0.0 return { "x": round(x, PRECISION), "y": round(y, PRECISION), "z": round(z, PRECISION) }
def getGeographyBarsLayout(userOptions={}): global items cfg = {"layout": "bars"} latCol = "lat" lonCol = "lon" if latCol not in items[0] or lonCol not in items[0]: print( "Could not find column (%s, %s) in items, please add these columns to metadata cols with 'type' = 'float'" % (lonCol, latCol)) sys.exit() # create unique key for lat lon for i, item in enumerate(items): items[i]["lonLatKey"] = (mu.roundInt(item[lonCol] * PRECISION), mu.roundInt(item[latCol] * PRECISION)) latRange = (90.0, -90.0) lonRange = (-180.0, 180.0) dimensions = 3 groups = lu.groupList(items, "lonLatKey") # group by lat lon counts = [group["count"] for group in groups] minCount, maxCount = (min(counts), max(counts)) # assign position values values = np.zeros(len(items) * dimensions) for group in groups: y = mu.norm(group["count"], (minCount, maxCount)) y = mu.lerp((0.01, 1.0), y) for item in group["items"]: itemIndex = item["index"] x = 1.0 - mu.norm(item[lonCol], lonRange) z = 1.0 - mu.norm(item[latCol], latRange) values[itemIndex * dimensions] = round(x, PRECISION) values[itemIndex * dimensions + 1] = round(y, PRECISION) values[itemIndex * dimensions + 2] = round(z, PRECISION) values = values.tolist() return (cfg, values)
def makeCategoryTrackOverlay(filename, width=2048, lineThickness=4): global categories categoryCount = len(categories) if categoryCount < 1: return height = width baseImg = Image.new(mode="RGB", size=(width, height), color=(255, 255, 255)) draw = ImageDraw.Draw(baseImg) barWidth = (width - (categoryCount-1) * lineThickness) / categoryCount for i, cat in enumerate(categories): x0 = mu.roundInt(i * (barWidth + lineThickness)) x1 = mu.roundInt(x0 + barWidth) y0 = 0 y1 = height if i >= (categoryCount-1): x1 = width draw.rectangle([x0, y0, x1, y1], fill=cat["color"]) baseImg.save(filename)
def containImage(img, w, h, resampleType="default", bgcolor=[0,0,0]): # Lanczos = good for downsizing resampleType = Image.LANCZOS if resampleType=="default" else resampleType vw, vh = img.size if vw == w and vh == h: return img # create a base image w = mu.roundInt(w) h = mu.roundInt(h) if img.mode=="RGBA" and len(bgcolor)==3: bgcolor.append(0) baseImg = Image.new(mode=img.mode, size=(w, h), color=tuple(bgcolor)) ratio = 1.0 * w / h vratio = 1.0 * vw / vh # first, resize image newW = w newH = h pasteX = 0 pasteY = 0 if vratio > ratio: newH = w / vratio pasteY = mu.roundInt((h-newH) * 0.5) else: newW = h * vratio pasteX = mu.roundInt((w-newW) * 0.5) try: resized = img.resize((mu.roundInt(newW), mu.roundInt(newH)), resample=resampleType) # then paste the resized image baseImg.paste(resized, (pasteX, pasteY)) except OSError: print("Error in resizing") baseImg = None return baseImg
cumulative = match["collection"] for division in divisions: if division in match and match[division] != "": breakdown.append(match[division]) if len(breakdown) > 0: breakdown.append(cumulative-sum(breakdown)) annualData[i]["cumulative"] = cumulative annualData[i]["breakdown"] = breakdown # fill in the remainder data model = annualData[-1] for i, d in enumerate(annualData): if i > 0 and d["cumulative"] < 1: n = 1.0 * i / (len(annualData)-1) nEased = mu.ease(n, "quadIn") cumulative = mu.roundInt(nEased * model["cumulative"]) annualData[i]["cumulative"] = cumulative # add some randomness to the data for i, d in enumerate(annualData): if i > 1 and i < len(annualData)-1: prev = annualData[i-1] delta = d["cumulative"] - prev["cumulative"] if delta > 0: half = mu.roundInt(delta * 0.5) random.seed(i) rdelta = random.randint(-int(half/2), int(half/2)) annualData[i-1]["cumulative"] += rdelta annualData[i]["cumulative"] -= rdelta # ensure each year increases
# Add item slides from stories to item metadata storyItems = {} if "stories" in config: stories = config["stories"] for key, story in stories.items(): for slide in story["slides"]: if "itemId" in slide: slideMeta = slide.copy() slideMeta.pop("itemId", None) storyItems[str(slide["itemId"])] = slideMeta itemsPerFile = a.ITEMS_PER_FILE if itemsPerFile < 1: targetFiles = 1000 itemsPerFile = mu.roundInt(1.0 * itemCount / targetFiles) itemsPerFile = min(itemsPerFile, 1000) totalFiles = mu.ceilInt(1.0 * itemCount / itemsPerFile) print(f'{itemsPerFile} per file with a total of {totalFiles} files') fileItems = [] fileIndex = 0 for i, item in enumerate(items): itemOut = [] for field in itemFields: if field["column"] not in item: continue value = item[field["column"]]