def from_file(cls, path, *args, **kwargs):
     with open(path, "r") as f:
         d = json.load(f)
     points = d["points"]
     points.append(points[0])  # Close the loop
     points = interpolate_points(points, 1000)
     return cls(points, *args, **kwargs)
    def __init__(self, pts, nb_checkpoints=100, render_params=None):
        l = LineString(pts).length
        n = int(l / 3e-3)  # Get number of points for 3 mm spacing

        self.pts = interpolate_points(
            np.array(pts), n)  # interpolate points to get the right spacing
        self.x = self.pts[:, 0]
        self.y = self.pts[:, 1]

        self.render_params = render_params

        self.mpt = MultiPoint(self.pts)
        self.string = LineString(self.pts)

        # Find starting point and angle
        self.start_xy = self.x[0], self.y[0]
        self.start_angle = self.angle_at_index(0)

        # Get length
        self.length = self.string.length

        # Progress tracking setup
        self.progress = 0.
        self.progress_idx = 0
        self.nb_checkpoints = nb_checkpoints
        self.checkpoints = [
            i * (self.length / self.nb_checkpoints)
            for i in range(1, self.nb_checkpoints + 1)
        ]
        self.next_checkpoint_idx = 0
        self.done = False
    def _render(self,
                w=3.,
                h=2.,
                ppm=1500,
                line_thickness=0.015,
                save=None,
                line_color="black",
                background="white",
                line_opacity=0.8,
                dashed=False):
        """
        Render track using open-cv
        :param w: canvas width in meters
        :param h: canvas height in meters
        :param ppm: pixel per meter
        :param line_thickness: line thickness in meters
        :param save: path to save
        :param line_color: string or BGR tuple
                           options: [black, red, green, blue]
        :param background: string or BGR tuple
                           options: [wood, wood_2, concrete, brick, checkerboard, white, gray]
        :param line_opacity: opacity of line in range 0, 1 where 0 is fully transparent
        :return: rendered track image array
        """
        import cv2
        w_res = int(round(w * ppm))
        h_res = int(round(h * ppm))
        t_res = int(round(line_thickness * ppm))

        background_bgr = None
        if isinstance(background, str):
            background = background.lower()
            if background == "wood":
                bg = cv2.imread(
                    os.path.join(root_dir, "track_textures", "wood.jpg"))
            elif background == "wood_2":
                bg = cv2.imread(
                    os.path.join(root_dir, "track_textures", "wood_2.jpg"))
            elif background == "concrete":
                bg = cv2.imread(
                    os.path.join(root_dir, "track_textures", "concrete.jpg"))
            elif background == "brick":
                bg = cv2.imread(
                    os.path.join(root_dir, "track_textures", "brick.jpg"))
            elif background == "checkerboard":
                bg = cv2.imread(
                    os.path.join(root_dir, "track_textures",
                                 "checkerboard.jpg"))
            elif background == "white":
                background_bgr = (255, 255, 255)
            elif background == "gray":
                background_bgr = (150, 150, 150)
            else:
                raise ValueError("Invalid background string.")

            if background_bgr:
                bg = np.ones((h_res, w_res, 3), dtype=np.uint8)
                bg[:, :, 0] *= background_bgr[0]
                bg[:, :, 1] *= background_bgr[1]
                bg[:, :, 2] *= background_bgr[2]
            else:
                bg = cv2.resize(bg, (w_res, h_res),
                                interpolation=cv2.INTER_LINEAR)

        elif isinstance(background, tuple):
            bg = np.ones((h_res, w_res, 3), dtype=np.uint8)
            bg[:, :, 0] *= background[0]
            bg[:, :, 1] *= background[1]
            bg[:, :, 2] *= background[2]
        else:
            raise ValueError("Invalid background.")

        if isinstance(line_color, str):
            line_color = line_color.lower()
            if line_color == "black":
                line_bgr = (0, 0, 0)
            elif line_color == "red":
                line_bgr = (0, 0, 255)
            elif line_color == "green":
                line_bgr = (0, 128, 0)
            elif line_color == "blue":
                line_bgr = (255, 0, 0)
            else:
                raise ValueError("Invalid color string.")
        elif isinstance(line_color, tuple):
            line_bgr = line_color
        else:
            raise ValueError("Invalid line_color.")

        line = bg.copy()

        if dashed:
            pts = interpolate_points(self.pts, 1000)
            n = self.length / dashed
            chunks = np.array_split(pts, n)[::2]
            for c in chunks:
                for i in range(len(c) - 1):
                    x1, y1 = c[i]
                    x1_img = int(round((x1 + w / 2) * ppm, ndigits=0))
                    y1_img = int(round(h_res - (y1 + h / 2) * ppm, ndigits=0))

                    x2, y2 = c[i + 1]
                    x2_img = int(round((x2 + w / 2) * ppm, ndigits=0))
                    y2_img = int(round(h_res - (y2 + h / 2) * ppm, ndigits=0))
                    cv2.line(line, (x1_img, y1_img), (x2_img, y2_img),
                             color=line_bgr,
                             thickness=t_res,
                             lineType=cv2.LINE_AA)
        else:
            for i in range(len(self.pts) - 1):
                x1, y1 = self.pts[i]
                x1_img = int(round((x1 + w / 2) * ppm, ndigits=0))
                y1_img = int(round(h_res - (y1 + h / 2) * ppm, ndigits=0))

                x2, y2 = self.pts[i + 1]
                x2_img = int(round((x2 + w / 2) * ppm, ndigits=0))
                y2_img = int(round(h_res - (y2 + h / 2) * ppm, ndigits=0))

                cv2.line(line, (x1_img, y1_img), (x2_img, y2_img),
                         color=line_bgr,
                         thickness=t_res,
                         lineType=cv2.LINE_AA)

        alpha = line_opacity
        out = cv2.addWeighted(line, alpha, bg, 1 - alpha, 0)

        if save is not None:
            cv2.imwrite(save, out)
        return out