def locdir_at(self, phi, theta, psi, row, col, alt=0, geo=True):
        """
        Given the satellite attitude, computes lon, lat of point row, col, alt.

        Args:
            phi, theta, psi: attitude angles of the satellite camera
            row, col: pixel coordinates in the image plane
            alt: altitude of the 3d point above the Earth surface
            geo: boolean flag telling whether to return geographic
                coordinates or Cartesian coordinates.

        Returns:
            geographic coordinates (lon, lat), or Cartesian coordinates
            (x, y, z). Cartesian coordinates are computed with respect to the
            orbital frame.
        """
        # first compute coordinates of the 3-space point in the orbital frame
        orbital_to_camera = utils.rotation_3d('xyz', phi, theta, psi)
        v = np.dot(orbital_to_camera, self.instrument.sight_vector(col))
        c = np.array([0, 0, self.orbit.radius])
        r = PhysicalConstants.earth_radius + alt
        p = utils.intersect_line_and_sphere(v, c, r)
        if not geo:
            return p

        # then convert them to lon, lat, using t to get the satellite position
        if p is None:
            return p
        else:
            t = row * self.instrument.dwell_time
            mat_t_ol = self.rotational_to_orbital(t)
            return utils.lon_lat(np.dot(mat_t_ol, -c + p))
    def locdir_at(self, phi, theta, psi, row, col, alt=0, geo=True):
        """
        Given the satellite attitude, computes lon, lat of point row, col, alt.

        Args:
            phi, theta, psi: attitude angles of the satellite camera
            row, col: pixel coordinates in the image plane
            alt: altitude of the 3d point above the Earth surface
            geo: boolean flag telling whether to return geographic
                coordinates or Cartesian coordinates.

        Returns:
            geographic coordinates (lon, lat), or Cartesian coordinates
            (x, y, z). Cartesian coordinates are computed with respect to the
            orbital frame.
        """
        # first compute coordinates of the 3-space point in the orbital frame
        orbital_to_camera = utils.rotation_3d('xyz', phi, theta, psi)
        v = np.dot(orbital_to_camera, self.instrument.sight_vector(col))
        c = np.array([0, 0, self.orbit.radius])
        r = PhysicalConstants.earth_radius + alt
        p = utils.intersect_line_and_sphere(v, c, r)
        if not geo:
            return p

        # then convert them to lon, lat, using t to get the satellite position
        if p is None:
            return p
        else:
            t = row * self.instrument.dwell_time
            mat_t_ol = self.rotational_to_orbital(t)
            return utils.lon_lat(np.dot(mat_t_ol, -c + p))
    def pushbroom_direction_on_the_ground(self, cap, p, t):
        """
        Computes the projection of the pushbroom direction on the ground.

        Args:
            cap: direction of the pushbroom array movement, on the ground, in
                degrees. North is 0, Sud is 180 and East is 90.
            p: orbital coordinates of the ground point currently targeted
            t: time elapsed since the beginning of the acquisition, in seconds

        Returns:
            the orbital coordinates of a vector giving the pushbroom movement
            direction, projected on the ground.
        """
        # coordinates of the Earth center in the orbital frame
        c = np.array([0, 0, self.orbit.radius])

        # direction of the pushbroom movement, projected on the ground, in
        # local geographic coordinates (z, easting, northing)
        v = np.array([0, np.sin(cap * np.pi/180), np.cos(cap * np.pi/180)])

        # convert v from local geographic coordinates to orbital coordinates
        rot_to_orb = self.rotational_to_orbital(t)
        p_rot = np.dot(rot_to_orb, -c + p)
        lon, lat = utils.lon_lat(p_rot)
        rot_to_geo = utils.rotational_to_local_geographic(lon, lat)
        return np.dot(rot_to_orb.T, np.dot(rot_to_geo, v))
    def pushbroom_direction_on_the_ground(self, cap, p, t):
        """
        Computes the projection of the pushbroom direction on the ground.

        Args:
            cap: direction of the pushbroom array movement, on the ground, in
                degrees. North is 0, Sud is 180 and East is 90.
            p: orbital coordinates of the ground point currently targeted
            t: time elapsed since the beginning of the acquisition, in seconds

        Returns:
            the orbital coordinates of a vector giving the pushbroom movement
            direction, projected on the ground.
        """
        # coordinates of the Earth center in the orbital frame
        c = np.array([0, 0, self.orbit.radius])

        # direction of the pushbroom movement, projected on the ground, in
        # local geographic coordinates (z, easting, northing)
        v = np.array([0, np.sin(cap * np.pi / 180), np.cos(cap * np.pi / 180)])

        # convert v from local geographic coordinates to orbital coordinates
        rot_to_orb = self.rotational_to_orbital(t)
        p_rot = np.dot(rot_to_orb, -c + p)
        lon, lat = utils.lon_lat(p_rot)
        rot_to_geo = utils.rotational_to_local_geographic(lon, lat)
        return np.dot(rot_to_orb.T, np.dot(rot_to_geo, v))