import attr from collections import OrderedDict from collections.abc import Iterable @attr.s(slots=True) class RPC(object): """Rational Polynomial Coefficients used to map (x, y, z) <-> (row, col) coordinates. This class contains a mapping between various RPC attributes and values. Attributes ---------- err_bias, err_rand: float, optional The RMS bias and random error in meters per horizontal axis of all points in image. lat_off, long_off, height_off: float Geodetic latitude, longitude, and height offset. lat_scale, long_scale, height_scale: float Geodetic latitude, longitude, and height scaling. line_off, samp_off: float Line (row) and sample (column) offset. line_scale, samp_scale: float Line (row) and sample (column) offset. line_num_coeff, line_den_coeff, samp_num_coeff, samp_den_coeff: list The twenty coefficients describing a numerator or denominator polynomial corresponding to line (row) or sample (col). """ height_off = attr.ib() height_scale = attr.ib() lat_off = attr.ib() lat_scale = attr.ib() line_den_coeff = attr.ib() line_num_coeff = attr.ib() line_off = attr.ib() line_scale = attr.ib() long_off = attr.ib() long_scale = attr.ib() samp_den_coeff = attr.ib() samp_num_coeff = attr.ib() samp_off = attr.ib() samp_scale = attr.ib() err_bias = attr.ib(default=None) err_rand = attr.ib(default=None) def to_dict(self): """Return a dictionary representation of RPC""" return attr.asdict(self) def to_gdal(self): """Serialize RPC attribute name and values in a form expected by GDAL. Returns ------- dict Notes ----- The `err_bias` and `err_rand` are optional, and are not written to datasets by GDAL. """ out = { "HEIGHT_OFF": str(self.height_off), "HEIGHT_SCALE": str(self.height_scale), "LAT_OFF": str(self.lat_off), "LAT_SCALE": str(self.lat_scale), "LINE_DEN_COEFF": " ".join(map(str, self.line_den_coeff)), "LINE_NUM_COEFF": " ".join(map(str, self.line_num_coeff)), "LINE_OFF": str(self.line_off), "LINE_SCALE": str(self.line_scale), "LONG_OFF": str(self.long_off), "LONG_SCALE": str(self.long_scale), "SAMP_DEN_COEFF": " ".join(map(str, self.samp_den_coeff)), "SAMP_NUM_COEFF": " ".join(map(str, self.samp_num_coeff)), "SAMP_OFF": str(self.samp_off), "SAMP_SCALE": str(self.samp_scale) } if self.err_bias: out.update(ERR_BIAS=str(self.err_bias)) if self.err_rand: out.update(ERR_RAND=str(self.err_rand)) return out @classmethod def from_gdal(cls, rpcs): """Deserialize dict values to float or list. Returns ------- RPC """ out = {} for key, val in rpcs.items(): vals = val.split() if len(vals) > 1: out[key] = [float(v) for v in vals] else: out[key] = float(vals[0]) return cls( err_bias=out.get("ERR_BIAS"), err_rand=out.get("ERR_RAND"), height_off=out["HEIGHT_OFF"], height_scale=out["HEIGHT_SCALE"], lat_off=out["LAT_OFF"], lat_scale=out["LAT_SCALE"], line_den_coeff=out["LINE_DEN_COEFF"], line_num_coeff=out["LINE_NUM_COEFF"], line_off=out["LINE_OFF"], line_scale=out["LINE_SCALE"], long_off=out["LONG_OFF"], long_scale=out["LONG_SCALE"], samp_den_coeff=out["SAMP_DEN_COEFF"], samp_num_coeff=out["SAMP_NUM_COEFF"], samp_off=out["SAMP_OFF"], samp_scale=out["SAMP_SCALE"], )