def pointToWorld(point, view): bounds = view["bounds"] camPos = view["cameraPosition"] x = mu.lerp((bounds[1], bounds[0]), point["x"]) y = camPos[1] z = mu.lerp((bounds[3], bounds[2]), point["y"]) return { "x": x, "y": y, "z": z }
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 getCountryLabels(userOptions={}): global items cfg = {} options = {"y": 0.5} options.update(userOptions) countryCol = "country" latCol = "lat" lonCol = "lon" if countryCol not in items[0]: print( "`countryColumn` needs to be set in config yml to support country labels; they will not show otherwise." ) return (None, None) 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 country labels; they will not show otherwise.") return (None, None) latRange = (90.0, -90.0) lonRange = (-180.0, 180.0) groups = lu.groupList(items, countryCol) # group by country counts = [group["count"] for group in groups] minCount, maxCount = (min(counts), max(counts)) labels = [] for group in groups: firstItem = group["items"][0] label = firstItem[countryCol] lon = firstItem[lonCol] lat = firstItem[latCol] y = options["y"] x = 1.0 - mu.norm(lon, lonRange) z = 1.0 - mu.norm(lat, latRange) # HACK: offset z slightly to acommodate size of bar w = mu.norm(group["count"], (minCount, maxCount)) w = mu.lerp((0.01, 1.0), w) # assume height is half the depth; divide by 6 for radius calculation (see geometry.js) radius = 0.5 / 6.0 * w + 0.005 z = z - radius labels += [ round(x, PRECISION), round(y, PRECISION), round(z, PRECISION), label ] return (cfg, labels)
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 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 makeSpriteFile(audioFn, dataFn, filenames, dur, matchDbValue=-9, reverb=0, quantities=None, sampleWidth=2, sampleRate=48000, channels=2): totalDuration = dur * len(filenames) if quantities is not None: totalDuration *= len(quantities) baseAudio = getBlankAudio(totalDuration, sampleWidth, sampleRate, channels) sprites = [] for i, fn in enumerate(filenames): audio = getAudio(fn, sampleWidth, sampleRate, channels) audio = matchDb(audio, matchDbValue) # normalize audio if reverb > 0: audio = applyReverb(audio, reverb) if quantities is not None: for j, q in enumerate(quantities): sectionStart = i * len(quantities) * dur + j * dur sectionBaseAudio = getBlankAudio(dur, sampleWidth, sampleRate, channels) volumeRange = (0.2, 0.8) count = q["count"] clipDur = int(1.0 * dur / q["count"]) audioClip = getAudioClip(audio, 0, clipDur, clipFadeIn=10, clipFadeOut=10) for k in range(q["count"]): p = 1.0 * k / (q["count"]-1) volume = mu.lerp((volumeRange[1], volumeRange[0]), p) qstart = k * clipDur dbAdjust = volumeToDb(volume) modifiedAudio = audioClip.apply_gain(dbAdjust) sectionBaseAudio = sectionBaseAudio.overlay(modifiedAudio, position=qstart) audioDur = len(sectionBaseAudio) # clip audio if necessary if audioDur > dur: sectionBaseAudio = sectionBaseAudio[:dur] # fade in and out sectionBaseAudio = sectionBaseAudio.fade_in(10).fade_out(20) baseAudio = baseAudio.overlay(sectionBaseAudio, position=sectionStart) sprite = { "src": os.path.basename(fn), "start": sectionStart, "dur": dur, "quantity": q["name"] } sprites.append(sprite) else: start = i*dur audioDur = len(audio) # clip audio if necessary if audioDur > dur: audio = audio[:dur] # fade in and out audio = audio.fade_in(10).fade_out(20) # paste section on base audio baseAudio = baseAudio.overlay(audio, position=start) sprite = { "src": os.path.basename(fn), "start": start, "dur": dur } sprites.append(sprite) format = os.path.basename(audioFn).split(".")[-1] baseAudio.export(audioFn, format=format) jsonOut = { "name": os.path.basename(audioFn), "sprites": sprites } io.writeJSON(dataFn, jsonOut, pretty=True) return jsonOut
# for each chunk groupChunks = [] for l in range(chunkCount): # for each dot chunkDots = [] for m in range(chunkSize): groupItemsIndex = l * chunkSize + m if groupItemsIndex >= len(groupItems): break # determine pixel color item = groupItems[groupItemsIndex] r = g = b = int( round(mu.lerp((255 - a.MIN_ALPHA, 0), item["nalpha"]))) if confidenceKey is not None: confidence = item[confidenceKey] if confidence < 1.0: if item["Acquisition Year"] >= 9999: r = g = b = 0 r = int(round(mu.lerp((255.0, r), confidence))) # determine position dotRow = int(1.0 * m / chunkColCount) dotCol = m % chunkColCount dotDrawData = { "x": dotCol * a.DOT_WIDTH + dotCol - 1, "y": dotRow * a.DOT_WIDTH + dotRow - 1, "width": a.DOT_WIDTH, "height": a.DOT_WIDTH,
def getSphereCategoryTimelineLayout(userOptions={}): global items global sets cfg = {"layout": "spheres"} categoryCol = "category" 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() if categoryCol not in sets: print( "Could not find column %s in sets, please add this column to metadata cols with 'asIndex' = true" % categoryCol) sys.exit() categorySet = sets[categoryCol] categoryCount = len(categorySet) dimensions = 3 groups = lu.groupList(items, yearCol) # group by year groups = sorted(groups, key=lambda group: group[yearCol]) years = [item[yearCol] for item in items] minYear = min(years) maxYear = max(years) + 1 nUnit = 1.0 / (maxYear - minYear) # determine category sphere count range minCount = 9999999999 maxCount = 0 for i, group in enumerate(groups): subgroups = lu.groupList(group["items"], categoryCol) # group by category for subgroup in subgroups: minCount = min(minCount, subgroup["count"]) maxCount = max(maxCount, subgroup["count"]) groups[i]["categoryGroups"] = subgroups # assign position values values = np.zeros(len(items) * dimensions) for i, group in enumerate(groups): z = mu.norm( group[yearCol], (minYear, maxYear)) + nUnit * 0.5 # place spheres in the center of the year subgroups = group["categoryGroups"] subgroupLookup = lu.createLookup(subgroups, categoryCol) for j, category in enumerate(categorySet): x = 1.0 - 1.0 * j / (categoryCount - 1) categoryKey = str(j) if categoryKey in subgroupLookup: subgroup = subgroupLookup[categoryKey] y = mu.norm(subgroup["count"], (minCount, maxCount)) y = mu.lerp((0.01, 1.0), y) for catItem in subgroup["items"]: itemIndex = catItem["index"] 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 getSphereCategoryTimelineLayout(userOptions={}): global items global categories cfg = { "layout": "spheres" } categoryCol = "category" yearCol = "year" if yearCol not in items[0]: print("`dateColumn` needs to be set in config yml to support timelineTracks layout") return (False, False) if categoryCol not in items[0]: print("`groupByColumn` needs to be set in config yml to support timelineTracks layout") return (False, False) categoryCount = len(categories) dimensions = 3 groups = lu.groupList(items, yearCol) # group by year groups = sorted(groups, key=lambda group: group[yearCol]) years = [item[yearCol] for item in items] minYear = min(years) maxYear = max(years) + 1 nUnit = 1.0 / (maxYear-minYear) # determine category sphere count range minCount = 9999999999 maxCount = 0 for i, group in enumerate(groups): subgroups = lu.groupList(group["items"], categoryCol) # group by category for subgroup in subgroups: minCount = min(minCount, subgroup["count"]) maxCount = max(maxCount, subgroup["count"]) groups[i]["categoryGroups"] = subgroups # assign position values values = np.zeros(len(items) * dimensions) for i, group in enumerate(groups): z = mu.norm(group[yearCol], (minYear, maxYear)) + nUnit*0.5 # place spheres in the center of the year subgroups = group["categoryGroups"] subgroupLookup = lu.createLookup(subgroups, categoryCol) for j, category in enumerate(categories): x = 1.0 - 1.0 * j / (categoryCount-1) categoryKey = category["text"] if categoryKey in subgroupLookup: subgroup = subgroupLookup[categoryKey] y = mu.norm(subgroup["count"], (minCount, maxCount)) y = mu.lerp((0.01, 1.0), y) for catItem in subgroup["items"]: itemIndex = catItem["index"] cy = y # a bit of a hack to ensure highighted items are visible if itemHasStory(catItem): cy = y + 1.25 values[itemIndex*dimensions] = round(x, PRECISION) values[itemIndex*dimensions+1] = round(cy, PRECISION) values[itemIndex*dimensions+2] = round(z, PRECISION) values = values.tolist() return (cfg, values)