Esempio n. 1
0
def project(
    bbox: s2sphere.LatLngRect, size: XY, offset: XY, latlnglines: typing.List[typing.List[s2sphere.LatLng]]
) -> typing.List[typing.List[typing.Tuple[float, float]]]:
    min_x = lng2x(bbox.lng_lo().degrees)
    d_x = lng2x(bbox.lng_hi().degrees) - min_x
    while d_x >= 2:
        d_x -= 2
    while d_x < 0:
        d_x += 2
    min_y = lat2y(bbox.lat_lo().degrees)
    max_y = lat2y(bbox.lat_hi().degrees)
    d_y = abs(max_y - min_y)

    scale = size.x / d_x if size.x / size.y <= d_x / d_y else size.y / d_y
    offset = offset + 0.5 * (size - scale * XY(d_x, -d_y)) - scale * XY(min_x, min_y)
    lines = []
    for latlngline in latlnglines:
        line = []
        for latlng in latlngline:
            if bbox.contains(latlng):
                line.append((offset + scale * latlng2xy(latlng)).tuple())
            else:
                if len(line) > 0:
                    lines.append(line)
                    line = []
        if len(line) > 0:
            lines.append(line)
    return lines
Esempio n. 2
0
 def draw(self, dr: svgwrite.Drawing, g: svgwrite.container.Group, size: XY,
          offset: XY) -> None:
     """For each track, draw it on the poster."""
     if self.poster.tracks is None:
         raise PosterError("No tracks to draw.")
     cell_size, counts = utils.compute_grid(len(self.poster.tracks), size)
     if cell_size is None or counts is None:
         raise PosterError("Unable to compute grid.")
     count_x, count_y = counts[0], counts[1]
     spacing_x = 0 if count_x <= 1 else (size.x - cell_size * count_x) / (
         count_x - 1)
     spacing_y = 0 if count_y <= 1 else (size.y - cell_size * count_y) / (
         count_y - 1)
     offset.x += (size.x - count_x * cell_size -
                  (count_x - 1) * spacing_x) / 2
     offset.y += (size.y - count_y * cell_size -
                  (count_y - 1) * spacing_y) / 2
     year_groups: typing.Dict[int, svgwrite.container.Group] = {}
     for (index, tr) in enumerate(self.poster.tracks):
         year = tr.start_time().year
         if year not in year_groups:
             g_year = dr.g(id=f"year{year}")
             g.add(g_year)
             year_groups[year] = g_year
         else:
             g_year = year_groups[year]
         p = XY(index % count_x, index // count_x) * XY(
             cell_size + spacing_x, cell_size + spacing_y)
         self._draw_track(
             dr,
             g_year,
             tr,
             0.9 * XY(cell_size, cell_size),
             offset + 0.05 * XY(cell_size, cell_size) + p,
         )
Esempio n. 3
0
 def draw(self, dr: svgwrite.Drawing, size: XY, offset: XY) -> None:
     """For each track, draw it on the poster."""
     if self.poster.tracks is None:
         raise PosterError("No tracks to draw.")
     cell_size, counts = utils.compute_grid(len(self.poster.tracks), size)
     if cell_size is None or counts is None:
         raise PosterError("Unable to compute grid.")
     count_x, count_y = counts[0], counts[1]
     spacing_x = 0 if count_x <= 1 else (size.x - cell_size * count_x) / (
         count_x - 1)
     spacing_y = 0 if count_y <= 1 else (size.y - cell_size * count_y) / (
         count_y - 1)
     offset.x += (size.x - count_x * cell_size -
                  (count_x - 1) * spacing_x) / 2
     offset.y += (size.y - count_y * cell_size -
                  (count_y - 1) * spacing_y) / 2
     for (index, tr) in enumerate(self.poster.tracks):
         p = XY(index % count_x, index // count_x) * XY(
             cell_size + spacing_x, cell_size + spacing_y)
         self._draw_track(
             dr,
             tr,
             0.9 * XY(cell_size, cell_size),
             offset + 0.05 * XY(cell_size, cell_size) + p,
         )
Esempio n. 4
0
 def draw(self, drawer: "TracksDrawer", output: str) -> None:
     """Set the Poster's drawer and draw the tracks."""
     self.tracks_drawer = drawer
     d = svgwrite.Drawing(output, (f"{self.width}mm", f"{self.height}mm"))
     d.viewbox(0, 0, self.width, self.height)
     d.add(d.rect((0, 0), (self.width, self.height), fill=self.colors["background"]))
     self._draw_header(d)
     self._draw_footer(d)
     self._draw_tracks(d, XY(self.width - 20, self.height - 30 - 30), XY(10, 30))
     d.save()
Esempio n. 5
0
 def _draw_rings(self, dr: svgwrite.Drawing, g: svgwrite.container.Group,
                 center: XY, radius_range: ValueRange) -> None:
     length_range = self.poster.length_range_by_date
     if not length_range.is_valid():
         return
     min_length = length_range.lower()
     max_length = length_range.upper()
     assert min_length is not None
     assert max_length is not None
     ring_distance = self._determine_ring_distance(max_length)
     if ring_distance is None:
         return
     distance = ring_distance
     while distance < max_length:
         radius = radius_range.interpolate(
             (distance / max_length).magnitude)
         g.add(
             dr.circle(
                 center=center.tuple(),
                 r=radius,
                 stroke=self._ring_color,
                 stroke_opacity="0.2",
                 fill="none",
                 stroke_width=0.3,
             ))
         distance += ring_distance
Esempio n. 6
0
    def draw(self, dr: svgwrite.Drawing, g: svgwrite.container.Group, size: XY,
             offset: XY) -> None:
        """Draw the circular Poster using distances broken down by time"""
        if self.poster.tracks is None:
            raise PosterError("No tracks to draw.")
        if self.poster.length_range_by_date is None:
            return

        years = self.poster.years.count()
        _, counts = utils.compute_grid(years, size)
        if counts is None:
            raise PosterError("Unable to compute grid.")
        count_x, count_y = counts[0], counts[1]
        x, y = 0, 0
        cell_size = size * XY(1 / count_x, 1 / count_y)
        margin = XY(4, 4)
        if count_x <= 1:
            margin.x = 0
        if count_y <= 1:
            margin.y = 0
        sub_size = cell_size - 2 * margin
        for year in self.poster.years.iter():
            g_year = dr.g(id=f"year{year}")
            g.add(g_year)
            self._draw_year(dr, g_year, sub_size,
                            offset + margin + cell_size * XY(x, y), year)
            x += 1
            if x >= count_x:
                x = 0
                y += 1
    def draw(self, dr: svgwrite.Drawing, g: svgwrite.container.Group, size: XY,
             offset: XY) -> None:
        """Iterate through the Poster's years, creating a calendar for each."""
        if self.poster.tracks is None:
            raise PosterError("No tracks to draw.")
        years = self.poster.years.count()
        _, counts = utils.compute_grid(years, size)
        if counts is None:
            raise PosterError("Unable to compute grid.")
        count_x, count_y = counts[0], counts[1]
        x, y = 0, 0
        cell_size = size * XY(1 / count_x, 1 / count_y)
        margin = XY(4, 8)
        if count_x <= 1:
            margin.x = 0
        if count_y <= 1:
            margin.y = 0
        sub_size = cell_size - 2 * margin

        for year in self.poster.years.iter():
            g_year = dr.g(id=f"year{year}")
            g.add(g_year)
            self._draw(dr, g_year, sub_size,
                       offset + margin + cell_size * XY(x, y), year)
            x += 1
            if x >= count_x:
                x = 0
                y += 1
    def draw(self, dr: svgwrite.Drawing, size: XY, offset: XY):
        """Iterate through the Poster's years, creating a calendar for each."""
        if self.poster.tracks is None:
            raise PosterError("No tracks to draw.")
        years = self.poster.years.count()
        _, counts = utils.compute_grid(years, size)
        if counts is None:
            raise PosterError("Unable to compute grid.")
        count_x, count_y = counts[0], counts[1]
        x, y = 0, 0
        cell_size = size * XY(1 / count_x, 1 / count_y)
        margin = XY(4, 8)
        if count_x <= 1:
            margin.x = 0
        if count_y <= 1:
            margin.y = 0
        sub_size = cell_size - 2 * margin

        for year in range(self.poster.years.from_year,
                          self.poster.years.to_year + 1):
            self._draw(dr, sub_size, offset + margin + cell_size * XY(x, y),
                       year)
            x += 1
            if x >= count_x:
                x = 0
                y += 1
Esempio n. 9
0
 def _draw_rings(self, dr: svgwrite.Drawing, center: XY, radius_range: ValueRange):
     length_range = self.poster.length_range_by_date
     ring_distance = self._determine_ring_distance()
     if ring_distance is None:
         return
     distance = ring_distance
     while distance < length_range.upper():
         radius = (
             radius_range.lower()
             + radius_range.diameter() * distance / length_range.upper()
         )
         dr.add(
             dr.circle(
                 center=center.tuple(),
                 r=radius,
                 stroke=self._ring_color,
                 stroke_opacity="0.2",
                 fill="none",
                 stroke_width=0.3,
             )
         )
         distance += ring_distance
Esempio n. 10
0
    def _draw_year(self, dr: svgwrite.Drawing, g: svgwrite.container.Group,
                   size: XY, offset: XY, year: int) -> None:
        min_size = min(size.x, size.y)
        outer_radius = 0.5 * min_size - 6
        radius_range = ValueRange.from_pair(outer_radius / 4, outer_radius)
        center = offset + 0.5 * size

        if self._rings:
            self._draw_rings(dr, g, center, radius_range)

        year_style = f"dominant-baseline: central; font-size:{min_size * 4.0 / 80.0}px; font-family:Arial;"
        month_style = f"font-size:{min_size * 3.0 / 80.0}px; font-family:Arial;"

        g.add(
            dr.text(
                f"{year}",
                insert=center.tuple(),
                fill=self.poster.colors["text"],
                text_anchor="middle",
                alignment_baseline="middle",
                style=year_style,
            ))
        df = 360.0 / (366 if calendar.isleap(year) else 365)
        day = 0
        date = datetime.date(year, 1, 1)
        while date.year == year:
            text_date = date.strftime("%Y-%m-%d")
            a1 = math.radians(day * df)
            a2 = math.radians((day + 1) * df)
            if date.day == 1:
                (_, last_day) = calendar.monthrange(date.year, date.month)
                a3 = math.radians((day + last_day - 1) * df)
                sin_a1, cos_a1 = math.sin(a1), math.cos(a1)
                sin_a3, cos_a3 = math.sin(a3), math.cos(a3)
                r1 = outer_radius + 1
                r2 = outer_radius + 6
                r3 = outer_radius + 2
                g.add(
                    dr.line(
                        start=(center + r1 * XY(sin_a1, -cos_a1)).tuple(),
                        end=(center + r2 * XY(sin_a1, -cos_a1)).tuple(),
                        stroke=self.poster.colors["text"],
                        stroke_width=0.3,
                    ))
                path = dr.path(
                    d=("M", center.x + r3 * sin_a1, center.y - r3 * cos_a1),
                    fill="none",
                    stroke="none",
                )
                path.push(
                    f"a{r3},{r3} 0 0,1 {r3 * (sin_a3 - sin_a1)},{r3 * (cos_a1 - cos_a3)}"
                )
                g.add(path)
                tpath = svgwrite.text.TextPath(
                    path,
                    self.poster.month_name(date.month),
                    startOffset=(0.5 * r3 * (a3 - a1)))
                text = dr.text(
                    "",
                    fill=self.poster.colors["text"],
                    text_anchor="middle",
                    style=month_style,
                )
                text.add(tpath)
                g.add(text)
            if text_date in self.poster.tracks_by_date:
                self._draw_circle_segment(
                    dr,
                    g,
                    self.poster.tracks_by_date[text_date],
                    a1,
                    a2,
                    radius_range,
                    center,
                )

            day += 1
            date += datetime.timedelta(1)
Esempio n. 11
0
    def _draw(self, dr: svgwrite.Drawing, g: svgwrite.container.Group,
              size: XY, offset: XY, year: int) -> None:
        min_size = min(size.x, size.y)
        year_size = min_size * 4.0 / 80.0
        year_style = f"font-size:{year_size}px; font-family:Arial;"
        month_style = f"font-size:{min_size * 3.0 / 80.0}px; font-family:Arial;"
        day_style = f"dominant-baseline: central; font-size:{min_size * 1.0 / 80.0}px; font-family:Arial;"
        day_length_style = f"font-size:{min_size * 1.0 / 80.0}px; font-family:Arial;"

        g.add(
            dr.text(
                f"{year}",
                insert=offset.tuple(),
                fill=self.poster.colors["text"],
                alignment_baseline="hanging",
                style=year_style,
            ))
        offset.y += year_size
        size.y -= year_size
        count_x = 31
        for month in range(1, 13):
            date = datetime.date(year, month, 1)
            (_, last_day) = calendar.monthrange(year, month)
            count_x = max(count_x, date.weekday() + last_day)

        cell_size = min(size.x / count_x, size.y / 36)
        spacing = XY(
            (size.x - cell_size * count_x) / (count_x - 1),
            (size.y - cell_size * 3 * 12) / 11,
        )

        for month in range(1, 13):
            date = datetime.date(year, month, 1)
            y = month - 1
            y_pos = offset.y + (y * 3 + 1) * cell_size + y * spacing.y
            g.add(
                dr.text(
                    self.poster.month_name(month),
                    insert=(offset.x, y_pos - 2),
                    fill=self.poster.colors["text"],
                    alignment_baseline="hanging",
                    style=month_style,
                ))

            day_offset = date.weekday()
            while date.month == month:
                x = date.day - 1
                x_pos = offset.x + (day_offset + x) * cell_size + x * spacing.x
                pos = (x_pos + 0.05 * cell_size, y_pos + 1.15 * cell_size)
                dim = (cell_size * 0.9, cell_size * 0.9)
                text_date = date.strftime("%Y-%m-%d")
                if text_date in self.poster.tracks_by_date:
                    tracks = self.poster.tracks_by_date[text_date]
                    length = sum([t.length() for t in tracks])
                    has_special = len([t for t in tracks if t.special]) > 0
                    color = self.color(self.poster.length_range_by_date,
                                       length, has_special)
                    g.add(dr.rect(pos, dim, fill=color))
                    g.add(
                        dr.text(
                            utils.format_float(self.poster.m2u(length)),
                            insert=(
                                pos[0] + cell_size / 2,
                                pos[1] + cell_size + cell_size / 2,
                            ),
                            text_anchor="middle",
                            style=day_length_style,
                            fill=self.poster.colors["text"],
                        ))
                else:
                    g.add(dr.rect(pos, dim, fill="#444444"))

                g.add(
                    dr.text(
                        localized_day_of_week_name(date.weekday(), short=True),
                        insert=(
                            offset.x + (day_offset + x) * cell_size +
                            cell_size / 2,
                            pos[1] + cell_size / 2,
                        ),
                        text_anchor="middle",
                        alignment_baseline="middle",
                        style=day_style,
                    ))
                date += datetime.timedelta(1)
Esempio n. 12
0
    def draw(self, dr: svgwrite.Drawing, g: svgwrite.container.Group, size: XY,
             offset: XY) -> None:
        if self.poster.tracks is None:
            raise PosterError("No tracks to draw")
        year_size = 200 * 4.0 / 80.0
        year_style = f"font-size:{year_size}px; font-family:Arial;"
        year_length_style = f"font-size:{110 * 3.0 / 80.0}px; font-family:Arial;"
        month_names_style = "font-size:2.5px; font-family:Arial"
        total_length_year_dict = self.poster.total_length_year_dict
        for year in self.poster.years.iter():
            g_year = dr.g(id=f"year{year}")
            g.add(g_year)

            start_date_weekday, _ = calendar.monthrange(year, 1)
            github_rect_first_day = datetime.date(year, 1, 1)
            # Github profile the first day start from the last Monday of the last year or the first Monday of this year
            # It depands on if the first day of this year is Monday or not.
            github_rect_day = github_rect_first_day + datetime.timedelta(
                -start_date_weekday)
            year_length = total_length_year_dict.get(year, 0)
            year_length_str = utils.format_float(self.poster.m2u(year_length))
            month_names = [
                locale.nl_langinfo(day)[:3]  # Get only first three letters
                for day in [
                    locale.MON_1,
                    locale.MON_2,
                    locale.MON_3,
                    locale.MON_4,
                    locale.MON_5,
                    locale.MON_6,
                    locale.MON_7,
                    locale.MON_8,
                    locale.MON_9,
                    locale.MON_10,
                    locale.MON_11,
                    locale.MON_12,
                ]
            ]
            km_or_mi = self.poster.u()
            g_year.add(
                dr.text(
                    f"{year}",
                    insert=offset.tuple(),
                    fill=self.poster.colors["text"],
                    alignment_baseline="hanging",
                    style=year_style,
                ))

            g_year.add(
                dr.text(
                    f"{year_length_str} {km_or_mi}",
                    insert=(offset.tuple()[0] + 165, offset.tuple()[1] + 2),
                    fill=self.poster.colors["text"],
                    alignment_baseline="hanging",
                    style=year_length_style,
                ))
            # add month name up to the poster one by one because of svg text auto trim the spaces.
            for num, name in enumerate(month_names):
                g_year.add(
                    dr.text(
                        f"{name}",
                        insert=(offset.tuple()[0] + 15.5 * num,
                                offset.tuple()[1] + 14),
                        fill=self.poster.colors["text"],
                        style=month_names_style,
                    ))

            rect_x = 10.0
            dom = (2.6, 2.6)
            # add every day of this year for 53 weeks and per week has 7 days
            for _i in range(54):
                rect_y = offset.y + year_size + 2
                for _j in range(7):
                    if int(github_rect_day.year) > year:
                        break
                    rect_y += 3.5
                    color = "#444444"
                    date_title = str(github_rect_day)
                    if date_title in self.poster.tracks_by_date:
                        tracks = self.poster.tracks_by_date[date_title]
                        length = sum([t.length() for t in tracks])
                        distance1 = self.poster.special_distance[
                            "special_distance"]
                        distance2 = self.poster.special_distance[
                            "special_distance2"]
                        has_special = distance1 < length < distance2
                        color = self.color(self.poster.length_range_by_date,
                                           length, has_special)
                        if length >= distance2:
                            special_color = self.poster.colors.get(
                                "special2") or self.poster.colors.get(
                                    "special")
                            if special_color is not None:
                                color = special_color
                        str_length = utils.format_float(
                            self.poster.m2u(length))
                        date_title = f"{date_title} {str_length} {km_or_mi}"

                    rect = dr.rect((rect_x, rect_y), dom, fill=color)
                    rect.set_desc(title=date_title)
                    g_year.add(rect)
                    github_rect_day += datetime.timedelta(1)
                rect_x += 3.5
            offset.y += 3.5 * 9 + year_size + 1.5
Esempio n. 13
0
def latlng2xy(latlng: s2sphere.LatLng) -> XY:
    return XY(lng2x(latlng.lng().degrees), lat2y(latlng.lat().degrees))
Esempio n. 14
0
    def _draw(self, dr: svgwrite.Drawing, size: XY, offset: XY, year: int):
        min_size = min(size.x, size.y)
        year_size = min_size * 4.0 / 80.0
        year_style = f"font-size:{year_size}px; font-family:Arial;"
        month_style = f"font-size:{min_size * 3.0 / 80.0}px; font-family:Arial;"
        day_style = f"dominant-baseline: central; font-size:{min_size * 1.0 / 80.0}px; font-family:Arial;"
        day_length_style = f"font-size:{min_size * 1.0 / 80.0}px; font-family:Arial;"

        dr.add(
            dr.text(
                f"{year}",
                insert=offset.tuple(),
                fill=self.poster.colors["text"],
                alignment_baseline="hanging",
                style=year_style,
            )
        )
        offset.y += year_size
        size.y -= year_size
        count_x = 31
        for month in range(1, 13):
            date = datetime.date(year, month, 1)
            (_, last_day) = calendar.monthrange(year, month)
            count_x = max(count_x, date.weekday() + last_day)

        cell_size = min(size.x / count_x, size.y / 36)
        spacing = XY(
            (size.x - cell_size * count_x) / (count_x - 1),
            (size.y - cell_size * 3 * 12) / 11,
        )

        # chinese weekday key number is the third.
        keyword_num = 0
        if locale.getlocale()[0] == "zh_CN":
            keyword_num = 2
        # first character of localized day names, starting with Monday.
        dow = [
            locale.nl_langinfo(day)[keyword_num].upper()
            for day in [
                locale.DAY_2,
                locale.DAY_3,
                locale.DAY_4,
                locale.DAY_5,
                locale.DAY_6,
                locale.DAY_7,
                locale.DAY_1,
            ]
        ]

        for month in range(1, 13):
            date = datetime.date(year, month, 1)
            y = month - 1
            y_pos = offset.y + (y * 3 + 1) * cell_size + y * spacing.y
            dr.add(
                dr.text(
                    date.strftime("%B"),
                    insert=(offset.x, y_pos - 2),
                    fill=self.poster.colors["text"],
                    alignment_baseline="hanging",
                    style=month_style,
                )
            )

            day_offset = date.weekday()
            while date.month == month:
                x = date.day - 1
                x_pos = offset.x + (day_offset + x) * cell_size + x * spacing.x
                pos = (x_pos + 0.05 * cell_size, y_pos + 1.15 * cell_size)
                dim = (cell_size * 0.9, cell_size * 0.9)
                text_date = date.strftime("%Y-%m-%d")
                if text_date in self.poster.tracks_by_date:
                    tracks = self.poster.tracks_by_date[text_date]
                    length = sum([t.length for t in tracks])
                    has_special = len([t for t in tracks if t.special]) > 0
                    color = self.color(
                        self.poster.length_range_by_date, length, has_special
                    )
                    dr.add(dr.rect(pos, dim, fill=color))
                    dr.add(
                        dr.text(
                            utils.format_float(self.poster.m2u(length)),
                            insert=(
                                pos[0] + cell_size / 2,
                                pos[1] + cell_size + cell_size / 2,
                            ),
                            text_anchor="middle",
                            style=day_length_style,
                            fill=self.poster.colors["text"],
                        )
                    )
                else:
                    dr.add(dr.rect(pos, dim, fill="#444444"))

                dr.add(
                    dr.text(
                        dow[date.weekday()],
                        insert=(
                            offset.x + (day_offset + x) * cell_size + cell_size / 2,
                            pos[1] + cell_size / 2,
                        ),
                        text_anchor="middle",
                        alignment_baseline="middle",
                        style=day_style,
                    )
                )
                date += datetime.timedelta(1)