<commit_before>from xml.etree.ElementTree import ElementTree

def apply_poly(poly, x, y, z):
    return poly[0] +\
           poly[1]*y + poly[2]*x + poly[3]*z +\
           poly[4]*y*x + poly[5]*y*z +poly[6]*x*z +\
           poly[7]*y*y + poly[8]*x*x + poly[9]*z*z +\
           poly[10]*x*y*z +\
           poly[11]*y*y*y +\
           poly[12]*y*x*x + poly[13]*y*z*z + poly[14]*y*y*x +\
           poly[15]*x*x*x +\
           poly[16]*x*z*z + poly[17]*y*y*z + poly[18]*x*x*z +\
           poly[19]*z*z*z

def apply_rfm( num, den, x, y, z):
    return apply_poly(num, x, y, z) / apply_poly(den, x, y, z)

class RPCModel:
    def __init__(self, XMLfile):
        self.read_rpc(XMLfile)

    def read_rpc(self,XMLfile):
        self.filepath = XMLfile
        tree = ElementTree()
        tree.parse(XMLfile)
        # direct model
        d=tree.find('Rational_Function_Model/Global_RFM/Direct_Model')
        direct=[float(child.text) for child in d]
        self.directLonNum,self.directLonDen,self.directLatNum,self.directLatDen,self.directBias=direct[:20],direct[20:40],direct[40:60],direct[60:80],direct[80:]
        # inverse model
        i=tree.find('Rational_Function_Model/Global_RFM/Inverse_Model')
        inverse=[float(child.text) for child in i]
        self.inverseColNum,self.inverseColDen,self.inverseLinNum,self.inverseLinDen,self.inverseBias=inverse[:20],inverse[20:40],inverse[40:60],inverse[60:80],inverse[80:]
        # validity domains
        v = tree.find('Rational_Function_Model/Global_RFM/RFM_Validity')
        vd=v.find('Direct_Model_Validity_Domain')
        self.firstRow, self.firstCol, self.lastRow, self.lastCol=[float(vd.find(s).text) for s in 'FIRST_ROW', 'FIRST_COL', 'LAST_ROW', 'LAST_COL']
        vi=v.find('Inverse_Model_Validity_Domain')
        self.firstLon, self.firstLat, self.lastLon, self.lastLat= [float(vi.find(s).text) for s in 'FIRST_LON', 'FIRST_LAT', 'LAST_LON', 'LAST_LAT']
        #  scale and offset
        self.lonScale,self.lonOff,self.latScale,self.latOff,self.altScale,self.altOff,self.colScale,self.colOff,self.linScale,self.linOff = [float(v.find(s).text) for s in 'LONG_SCALE', 'LONG_OFF', 'LAT_SCALE', 'LAT_OFF', 'HEIGHT_SCALE', 'HEIGHT_OFF', 'SAMP_SCALE', 'SAMP_OFF', 'LINE_SCALE', 'LINE_OFF']

    def direct_estimate(self, col, lin, alt, return_normalized=False):
        cCol, cLin, cAlt=(col-self.colOff)/self.colScale, (lin-self.linOff)/self.linScale, (alt-self.altOff)/self.altScale
        cLon,cLat=apply_rfm(self.directLonNum, self.directLonDen, cLin, cCol, cAlt), apply_rfm(self.directLatNum, self.directLatDen, cLin, cCol, cAlt),
        lon,lat=cLon*self.lonScale+self.lonOff, cLat*self.latScale+self.latOff
        if return_normalized:
           return cLon,cLat,cAlt
        return lon,lat,alt

    def inverse_estimate(self, lon, lat, alt):
        cLon, cLat, cAlt=(lon-self.lonOff)/self.lonScale, (lat-self.latOff)/self.latScale, (alt-self.altOff)/self.altScale
        cCol,cLin=apply_rfm(self.inverseColNum, self.inverseColDen, cLat, cLon, cAlt), apply_rfm(self.inverseLinNum, self.inverseLinDen, cLat, cLon, cAlt),
        col,lin=cCol*self.colScale+self.colOff, cLin*self.linScale+self.linOff
        return col,lin,alt

    def __repr__(self):
        return '''
    ### Direct Model ###
        directLonNum={directLonNum}
        directLonDen={directLonDen}
        directLatNum={directLatNum}
        directLatDen={directLatDen}
        directBias={directBias}

    ### Inverse Model ###
        inverseColNum={inverseColNum}
        inverseColDen={inverseColDen}
        inverseLinNum={inverseLinNum}
        inverseLinDen={inverseLinDen}
        inverseBias={inverseBias}

    ### Validity Domains ###
        firstLon={firstLon}
        firstLat={firstLat}
        lastLon={lastLon}
        lastLat={lastLat}

    ### Scale and Offsets ###
        lonScale={lonScale}
        lonOff={lonOff}
        latScale={latScale}
        latOff={latOff}
        altScale={altScale}
        altOff={altOff}
        colScale={colScale}
        colOff={colOff}
        linScale={linScale}
        linOff={linOff}'''.format(directLonNum=self.directLonNum,directLonDen=self.directLonDen,directLatNum=self.directLatNum,directLatDen=self.directLatDen,directBias=self.directBias,
                                  inverseColNum=self.inverseColNum,inverseColDen=self.inverseColDen,inverseLinNum=self.inverseLinNum,inverseLinDen=self.inverseLinDen,inverseBias=self.inverseBias,
                                  firstLon=self.firstLon, firstLat=self.firstLat, lastLon=self.lastLon, lastLat=self.lastLat,
                                  lonScale=self.lonScale,lonOff=self.lonOff,latScale=self.latScale,latOff=self.latOff,altScale=self.altScale,altOff=self.altOff,colScale=self.colScale,colOff=self.colOff,linScale=self.linScale,linOff=self.linOff)


if __name__ == '__main__':
    # test on the first melbourne image
    rpc = RPCModel('../testdata/RPC_PHR1A_P_201202250024153_SEN_IPU_20120229_8029-001.XML')
    col, lin = 20000, 8000
    alt = 90 # I don't know what to put here
    print 'col={col}, lin={lin}, alt={alt}'.format(col=col, lin=lin, alt=alt)
    lon, lat, alt = rpc.direct_estimate(col, lin, alt)
    print 'lon={lon}, lat={lat}, alt={alt}'.format(lon=lon, lat=lat, alt=alt)
    col, lin, alt = rpc.inverse_estimate(lon, lat, alt)
    print 'col={col}, lin={lin}, alt={alt}'.format(col=col, lin=lin, alt=alt)<commit_msg>made the code of rpc_model.py easier to read<commit_after>from xml.etree.ElementTree import ElementTree

def apply_poly(poly, x, y, z):
    #TODO: use numpy poly3d function. Test if its faster
    """
    Evaluates a 3-variate polynom of degree 3, on a triplet of numbers.

    Args:
        poly: list of the 20 coefficients of the 3-variate degree 3 polynom
        x, y, z: triplet of floats

    Returns:
        the value of the polynom
    """
    return poly[0] +\
           poly[1]*y + poly[2]*x + poly[3]*z +\
           poly[4]*y*x + poly[5]*y*z +poly[6]*x*z +\
           poly[7]*y*y + poly[8]*x*x + poly[9]*z*z +\
           poly[10]*x*y*z +\
           poly[11]*y*y*y +\
           poly[12]*y*x*x + poly[13]*y*z*z + poly[14]*y*y*x +\
           poly[15]*x*x*x +\
           poly[16]*x*z*z + poly[17]*y*y*z + poly[18]*x*x*z +\
           poly[19]*z*z*z

def apply_rfm(num, den, x, y, z):
    return apply_poly(num, x, y, z) / apply_poly(den, x, y, z)

class RPCModel:
    def __init__(self, XMLfile):
        self.read_rpc(XMLfile)

    def read_rpc(self, XMLfile):
        self.filepath = XMLfile
        tree = ElementTree()
        tree.parse(XMLfile)

        # direct model
        d = tree.find('Rational_Function_Model/Global_RFM/Direct_Model')
        direct = [float(child.text) for child in d]
        self.directLonNum = direct[:20]
        self.directLonDen = direct[20:40]
        self.directLatNum = direct[40:60]
        self.directLatDen = direct[60:80]
        self.directBias   = direct[80:]

        # inverse model
        i = tree.find('Rational_Function_Model/Global_RFM/Inverse_Model')
        inverse = [float(child.text) for child in i]
        self.inverseColNum = inverse[:20]
        self.inverseColDen = inverse[20:40]
        self.inverseLinNum = inverse[40:60]
        self.inverseLinDen = inverse[60:80]
        self.inverseBias   = inverse[80:]

        # validity domains
        v = tree.find('Rational_Function_Model/Global_RFM/RFM_Validity')
        vd = v.find('Direct_Model_Validity_Domain')
        self.firstRow = float(vd.find('FIRST_ROW').text)
        self.firstCol = float(vd.find('FIRST_COL').text)
        self.lastRow  = float(vd.find('LAST_ROW').text)
        self.lastCol  = float(vd.find('LAST_COL').text)

        vi = v.find('Inverse_Model_Validity_Domain')
        self.firstLon = float(vi.find('FIRST_LON').text)
        self.firstLat = float(vi.find('FIRST_LAT').text)
        self.lastLon  = float(vi.find('LAST_LON').text)
        self.lastLat  = float(vi.find('LAST_LAT').text)

        # scale and offset
        self.lonScale = float(v.find('LONG_SCALE').text)
        self.lonOff   = float(v.find('LONG_OFF').text)
        self.latScale = float(v.find('LAT_SCALE').text)
        self.latOff   = float(v.find('LAT_OFF').text)
        self.altScale = float(v.find('HEIGHT_SCALE').text)
        self.altOff   = float(v.find('HEIGHT_OFF').text)
        self.colScale = float(v.find('SAMP_SCALE').text)
        self.colOff   = float(v.find('SAMP_OFF').text)
        self.linScale = float(v.find('LINE_SCALE').text)
        self.linOff   = float(v.find('LINE_OFF').text)

    def direct_estimate(self, col, lin, alt, return_normalized=False):
        cCol = (col - self.colOff) / self.colScale
        cLin = (lin - self.linOff) / self.linScale
        cAlt = (alt - self.altOff) / self.altScale
        cLon = apply_rfm(self.directLonNum, self.directLonDen, cLin, cCol, cAlt)
        cLat = apply_rfm(self.directLatNum, self.directLatDen, cLin, cCol, cAlt)
        lon = cLon*self.lonScale + self.lonOff
        lat = cLat*self.latScale + self.latOff
        if return_normalized:
           return cLon, cLat, cAlt
        return lon, lat, alt

    def inverse_estimate(self, lon, lat, alt):
        cLon = (lon - self.lonOff) / self.lonScale
        cLat = (lat - self.latOff) / self.latScale
        cAlt = (alt - self.altOff) / self.altScale
        cCol = apply_rfm(self.inverseColNum, self.inverseColDen, cLat, cLon, cAlt)
        cLin = apply_rfm(self.inverseLinNum, self.inverseLinDen, cLat, cLon, cAlt)
        col = cCol*self.colScale+self.colOff
        lin = cLin*self.linScale+self.linOff
        return col, lin, alt

    def __repr__(self):
        return '''
    ### Direct Model ###
        directLonNum = {directLonNum}
        directLonDen = {directLonDen}
        directLatNum = {directLatNum}
        directLatDen = {directLatDen}
        directBias   = {directBias}

    ### Inverse Model ###
        inverseColNum = {inverseColNum}
        inverseColDen = {inverseColDen}
        inverseLinNum = {inverseLinNum}
        inverseLinDen = {inverseLinDen}
        inverseBias   = {inverseBias}

    ### Validity Domains ###
        firstLon = {firstLon}
        firstLat = {firstLat}
        lastLon  = {lastLon}
        lastLat  = {lastLat}

    ### Scale and Offsets ###
        lonScale = {lonScale}
        lonOff   = {lonOff}
        latScale = {latScale}
        latOff   = {latOff}
        altScale = {altScale}
        altOff   = {altOff}
        colScale = {colScale}
        colOff   = {colOff}
        linScale = {linScale}
        linOff   = {linOff}'''.format(
        directLonNum  = self.directLonNum,
        directLonDen  = self.directLonDen,
        directLatNum  = self.directLatNum,
        directLatDen  = self.directLatDen,
        directBias    = self.directBias,
        inverseColNum = self.inverseColNum,
        inverseColDen = self.inverseColDen,
        inverseLinNum = self.inverseLinNum,
        inverseLinDen = self.inverseLinDen,
        inverseBias   = self.inverseBias,
        firstLon      = self.firstLon,
        firstLat      = self.firstLat,
        lastLon       = self.lastLon,
        lastLat       = self.lastLat,
        lonScale      = self.lonScale,
        lonOff        = self.lonOff,
        latScale      = self.latScale,
        latOff        = self.latOff,
        altScale      = self.altScale,
        altOff        = self.altOff,
        colScale      = self.colScale,
        colOff        = self.colOff,
        linScale      = self.linScale,
        linOff        = self.linOff)


if __name__ == '__main__':
    # test on the first haiti image
    rpc = RPCModel('../pleiades_data/rpc/haiti/rpc01.xml')
    col, lin = 20000, 8000
    alt = 90
    print 'col={col}, lin={lin}, alt={alt}'.format(col=col, lin=lin, alt=alt)
    lon, lat, alt = rpc.direct_estimate(col, lin, alt)
    print 'lon={lon}, lat={lat}, alt={alt}'.format(lon=lon, lat=lat, alt=alt)
    col, lin, alt = rpc.inverse_estimate(lon, lat, alt)
    print 'col={col}, lin={lin}, alt={alt}'.format(col=col, lin=lin, alt=alt)
{'fraction_non_alphanumeric': 0.13735899137358992, 'fraction_numerical': 0.015428002654280026, 'mean_word_length': 2.75607476635514, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 3}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3056184', 'n_tokens_mistral': 4407, 'n_tokens_neox': 4218, 'n_words': 709}