示例#1
0
def getTimelineValues():
    global items

    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()

    years = [item[yearCol] for item in items]
    minYear = min(years)
    maxYear = max(years)
    totalYears = maxYear - minYear + 1

    groups = lu.groupList(items, yearCol) # group by year
    yearDataLookup = lu.createLookup(groups, yearCol)

    timelineItems = []
    for i in range(totalYears):
        year = i + minYear
        yearKey = str(year)
        count = 0
        if yearKey in yearDataLookup:
            count = yearDataLookup[yearKey]["count"]
        timelineItems.append({
            "year": year,
            "value": count
        })
    return timelineItems
def getTimelineTunnelLayout(userOptions={}):
    global items

    options = {
        "thickness": 0.2
    }
    options.update(userOptions)

    cfg = {}
    yearCol = "year"
    dimensions = 3

    if yearCol not in items[0]:
        print("`dateColumn` needs to be set in config yml to support timelineTunnel layout")
        return (False, False)

    years = [item[yearCol] for item in items]
    minYear = min(years)
    maxYear = max(years) + 1
    nUnit = 1.0 / (maxYear-minYear)
    groups = lu.groupList(items, yearCol) # group by year
    groups = sorted(groups, key=lambda group: group[yearCol])
    nThickness = options["thickness"]
    minDistance = 0.5-nThickness
    maxDistance = 0.5
    count = 0
    values = np.zeros(len(items) * dimensions)

    for i, group in enumerate(groups):
        minZ = mu.norm(group[yearCol], (minYear, maxYear))
        maxZ = minZ + nUnit
        for j, item in enumerate(group["items"]):
            index = item["index"]
            x = y = 0.5
            z = mu.randomUniform(minZ, maxZ, seed=count+5)
            # angle =  mu.randomUniform(0, 360, seed=count+7)
            angle =  mu.randomUniform(-240, 60, seed=count+7)

            distance =  mu.randomUniform(minDistance, maxDistance, seed=count+9)
            # ensure story items are visible
            if itemHasStory(item):
                distance = minDistance * 0.8
            x, y =  mu.translatePoint(x, y, distance, angle)
            values[index*dimensions] = round(x, PRECISION)
            values[index*dimensions+1] = round(y, PRECISION)
            values[index*dimensions+2] = round(z, PRECISION)
            count += 1
    values = values.tolist()
    return (cfg, values)
示例#3
0
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)
示例#4
0
def getTimelineTunnelLayout(userOptions={}):
    global items

    options = {"thickness": 0.2}
    options.update(userOptions)

    cfg = {}
    yearCol = "year"
    dimensions = 3

    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()

    years = [item[yearCol] for item in items]
    minYear = min(years)
    maxYear = max(years) + 1
    nUnit = 1.0 / (maxYear - minYear)
    groups = lu.groupList(items, yearCol)  # group by year
    groups = sorted(groups, key=lambda group: group[yearCol])
    nThickness = options["thickness"]
    count = 0
    values = np.zeros(len(items) * dimensions)

    for i, group in enumerate(groups):
        minZ = mu.norm(group[yearCol], (minYear, maxYear))
        maxZ = minZ + nUnit
        for j, item in enumerate(group["items"]):
            index = item["index"]
            x = y = 0.5
            z = mu.randomUniform(minZ, maxZ, seed=count + 5)
            # angle =  mu.randomUniform(0, 360, seed=count+7)
            angle = mu.randomUniform(-240, 60, seed=count + 7)
            distance = mu.randomUniform(0.5 - nThickness, 0.5, seed=count + 9)
            x, y = mu.translatePoint(x, y, distance, angle)
            values[index * dimensions] = round(x, PRECISION)
            values[index * dimensions + 1] = round(y, PRECISION)
            values[index * dimensions + 2] = round(z, PRECISION)
            count += 1
    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("`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)
示例#6
0
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)
示例#7
0
    if item["Acquisition Year"] >= 9999:
        items[i]["Acquisition Year"] = None
items = mu.addNormalizedValues(items, "Acquisition Year", "nalpha")
for i, item in enumerate(items):
    if item["Acquisition Year"] is None:
        items[i]["Acquisition Year"] = 9999

# Process data into sections and groups within each section
for i, col in enumerate(collections):
    # Break items into sections
    itemsBySection = []
    groupBy = None
    if "groupKey" in col:
        groupBy = col["groupKey"]
        itemsBySection = lu.groupList([
            item for item in items
            if groupBy in item and len(str(item[groupBy])) > 0
        ], groupBy)
        itemsBySection = sorted(itemsBySection, key=lambda k: k[groupBy])
    else:
        itemsBySection = [{"items": items, "count": itemCount}]

    # Break groups down further into groups
    sections = []
    for sectionItems in itemsBySection:
        sectionItemsByGroup = lu.groupList(sectionItems["items"],
                                           col["key"],
                                           sort=True)
        # group by year if year
        if "Year" in col["key"]:
            sectionItemsByGroup = sorted(sectionItemsByGroup,
                                         key=lambda k: k[col["key"]])
示例#8
0
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)
示例#10
0
def getItems(config):
    inputFile = config["metadataFile"]
    idCol = config["identifierColumn"] if "identifierColumn" in config else None

    fieldnames, items = io.readCsv(inputFile, parseNumbers=False)
    if "metadataFilterQuery" in config:
        items = lu.filterByQueryString(items, config["metadataFilterQuery"])
        print("%s items after filtering" % len(items))

    # map year, lat/lon, and category
    columnMap = [("dateColumn", "year"), ("latitudeColumn", "lat"),
                 ("longitudeColumn", "lon"), ("countryColumn", "country"),
                 ("groupByColumn", "category")]
    minimumYear = config["minimumYear"] if "minimumYear" in config else None
    maximumYear = config["maximumYear"] if "maximumYear" in config else None
    validItems = []
    for i, item in enumerate(items):
        validatedItem = item.copy()
        isValid = True
        for configKey, toColumn in columnMap:
            if configKey not in config:
                continue

            value = item[config[configKey]]
            if toColumn == "year":
                value = su.validateYear(value, minimumYear, maximumYear)
            elif toColumn == "lat":
                value = su.validateLat(value)
            elif toColumn == "lon":
                value = su.validateLon(value)
            if value is None:
                isValid = False
                break

            validatedItem[toColumn] = value

        if isValid:
            validItems.append(validatedItem)

    diff = len(items) - len(validItems)
    print(f'Found {diff} invalid items.')

    # Sort so that index corresponds to ID
    if idCol is not None:
        for i, item in enumerate(validItems):
            validItems[i]["_id"] = str(item[idCol])
        validItems = sorted(validItems, key=lambda item: item["_id"])

    validItems = lu.addIndices(validItems)
    if idCol is None:
        for i, item in enumerate(validItems):
            validItems[i]["_id"] = str(i)

    # Retrieve categories
    categories = []
    itemsByCategory = lu.groupList(validItems,
                                   "category",
                                   sort=True,
                                   desc=True)
    if "groupLimit" in config and len(itemsByCategory) > config["groupLimit"]:
        limit = config["groupLimit"] - 1
        otherItems = itemsByCategory[limit:]
        otherLabel = config["otherLabel"] if "otherLabel" in config else "Other"
        otherCount = 0
        for group in otherItems:
            for item in group["items"]:
                validItems[item["index"]]["category"] = otherLabel
                otherCount += 1
        itemsByCategory = itemsByCategory[:limit] + [{
            "category": otherLabel,
            "count": otherCount
        }]
    categoryColors = config["groupColors"]
    colorCount = len(categoryColors)
    for i, category in enumerate(itemsByCategory):
        color = categoryColors[i % colorCount]
        categories.append({
            "text": category["category"],
            "color": color,
            "count": category["count"]
        })

    return (validItems, categories)
a = parser.parse_args()

COLORS = ["#612323", "#204f1c", "#4d1e59", "#112e6b", "#4b5713", "#571330"]
colorCount = len(COLORS)

# Make sure output dirs exist
io.makeDirectories([a.OUTPUT_FILE, a.CACHE_FILE])
font = ImageFont.truetype(font="fonts/Open_Sans/OpenSans-Regular.ttf", size=a.FONT_SIZE)

print("Reading data...")
fieldNames, items = io.readCsv(a.INPUT_FILE)

yLabels = lu.unique([item[a.Y_AXIS] for item in items])
if a.Y_AXIS == "Region":
    items = [item for item in items if item["Region"] != "Europe"]
    itemsByRegion = lu.groupList(items, "Region")
    for i, region in enumerate(itemsByRegion):
        itemsByRegion[i]["lat"] = np.mean([item["Latitude"] for item in region["items"] if -90 <= item["Latitude"] <= 90])
    itemsByRegion = sorted(itemsByRegion, key=lambda region: -region["lat"])
    yLabels = [region["Region"] for region in itemsByRegion]
else:
    yLabels = sorted(yLabels)
yLabelCount = len(yLabels)

xLabels = []
yearStart = yearEnd = None
if "Year" in a.X_AXIS:
    items = [item for item in items if item[a.X_AXIS] < 9999]
    items = sorted(items, key=lambda item: item[a.X_AXIS])
    yearStart = items[0][a.X_AXIS]
    yearEnd = items[-1][a.X_AXIS]