def gen_group3(matchesdir, filename=None):
    ROWS_HEAD=BONUS_INFO_KEYS+EVALUATORS_KEYS+["config"]
    rows=[]
    for st in walk_statistics(matchesdir):
        for side in ["left", "right"]:
            st.side=side
            if not(st.team.lower().startswith("fcportugal")):
                # each row contains fcportugal data only
                continue

            teamdata=SortedDict()
            teamdata.update(bonus_info(st))
            teamdata.update(evaluators(st))
            t_config=discoverteamconfig(st.xml,st.side)
            teamdata["config"]=t_config
            # for confname in T_CONFIG_NAMES:
            #     teamdata[confname]=t_config[confname]

            # another row.
            rows.append(teamdata)

    if filename is not None:
        with open(filename, "w") as f:
            f.write(";".join(ROWS_HEAD)+"\n")
            for row in rows:
                f.write(";".join([str(value) for _, value in row.iteritems()]))
                f.write("\n")

    return rows
def gen_group4(matchesdir, filename=None):
    ROWS_HEAD=BONUS_INFO_KEYS+EVALUATORS_KEYS+["config"]+["opponent"]+STATISTICS14_KEYS
    rows=[]
    for st in walk_statistics(matchesdir):
        fcp,other = teams_sides(st)
        teamsdata=SortedDict()

        st.side=fcp
        teamsdata.update(bonus_info(st))
        teamsdata.update(evaluators(st))
        t_config=discoverteamconfig(st.xml,st.side)
        teamsdata["config"]=t_config
        # for confname in T_CONFIG_NAMES:
        #     teamsdata[confname]=t_config[confname]

        st.side=other
        teamsdata["opponent"]=st.team
        teamsdata.update(statistics14(st))

        #another row
        rows.append(teamsdata)

    if filename is not None:
        with open(filename, "w") as f:
            f.write(";".join(ROWS_HEAD)+"\n")
            for row in rows:
                f.write(";".join([str(value) for _, value in row.iteritems()]))
                f.write("\n")

    return rows
def gen_group2(matchesdir, target_dir=None):
    ROWS_HEAD=BONUS_INFO_KEYS+STATISTICS14_KEYS
    teams={}
    for st in walk_statistics(matchesdir):
        for side in ["left", "right"]:
            st.side=side
            if st.team not in teams:
                teams[st.team]=[]

            teamdata=SortedDict()
            teamdata.update(bonus_info(st))
            teamdata.update(statistics14(st))

            # another row
            teams[st.team].append(teamdata)


    # write
    if target_dir is not None:
        if not os.path.isdir(target_dir):
            os.mkdir(target_dir)
        for teamname, teamdata in teams.iteritems():
            fname=os.path.join(target_dir, teamname+".csv")
            with open(fname, "w") as f:
                f.write(";".join(ROWS_HEAD)+"\n")
                for row in teamdata:
                    f.write(";".join([str(value) for _, value in row.iteritems()]))
                    f.write("\n")

    return teams
Ejemplo n.º 4
0
 def __init__(self, code, name, offset, relative_to=None):
     self.code = code
     self.name = name
     self._offset = offset
     self.relative_to = relative_to
     self.platforms = SortedDict()
     self.placed = SortedDict()
     self.label_direction = None
     self.label_offset = Vector(0, 0)
Ejemplo n.º 5
0
 def __init__(self, code, name, offset, relative_to=None):
     self.code = code
     self.name = name
     self._offset = offset
     self.relative_to = relative_to
     self.platforms = SortedDict()
     self.placed = SortedDict()
     self.label_direction = None
     self.label_offset = Vector(0, 0)
def gen_group1(dir_, filename=None):
    ROWS_HEAD=BONUS_INFO_KEYS+STATISTICS14_KEYS
    rows=[]
    for st in walk_statistics(dir_):
        for side in ["left", "right"]:
            st.side=side
            teamdata=SortedDict()
            teamdata.update(bonus_info(st))
            teamdata.update(statistics14(st))

            # another row.
            rows.append(teamdata)

    if filename is not None:
        with open(filename, "w") as f:
            f.write(";".join(ROWS_HEAD)+"\n")
            for row in rows:
                f.write(";".join([str(value) for _, value in row.iteritems()]))
                f.write("\n")

    return rows
Ejemplo n.º 7
0
    def load(self, filename):
        self.stations = SortedDict()
        self.lines = SortedDict()
        self.extents = [0, 0, 0, 0]
        self.outbounds = []
        draw_last = []
        draw_first = []
        with open(filename) as fh:
            for lineno, line in enumerate(fh):
                line = line.strip()
                if line and line[0] != "#":
                    # Get the parts
                    parts = line.split()
                    type, parts = parts[0], parts[1:]
                    # What kind of line is it?
                    if type == "line":
                        # Line definition
                        code = parts[0]
                        colors = [(
                            int(part[0:2], 16) / 255.0,
                            int(part[2:4], 16) / 255.0,
                            int(part[4:8], 16) / 255.0,
                        ) for part in parts[1].split(",")]
                        self.lines[code] = Line(code, colors)

                    # Track segment
                    elif type in ("track", "subtrack"):
                        # It's a station-to-station description
                        station_code, platform_number = parts[0].split("-", 1)
                        dest_code, dest_number = parts[1].split("-", 1)
                        station = self.stations[station_code]
                        # Check for reverses
                        leaves_start = False
                        if platform_number[-1] == "!":
                            leaves_start = True
                            platform_number = platform_number[:-1]
                        finishes_end = False
                        if dest_number[-1] == "!":
                            finishes_end = True
                            dest_number = dest_number[:-1]
                        # Add it
                        try:
                            self.add_outbound(
                                station.platforms[platform_number],
                                self.stations[dest_code].platforms[dest_number],
                                self.lines[parts[2]],
                                leaves_start = leaves_start,
                                finishes_end = finishes_end,
                                subtrack = (type == "subtrack"),
                            )
                        except:
                            print "Error context: %s, %s" % (station, parts)
                            raise

                    # Station/waypoint record
                    elif type in ("station", "waypoint", "depot", "sidings", "disstation"):
                        # It's a station or points definition
                        code = parts[0]
                        index = 1
                        while "," not in parts[index]:
                            index += 1
                        name = " ".join(parts[1:index])
                        # Work out the coordinates
                        coord_parts = parts[index].split(",")
                        coords = Vector(*map(float, coord_parts[-2:])) * 10
                        if len(coord_parts) == 3:
                            relative_to = self.stations[coord_parts[0]]
                        else:
                            relative_to = None
                        if type == "station":
                            station_class = Station
                        elif type == "depot":
                            station_class = Depot
                        elif type == "sidings":
                            station_class = Sidings
                        elif type == "disstation":
                            station_class = DisusedStation
                        else:
                            station_class = Points
                        last_station = self.stations[code] = station_class(
                            code,
                            name,
                            coords,
                            relative_to = relative_to,
                        )
                        self.extents[0] = min(coords.x, self.extents[0])
                        self.extents[1] = max(coords.x, self.extents[1])
                        self.extents[2] = min(coords.y, self.extents[2])
                        self.extents[3] = max(coords.y, self.extents[3])

                    # Platform record
                    elif type == "platform":
                        # Add a platform to the last station
                        direction = getattr(Direction, parts[1])
                        try:
                            line = self.lines[parts[2]]
                        except (IndexError, KeyError):
                            line = self.lines["error"]
                        try:
                            platform_side_code = parts[3]
                            platform_side = {
                                "L": Segment.PLATFORM_LEFT,
                                "R": Segment.PLATFORM_RIGHT,
                                "B": Segment.PLATFORM_BOTH,
                                "N": Segment.PLATFORM_NONE,
                            }[platform_side_code.upper()]
                        except IndexError:
                            platform_side = Segment.PLATFORM_BOTH
                        self.stations[code].add_platform(parts[0], direction, line, platform_side)

                    # Drawing order modifiers
                    elif type == "draw":
                        if parts[0] == "first":
                            draw_first.append(last_station)
                        elif parts[0] == "last":
                            draw_last.append(last_station)
                        else:
                            raise ValueError("Unknown draw position %r" % parts[0])

                    # Label placement modifiers
                    elif type == "label":
                        last_station.label_direction = getattr(Direction, parts[0])
                    elif type == "label_offset":
                        last_station.label_offset = Vector(map(int, parts[0].split(",")))

                    # Unknown
                    else:
                        raise ValueError("Unknown line type %r" % type)
        # Now reorder those with special draw clauses
        for station in draw_first:
            self.stations.insert(0, station.code, station)
        for station in draw_last:
            self.stations.insert(len(self.stations), station.code, station)
Ejemplo n.º 8
0
class Map(object):

    padding = 50

    def __init__(self):
        pass

    def load(self, filename):
        self.stations = SortedDict()
        self.lines = SortedDict()
        self.extents = [0, 0, 0, 0]
        self.outbounds = []
        draw_last = []
        draw_first = []
        with open(filename) as fh:
            for lineno, line in enumerate(fh):
                line = line.strip()
                if line and line[0] != "#":
                    # Get the parts
                    parts = line.split()
                    type, parts = parts[0], parts[1:]
                    # What kind of line is it?
                    if type == "line":
                        # Line definition
                        code = parts[0]
                        colors = [(
                            int(part[0:2], 16) / 255.0,
                            int(part[2:4], 16) / 255.0,
                            int(part[4:8], 16) / 255.0,
                        ) for part in parts[1].split(",")]
                        self.lines[code] = Line(code, colors)

                    # Track segment
                    elif type in ("track", "subtrack"):
                        # It's a station-to-station description
                        station_code, platform_number = parts[0].split("-", 1)
                        dest_code, dest_number = parts[1].split("-", 1)
                        station = self.stations[station_code]
                        # Check for reverses
                        leaves_start = False
                        if platform_number[-1] == "!":
                            leaves_start = True
                            platform_number = platform_number[:-1]
                        finishes_end = False
                        if dest_number[-1] == "!":
                            finishes_end = True
                            dest_number = dest_number[:-1]
                        # Add it
                        try:
                            self.add_outbound(
                                station.platforms[platform_number],
                                self.stations[dest_code].platforms[dest_number],
                                self.lines[parts[2]],
                                leaves_start = leaves_start,
                                finishes_end = finishes_end,
                                subtrack = (type == "subtrack"),
                            )
                        except:
                            print "Error context: %s, %s" % (station, parts)
                            raise

                    # Station/waypoint record
                    elif type in ("station", "waypoint", "depot", "sidings", "disstation"):
                        # It's a station or points definition
                        code = parts[0]
                        index = 1
                        while "," not in parts[index]:
                            index += 1
                        name = " ".join(parts[1:index])
                        # Work out the coordinates
                        coord_parts = parts[index].split(",")
                        coords = Vector(*map(float, coord_parts[-2:])) * 10
                        if len(coord_parts) == 3:
                            relative_to = self.stations[coord_parts[0]]
                        else:
                            relative_to = None
                        if type == "station":
                            station_class = Station
                        elif type == "depot":
                            station_class = Depot
                        elif type == "sidings":
                            station_class = Sidings
                        elif type == "disstation":
                            station_class = DisusedStation
                        else:
                            station_class = Points
                        last_station = self.stations[code] = station_class(
                            code,
                            name,
                            coords,
                            relative_to = relative_to,
                        )
                        self.extents[0] = min(coords.x, self.extents[0])
                        self.extents[1] = max(coords.x, self.extents[1])
                        self.extents[2] = min(coords.y, self.extents[2])
                        self.extents[3] = max(coords.y, self.extents[3])

                    # Platform record
                    elif type == "platform":
                        # Add a platform to the last station
                        direction = getattr(Direction, parts[1])
                        try:
                            line = self.lines[parts[2]]
                        except (IndexError, KeyError):
                            line = self.lines["error"]
                        try:
                            platform_side_code = parts[3]
                            platform_side = {
                                "L": Segment.PLATFORM_LEFT,
                                "R": Segment.PLATFORM_RIGHT,
                                "B": Segment.PLATFORM_BOTH,
                                "N": Segment.PLATFORM_NONE,
                            }[platform_side_code.upper()]
                        except IndexError:
                            platform_side = Segment.PLATFORM_BOTH
                        self.stations[code].add_platform(parts[0], direction, line, platform_side)

                    # Drawing order modifiers
                    elif type == "draw":
                        if parts[0] == "first":
                            draw_first.append(last_station)
                        elif parts[0] == "last":
                            draw_last.append(last_station)
                        else:
                            raise ValueError("Unknown draw position %r" % parts[0])

                    # Label placement modifiers
                    elif type == "label":
                        last_station.label_direction = getattr(Direction, parts[0])
                    elif type == "label_offset":
                        last_station.label_offset = Vector(map(int, parts[0].split(",")))

                    # Unknown
                    else:
                        raise ValueError("Unknown line type %r" % type)
        # Now reorder those with special draw clauses
        for station in draw_first:
            self.stations.insert(0, station.code, station)
        for station in draw_last:
            self.stations.insert(len(self.stations), station.code, station)

    def save_offsets(self, filename):
        """
        Opens up the file, reads it, and writes new offsets if needs be.
        """
        with open(filename) as in_file:
            with open(filename + ".new", "w") as out_file:
                for lineno, line in enumerate(in_file):
                    line = line.strip()
                    parts = line.split()
                    type = parts[0] if parts else None
                    # What kind of line is it?
                    if type in ("station", "waypoint", "depot", "sidings", "disstation"):
                        # Get the code
                        code = parts[1]
                        # Get the real station
                        station = self.stations[code]
                        if station.relative_to:
                            coords = "%s,%.1f,%.1f" % (
                                station.relative_to.code,
                                (station._offset.x // 5) / 2.0,
                                (station._offset.y // 5) / 2.0,
                            )
                        else:
                            coords = "%.1f,%.1f" % (
                                (station._offset.x // 5) / 2.0,
                                (station._offset.y // 5) / 2.0,
                            )
                        out_file.write("%(type)s %(code)s %(name)s %(coords)s\n" % {
                            "type": type,
                            "code": code,
                            "name": station.name,
                            "coords": coords,
                        })
                    else:
                        out_file.write(line + "\n")
        os.rename(filename + ".new", filename)

    def nearest_station(self, coords):
        """
        Finds the nearest station to the coords, and returns it along with the
        distance to it.
        """
        nearest = (None, 100000000)
        for station in self.stations.values():
            distance = abs(station.offset - coords)
            if distance < nearest[1]:
                nearest = (station, distance)
        return nearest

    def stations_inside_bounds(self, tl, br):
        """
        Finds the nearest station to the coords, and returns it along with the
        distance to it.
        """
        for station in self.stations.values():
            if tl.x <= station.offset.x <= br.x and \
               tl.y <= station.offset.y <= br.y:
                if not station.relative_to:
                    yield station

    def add_outbound(self, platform, destination, line, subtrack=False, leaves_start=False, finishes_end=False):
        self.outbounds.append((
            platform,
            destination,
            line,
            subtrack,
            leaves_start,
            finishes_end,
        ))
                            
    def draw(self, ctx):
        """
        Draws the entire map.
        """
        for station in self.stations.values():
            for platform in station.platforms.values():
                platform.drawn = False
        self.draw_outbound(ctx)
        for station in self.stations.values():
            station.draw(ctx)

    def draw_debug(self, ctx, highlighted=set()):
        """
        Draws debug hints for stations and platforms.
        """
        for station in self.stations.values():
            station.draw_debug(ctx, highlighted)

    def draw_outbound(self, ctx):
        # Draw outbound segments
        for platform, destination, line, subtrack, leaves_start, finishes_end in self.outbounds:
            # Draw the ends if they've not been done yet.
            if not platform.drawn:
                platform.draw(ctx)
            if not destination.drawn:
                destination.draw(ctx)
            # Make sure which ends we're using
            if leaves_start:
                start_point = platform.start_point
                start_dir = platform.direction.left.left.left.left
            else:
                start_point = platform.end_point
                start_dir = platform.direction
            if finishes_end:
                end_point = destination.end_point
                end_dir = destination.direction.left.left.left.left
            else:
                end_point = destination.start_point
                end_dir = destination.direction
            # Draw!
            Segment(
                start_point,
                start_dir,
                end_point,
                end_dir,
                line.colors,
                subtrack = subtrack,
            ).draw(ctx)

    def to_pdf(self, filename):
        width = (self.extents[1] - self.extents[0]) + self.padding * 2
        height = (self.extents[3] - self.extents[2]) + self.padding * 2
        surface = cairo.PDFSurface(filename, width, height)
        ctx = cairo.Context(surface)
        ctx.translate(
            self.padding - self.extents[0],
            self.padding - self.extents[2],
        )
        self.draw(ctx)
        surface.finish()
Ejemplo n.º 9
0
    def load(self, filename):
        self.stations = SortedDict()
        self.lines = SortedDict()
        self.extents = [0, 0, 0, 0]
        self.outbounds = []
        draw_last = []
        draw_first = []
        with open(filename) as fh:
            for lineno, line in enumerate(fh):
                line = line.strip()
                if line and line[0] != "#":
                    # Get the parts
                    parts = line.split()
                    type, parts = parts[0], parts[1:]
                    # What kind of line is it?
                    if type == "line":
                        # Line definition
                        code = parts[0]
                        colors = [(
                            int(part[0:2], 16) / 255.0,
                            int(part[2:4], 16) / 255.0,
                            int(part[4:8], 16) / 255.0,
                        ) for part in parts[1].split(",")]
                        self.lines[code] = Line(code, colors)

                    # Track segment
                    elif type in ("track", "subtrack"):
                        # It's a station-to-station description
                        station_code, platform_number = parts[0].split("-", 1)
                        dest_code, dest_number = parts[1].split("-", 1)
                        station = self.stations[station_code]
                        # Check for reverses
                        leaves_start = False
                        if platform_number[-1] == "!":
                            leaves_start = True
                            platform_number = platform_number[:-1]
                        finishes_end = False
                        if dest_number[-1] == "!":
                            finishes_end = True
                            dest_number = dest_number[:-1]
                        # Add it
                        try:
                            self.add_outbound(
                                station.platforms[platform_number],
                                self.stations[dest_code].
                                platforms[dest_number],
                                self.lines[parts[2]],
                                leaves_start=leaves_start,
                                finishes_end=finishes_end,
                                subtrack=(type == "subtrack"),
                            )
                        except:
                            print "Error context: %s, %s" % (station, parts)
                            raise

                    # Station/waypoint record
                    elif type in ("station", "waypoint", "depot", "sidings",
                                  "disstation"):
                        # It's a station or points definition
                        code = parts[0]
                        index = 1
                        while "," not in parts[index]:
                            index += 1
                        name = " ".join(parts[1:index])
                        # Work out the coordinates
                        coord_parts = parts[index].split(",")
                        coords = Vector(*map(float, coord_parts[-2:])) * 10
                        if len(coord_parts) == 3:
                            relative_to = self.stations[coord_parts[0]]
                        else:
                            relative_to = None
                        if type == "station":
                            station_class = Station
                        elif type == "depot":
                            station_class = Depot
                        elif type == "sidings":
                            station_class = Sidings
                        elif type == "disstation":
                            station_class = DisusedStation
                        else:
                            station_class = Points
                        last_station = self.stations[code] = station_class(
                            code,
                            name,
                            coords,
                            relative_to=relative_to,
                        )
                        self.extents[0] = min(coords.x, self.extents[0])
                        self.extents[1] = max(coords.x, self.extents[1])
                        self.extents[2] = min(coords.y, self.extents[2])
                        self.extents[3] = max(coords.y, self.extents[3])

                    # Platform record
                    elif type == "platform":
                        # Add a platform to the last station
                        direction = getattr(Direction, parts[1])
                        try:
                            line = self.lines[parts[2]]
                        except (IndexError, KeyError):
                            line = self.lines["error"]
                        try:
                            platform_side_code = parts[3]
                            platform_side = {
                                "L": Segment.PLATFORM_LEFT,
                                "R": Segment.PLATFORM_RIGHT,
                                "B": Segment.PLATFORM_BOTH,
                                "N": Segment.PLATFORM_NONE,
                            }[platform_side_code.upper()]
                        except IndexError:
                            platform_side = Segment.PLATFORM_BOTH
                        self.stations[code].add_platform(
                            parts[0], direction, line, platform_side)

                    # Drawing order modifiers
                    elif type == "draw":
                        if parts[0] == "first":
                            draw_first.append(last_station)
                        elif parts[0] == "last":
                            draw_last.append(last_station)
                        else:
                            raise ValueError("Unknown draw position %r" %
                                             parts[0])

                    # Label placement modifiers
                    elif type == "label":
                        last_station.label_direction = getattr(
                            Direction, parts[0])
                    elif type == "label_offset":
                        last_station.label_offset = Vector(
                            map(int, parts[0].split(",")))

                    # Unknown
                    else:
                        raise ValueError("Unknown line type %r" % type)
        # Now reorder those with special draw clauses
        for station in draw_first:
            self.stations.insert(0, station.code, station)
        for station in draw_last:
            self.stations.insert(len(self.stations), station.code, station)
Ejemplo n.º 10
0
class Map(object):

    padding = 50

    def __init__(self):
        pass

    def load(self, filename):
        self.stations = SortedDict()
        self.lines = SortedDict()
        self.extents = [0, 0, 0, 0]
        self.outbounds = []
        draw_last = []
        draw_first = []
        with open(filename) as fh:
            for lineno, line in enumerate(fh):
                line = line.strip()
                if line and line[0] != "#":
                    # Get the parts
                    parts = line.split()
                    type, parts = parts[0], parts[1:]
                    # What kind of line is it?
                    if type == "line":
                        # Line definition
                        code = parts[0]
                        colors = [(
                            int(part[0:2], 16) / 255.0,
                            int(part[2:4], 16) / 255.0,
                            int(part[4:8], 16) / 255.0,
                        ) for part in parts[1].split(",")]
                        self.lines[code] = Line(code, colors)

                    # Track segment
                    elif type in ("track", "subtrack"):
                        # It's a station-to-station description
                        station_code, platform_number = parts[0].split("-", 1)
                        dest_code, dest_number = parts[1].split("-", 1)
                        station = self.stations[station_code]
                        # Check for reverses
                        leaves_start = False
                        if platform_number[-1] == "!":
                            leaves_start = True
                            platform_number = platform_number[:-1]
                        finishes_end = False
                        if dest_number[-1] == "!":
                            finishes_end = True
                            dest_number = dest_number[:-1]
                        # Add it
                        try:
                            self.add_outbound(
                                station.platforms[platform_number],
                                self.stations[dest_code].
                                platforms[dest_number],
                                self.lines[parts[2]],
                                leaves_start=leaves_start,
                                finishes_end=finishes_end,
                                subtrack=(type == "subtrack"),
                            )
                        except:
                            print "Error context: %s, %s" % (station, parts)
                            raise

                    # Station/waypoint record
                    elif type in ("station", "waypoint", "depot", "sidings",
                                  "disstation"):
                        # It's a station or points definition
                        code = parts[0]
                        index = 1
                        while "," not in parts[index]:
                            index += 1
                        name = " ".join(parts[1:index])
                        # Work out the coordinates
                        coord_parts = parts[index].split(",")
                        coords = Vector(*map(float, coord_parts[-2:])) * 10
                        if len(coord_parts) == 3:
                            relative_to = self.stations[coord_parts[0]]
                        else:
                            relative_to = None
                        if type == "station":
                            station_class = Station
                        elif type == "depot":
                            station_class = Depot
                        elif type == "sidings":
                            station_class = Sidings
                        elif type == "disstation":
                            station_class = DisusedStation
                        else:
                            station_class = Points
                        last_station = self.stations[code] = station_class(
                            code,
                            name,
                            coords,
                            relative_to=relative_to,
                        )
                        self.extents[0] = min(coords.x, self.extents[0])
                        self.extents[1] = max(coords.x, self.extents[1])
                        self.extents[2] = min(coords.y, self.extents[2])
                        self.extents[3] = max(coords.y, self.extents[3])

                    # Platform record
                    elif type == "platform":
                        # Add a platform to the last station
                        direction = getattr(Direction, parts[1])
                        try:
                            line = self.lines[parts[2]]
                        except (IndexError, KeyError):
                            line = self.lines["error"]
                        try:
                            platform_side_code = parts[3]
                            platform_side = {
                                "L": Segment.PLATFORM_LEFT,
                                "R": Segment.PLATFORM_RIGHT,
                                "B": Segment.PLATFORM_BOTH,
                                "N": Segment.PLATFORM_NONE,
                            }[platform_side_code.upper()]
                        except IndexError:
                            platform_side = Segment.PLATFORM_BOTH
                        self.stations[code].add_platform(
                            parts[0], direction, line, platform_side)

                    # Drawing order modifiers
                    elif type == "draw":
                        if parts[0] == "first":
                            draw_first.append(last_station)
                        elif parts[0] == "last":
                            draw_last.append(last_station)
                        else:
                            raise ValueError("Unknown draw position %r" %
                                             parts[0])

                    # Label placement modifiers
                    elif type == "label":
                        last_station.label_direction = getattr(
                            Direction, parts[0])
                    elif type == "label_offset":
                        last_station.label_offset = Vector(
                            map(int, parts[0].split(",")))

                    # Unknown
                    else:
                        raise ValueError("Unknown line type %r" % type)
        # Now reorder those with special draw clauses
        for station in draw_first:
            self.stations.insert(0, station.code, station)
        for station in draw_last:
            self.stations.insert(len(self.stations), station.code, station)

    def save_offsets(self, filename):
        """
        Opens up the file, reads it, and writes new offsets if needs be.
        """
        with open(filename) as in_file:
            with open(filename + ".new", "w") as out_file:
                for lineno, line in enumerate(in_file):
                    line = line.strip()
                    parts = line.split()
                    type = parts[0] if parts else None
                    # What kind of line is it?
                    if type in ("station", "waypoint", "depot", "sidings",
                                "disstation"):
                        # Get the code
                        code = parts[1]
                        # Get the real station
                        station = self.stations[code]
                        if station.relative_to:
                            coords = "%s,%.1f,%.1f" % (
                                station.relative_to.code,
                                (station._offset.x // 5) / 2.0,
                                (station._offset.y // 5) / 2.0,
                            )
                        else:
                            coords = "%.1f,%.1f" % (
                                (station._offset.x // 5) / 2.0,
                                (station._offset.y // 5) / 2.0,
                            )
                        out_file.write(
                            "%(type)s %(code)s %(name)s %(coords)s\n" % {
                                "type": type,
                                "code": code,
                                "name": station.name,
                                "coords": coords,
                            })
                    else:
                        out_file.write(line + "\n")
        os.rename(filename + ".new", filename)

    def nearest_station(self, coords):
        """
        Finds the nearest station to the coords, and returns it along with the
        distance to it.
        """
        nearest = (None, 100000000)
        for station in self.stations.values():
            distance = abs(station.offset - coords)
            if distance < nearest[1]:
                nearest = (station, distance)
        return nearest

    def stations_inside_bounds(self, tl, br):
        """
        Finds the nearest station to the coords, and returns it along with the
        distance to it.
        """
        for station in self.stations.values():
            if tl.x <= station.offset.x <= br.x and \
               tl.y <= station.offset.y <= br.y:
                if not station.relative_to:
                    yield station

    def add_outbound(self,
                     platform,
                     destination,
                     line,
                     subtrack=False,
                     leaves_start=False,
                     finishes_end=False):
        self.outbounds.append((
            platform,
            destination,
            line,
            subtrack,
            leaves_start,
            finishes_end,
        ))

    def draw(self, ctx):
        """
        Draws the entire map.
        """
        for station in self.stations.values():
            for platform in station.platforms.values():
                platform.drawn = False
        self.draw_outbound(ctx)
        for station in self.stations.values():
            station.draw(ctx)

    def draw_debug(self, ctx, highlighted=set()):
        """
        Draws debug hints for stations and platforms.
        """
        for station in self.stations.values():
            station.draw_debug(ctx, highlighted)

    def draw_outbound(self, ctx):
        # Draw outbound segments
        for platform, destination, line, subtrack, leaves_start, finishes_end in self.outbounds:
            # Draw the ends if they've not been done yet.
            if not platform.drawn:
                platform.draw(ctx)
            if not destination.drawn:
                destination.draw(ctx)
            # Make sure which ends we're using
            if leaves_start:
                start_point = platform.start_point
                start_dir = platform.direction.left.left.left.left
            else:
                start_point = platform.end_point
                start_dir = platform.direction
            if finishes_end:
                end_point = destination.end_point
                end_dir = destination.direction.left.left.left.left
            else:
                end_point = destination.start_point
                end_dir = destination.direction
            # Draw!
            Segment(
                start_point,
                start_dir,
                end_point,
                end_dir,
                line.colors,
                subtrack=subtrack,
            ).draw(ctx)

    def to_pdf(self, filename):
        width = (self.extents[1] - self.extents[0]) + self.padding * 2
        height = (self.extents[3] - self.extents[2]) + self.padding * 2
        surface = cairo.PDFSurface(filename, width, height)
        ctx = cairo.Context(surface)
        ctx.translate(
            self.padding - self.extents[0],
            self.padding - self.extents[2],
        )
        self.draw(ctx)
        surface.finish()
Ejemplo n.º 11
0
class Station(object):
    """
    A place on the map that lines go to and from.
    """

    station_gap = 10
    platform_class = Platform
    label_color = (0, 51 / 255.0, 102 / 255.0)
    label_size = 12
    label_distance = Vector(6, 4)

    def __init__(self, code, name, offset, relative_to=None):
        self.code = code
        self.name = name
        self._offset = offset
        self.relative_to = relative_to
        self.platforms = SortedDict()
        self.placed = SortedDict()
        self.label_direction = None
        self.label_offset = Vector(0, 0)

    @property
    def offset(self):
        if self.relative_to:
            return self.relative_to.offset + self._offset
        else:
            return self._offset

    def add_platform(self, number, direction, line, platform_side):
        # Work out its coords
        norm_direction = direction.normalized
        self.placed[norm_direction] = self.placed.get(norm_direction, 0) + 1
        # Make it
        self.platforms[number] = self.platform_class(
            station=self,
            number=number,
            direction=direction,
            offset=Vector(0, 0),
            line=line,
            platform_side=platform_side,
        )
        self.platforms[number].offset_number = self.placed[norm_direction] - 1
        # Recalculate all platform locations
        for platform in self.platforms.values():
            norm_direction = platform.direction.normalized
            platform.offset = (norm_direction.right.right.vector *
                               (platform.offset_number + 0.5 -
                                (self.placed[norm_direction] / 2.0)) *
                               self.station_gap)

    def __repr__(self):
        return "<Station %s (%s)>" % (self.code, self.name)

    def decide_label_direction(self):
        self.label_direction = Direction.W

    def draw(self, ctx):
        """
        Draws the station and its platforms.
        """
        for platform in self.platforms.values():
            if not platform.drawn:
                platform.draw(ctx)
        if not self.label_direction:
            self.decide_label_direction()
        self.draw_label(ctx)

    def draw_debug(self, ctx, highlighted):
        """
        Draws a debug symbol for this station.
        """
        # Draw a cross on the station
        ctx.save()
        if self in highlighted:
            ctx.set_source_rgba(100, 0, 0, 0.9)
        else:
            ctx.set_source_rgba(255, 0, 255, 0.6)
        ctx.translate(*self.offset)
        ctx.move_to(-5, -5)
        ctx.line_to(5, 5)
        ctx.move_to(5, -5)
        ctx.line_to(-5, 5)
        ctx.set_line_width(2)
        ctx.stroke()
        # Draw the name of the station/waypoint
        ctx.select_font_face(
            "LondonTwo",
            cairo.FONT_SLANT_NORMAL,
            cairo.FONT_WEIGHT_NORMAL,
        )
        ctx.set_font_size(3)
        ctx.move_to(6, -3)
        ctx.show_text(self.code)
        ctx.fill()
        # Draw a dot on each platform
        for platform in self.platforms.values():
            ctx.save()
            ctx.translate(*platform.offset)
            ctx.arc(0, 0, 3, 0, 7)
            ctx.fill()
            ctx.restore()
        # Draw a number on each platform
        for platform in self.platforms.values():
            ctx.save()
            ctx.translate(*platform.offset)
            ctx.set_source_rgb(0, 0, 100)
            ctx.select_font_face(
                "LondonTwo",
                cairo.FONT_SLANT_NORMAL,
                cairo.FONT_WEIGHT_NORMAL,
            )
            ctx.set_font_size(4)
            ctx.move_to(-1, 1)
            ctx.show_text(platform.number)
            ctx.restore()
        ctx.restore()

    def draw_label(self, ctx):
        if self.name:
            # Work out the bounding box of the platforms
            x_range = [0, 0]
            y_range = [0, 0]
            platform_directions = set()
            label_dir = self.label_direction
            for platform in self.platforms.values():
                platform_directions.add(platform.direction)
                # Diagonal platforms perpendicular to label direction
                # get put closer
                if platform.direction == label_dir.right.right or \
                   platform.direction == label_dir.left.left:
                    ends = [platform.mid_point]
                    if platform.platform_side & Segment.PLATFORM_LEFT:
                        ends.append(platform.mid_point +
                                    (platform.direction.left.left.vector *
                                     Segment.platform_distance))
                    if platform.platform_side & Segment.PLATFORM_RIGHT:
                        ends.append(platform.mid_point +
                                    (platform.direction.right.right.vector *
                                     Segment.platform_distance))
                # Use bounding box
                else:
                    ends = [platform.start_point, platform.end_point]
                    if platform.platform_side & Segment.PLATFORM_LEFT:
                        ends.append(platform.start_point +
                                    (platform.direction.left.left.vector *
                                     Segment.platform_distance))
                        ends.append(platform.end_point +
                                    (platform.direction.left.left.vector *
                                     Segment.platform_distance))
                    if platform.platform_side & Segment.PLATFORM_RIGHT:
                        ends.append(platform.start_point +
                                    (platform.direction.right.right.vector *
                                     Segment.platform_distance))
                        ends.append(platform.end_point +
                                    (platform.direction.right.right.vector *
                                     Segment.platform_distance))
                for end in ends:
                    end = end - self.offset
                    x_range[0] = min(end.x, x_range[0])
                    y_range[0] = min(end.y, y_range[0])
                    x_range[1] = max(end.x, x_range[1])
                    y_range[1] = max(end.y, y_range[1])
            ctx.set_source_rgb(*self.label_color)
            ctx.select_font_face(
                "LondonTwo",
                cairo.FONT_SLANT_NORMAL,
                cairo.FONT_WEIGHT_NORMAL,
            )
            ctx.set_font_size(self.label_size)
            lines = [{"text": x.strip()} for x in self.name.split("\\n")]
            # Work out the size of the entire label
            dir_vector = self.label_direction.vector
            width = 0
            height = 0
            for line in lines:
                x_bearing, y_bearing, this_width, this_height = \
                    ctx.text_extents(line['text'])[:4]
                width = max(width, this_width)
                height += this_height
                line['y'] = height
                line['height'] = this_height
                line['width'] = this_width
                line['x_bearing'] = x_bearing
                line['y_bearing'] = y_bearing
                height += 1
            height -= 1
            # Work out where to place it, using the text midpoint as the origin
            if dir_vector.x < 0:
                x_offset = x_range[0] - width / 2.0
                x_mult = 1
            elif dir_vector.x == 0:
                x_offset = 0
                x_mult = 0.5
            else:
                x_offset = x_range[1] + width / 2.0
                x_mult = 0
            if dir_vector.y < 0:
                y_offset = y_range[0] - height / 2.0
                y_delta = -ctx.font_extents()[1] * 0.6
            elif dir_vector.y == 0:
                y_offset = 0
                y_delta = 0
            else:
                y_offset = y_range[1] + height / 2.0
                y_delta = 0
            y_offset -= (self.label_size / 8.0)
            # Draw!
            for line in lines:
                line_x = -line['x_bearing'] + (
                    -width / 2.0) - (line['width'] - width) * x_mult
                line_y = y_delta + line['y'] - (height / 2.0)
                position = (Vector(x_offset, y_offset) +
                            Vector(line_x, line_y) + Vector(
                                dir_vector.x * self.label_distance.x,
                                dir_vector.y * self.label_distance.y,
                            ) + self.offset + self.label_offset)
                ctx.move_to(*position)
                ctx.show_text(line['text'])
            if False:  # debug
                ctx.fill()
                ctx.set_source_rgb(0, 1, 1)
                ctx.arc(self.offset.x + x_offset, self.offset.y + y_offset, 2,
                        0, 7)
                ctx.fill()
                ctx.set_source_rgb(0, 0.5, 1)
                ctx.arc(self.offset.x + x_range[1], self.offset.y + y_range[1],
                        2, 0, 7)
                ctx.fill()
                ctx.set_source_rgb(1, 0, 1)
                ctx.arc(self.offset.x + x_range[0], self.offset.y + y_range[0],
                        2, 0, 7)
                ctx.fill()
Ejemplo n.º 12
0
class Station(object):
    """
    A place on the map that lines go to and from.
    """

    station_gap = 10
    platform_class = Platform
    label_color = (0, 51/255.0, 102/255.0)
    label_size = 12
    label_distance = Vector(6, 4)

    def __init__(self, code, name, offset, relative_to=None):
        self.code = code
        self.name = name
        self._offset = offset
        self.relative_to = relative_to
        self.platforms = SortedDict()
        self.placed = SortedDict()
        self.label_direction = None
        self.label_offset = Vector(0, 0)

    @property
    def offset(self):
        if self.relative_to:
            return self.relative_to.offset + self._offset
        else:
            return self._offset

    def add_platform(self, number, direction, line, platform_side):
        # Work out its coords
        norm_direction = direction.normalized
        self.placed[norm_direction] = self.placed.get(norm_direction, 0) + 1
        # Make it
        self.platforms[number] = self.platform_class(
            station = self,
            number = number,
            direction = direction,
            offset = Vector(0, 0),
            line = line,
            platform_side = platform_side,
        )
        self.platforms[number].offset_number = self.placed[norm_direction] - 1
        # Recalculate all platform locations
        for platform in self.platforms.values():
            norm_direction = platform.direction.normalized
            platform.offset = (
                norm_direction.right.right.vector *
                (platform.offset_number + 0.5 - (self.placed[norm_direction] / 2.0)) *
                self.station_gap
            )

    def __repr__(self):
        return "<Station %s (%s)>" % (self.code, self.name)

    def decide_label_direction(self):
        self.label_direction = Direction.W

    def draw(self, ctx):
        """
        Draws the station and its platforms.
        """
        for platform in self.platforms.values():
            if not platform.drawn:
                platform.draw(ctx)
        if not self.label_direction:
            self.decide_label_direction()
        self.draw_label(ctx)

    def draw_debug(self, ctx, highlighted):
        """
        Draws a debug symbol for this station.
        """
        # Draw a cross on the station
        ctx.save()
        if self in highlighted:
            ctx.set_source_rgba(100, 0, 0, 0.9)
        else:
            ctx.set_source_rgba(255, 0, 255, 0.6)
        ctx.translate(*self.offset)
        ctx.move_to(-5, -5)
        ctx.line_to(5, 5)
        ctx.move_to(5, -5)
        ctx.line_to(-5, 5)
        ctx.set_line_width(2)
        ctx.stroke()
        # Draw a dot on each platform
        for platform in self.platforms.values():
            ctx.save()
            ctx.translate(*platform.offset)
            ctx.arc(0, 0, 3, 0, 7)
            ctx.fill()
            ctx.restore()
        # Draw a number on each platform
        for platform in self.platforms.values():
            ctx.save()
            ctx.translate(*platform.offset)
            ctx.set_source_rgb(0, 0, 100)
            ctx.select_font_face(
                "LondonTwo",
                cairo.FONT_SLANT_NORMAL,
                cairo.FONT_WEIGHT_NORMAL,
            )
            ctx.set_font_size(4)
            ctx.move_to(-1, 1)
            ctx.show_text(platform.number)
            ctx.restore()
        ctx.restore()


    def draw_label(self, ctx):
        if self.name:
            # Work out the bounding box of the platforms
            x_range = [0, 0]
            y_range = [0, 0]
            platform_directions = set()
            label_dir = self.label_direction
            for platform in self.platforms.values():
                platform_directions.add(platform.direction)
                # Diagonal platforms perpendicular to label direction
                # get put closer
                if platform.direction == label_dir.right.right or \
                   platform.direction == label_dir.left.left:
                    ends = [platform.mid_point]
                    if platform.platform_side & Segment.PLATFORM_LEFT:
                        ends.append(
                            platform.mid_point +
                            (platform.direction.left.left.vector * Segment.platform_distance)
                        )
                    if platform.platform_side & Segment.PLATFORM_RIGHT:
                        ends.append(
                            platform.mid_point +
                            (platform.direction.right.right.vector * Segment.platform_distance)
                        )
                # Use bounding box
                else:
                    ends = [platform.start_point, platform.end_point]
                    if platform.platform_side & Segment.PLATFORM_LEFT:
                        ends.append(
                            platform.start_point +
                            (platform.direction.left.left.vector * Segment.platform_distance)
                        )
                        ends.append(
                            platform.end_point +
                            (platform.direction.left.left.vector * Segment.platform_distance)
                        )
                    if platform.platform_side & Segment.PLATFORM_RIGHT:
                        ends.append(
                            platform.start_point +
                            (platform.direction.right.right.vector * Segment.platform_distance)
                        )
                        ends.append(
                            platform.end_point +
                            (platform.direction.right.right.vector * Segment.platform_distance)
                        )
                for end in ends:
                    end = end - self.offset
                    x_range[0] = min(end.x, x_range[0])
                    y_range[0] = min(end.y, y_range[0])
                    x_range[1] = max(end.x, x_range[1])
                    y_range[1] = max(end.y, y_range[1])
            ctx.set_source_rgb(*self.label_color)
            ctx.select_font_face(
                "LondonTwo",
                cairo.FONT_SLANT_NORMAL,
                cairo.FONT_WEIGHT_NORMAL,
            )
            ctx.set_font_size(self.label_size)
            lines = [{"text": x.strip()} for x in self.name.split("\\n")]
            # Work out the size of the entire label
            dir_vector = self.label_direction.vector
            width = 0
            height = 0
            for line in lines:
                x_bearing, y_bearing, this_width, this_height = \
                    ctx.text_extents(line['text'])[:4]
                width = max(width, this_width)
                height += this_height
                line['y'] = height
                line['height'] = this_height
                line['width'] = this_width
                line['x_bearing'] = x_bearing
                line['y_bearing'] = y_bearing
                height += 1
            height -= 1
            # Work out where to place it, using the text midpoint as the origin
            if dir_vector.x < 0:
                x_offset = x_range[0] - width / 2.0
                x_mult = 1
            elif dir_vector.x == 0:
                x_offset = 0
                x_mult = 0.5
            else:
                x_offset = x_range[1] + width / 2.0
                x_mult = 0
            if dir_vector.y < 0:
                y_offset = y_range[0] - height / 2.0
                y_delta = -ctx.font_extents()[1] * 0.6
            elif dir_vector.y == 0:
                y_offset = 0
                y_delta = 0
            else:
                y_offset = y_range[1] + height / 2.0
                y_delta = 0
            y_offset -= (self.label_size / 8.0)
            # Draw!
            for line in lines:
                line_x = -line['x_bearing'] + (-width/2.0) - (line['width'] - width) * x_mult
                line_y = y_delta + line['y'] - (height / 2.0)
                position = (
                    Vector(x_offset, y_offset) +
                    Vector(line_x, line_y) +
                    Vector(
                        dir_vector.x * self.label_distance.x,
                        dir_vector.y * self.label_distance.y,
                    ) +
                    self.offset +
                    self.label_offset
                )
                ctx.move_to(*position)
                ctx.show_text(line['text'])
            if False: # debug
                ctx.fill()
                ctx.set_source_rgb(0,1,1)
                ctx.arc(self.offset.x + x_offset, self.offset.y + y_offset, 2, 0, 7)
                ctx.fill()
                ctx.set_source_rgb(0,0.5,1)
                ctx.arc(self.offset.x + x_range[1], self.offset.y + y_range[1], 2, 0, 7)
                ctx.fill()
                ctx.set_source_rgb(1,0,1)
                ctx.arc(self.offset.x + x_range[0], self.offset.y + y_range[0], 2, 0, 7)
                ctx.fill()