def generic_geoid_test(instance, test_file, geoid_file): assert isinstance(instance, unittest.TestCase) _, gname = os.path.split(geoid_file) if gname.lower().startswith('egm84'): zcol = 2 elif gname.lower().startswith('egm96'): zcol = 3 else: zcol = 4 with open(test_file, 'r') as fi: lins = fi.read().splitlines() lats = numpy.zeros((len(lins), ), dtype=numpy.float64) lons = numpy.zeros((len(lins), ), dtype=numpy.float64) zs = numpy.zeros((len(lins), ), dtype=numpy.float64) for i, lin in enumerate(lins): slin = lin.strip().split() lats[i] = float(slin[0]) lons[i] = float(slin[1]) zs[i] = float(slin[zcol]) logging.info('number of test points {}'.format(lats.size)) with instance.subTest(msg="Load geoid file"): start = time.time() gh = geoid.GeoidHeight(file_name=geoid_file) logging.info('time loading geoid file {}'.format(time.time() - start)) recs = 10 # small linear test with instance.subTest(msg='Small linear interpolation test'): start = time.time() zs1 = gh.get(lats[:recs], lons[:recs], cubic=False) logging.info('linear - time {}, diff {}'.format( time.time() - start, zs1 - zs[:recs])) # small cubic test with instance.subTest(msg='Small cubic interpolation test'): start = time.time() zs1 = gh.get(lats[:recs], lons[:recs], cubic=True) logging.info('cubic - time {}, diff {}'.format(time.time() - start, zs1 - zs[:recs])) # full linear test with instance.subTest(msg="Full linear interpolation test"): start = time.time() zs1 = gh.get(lats, lons, cubic=False) diff = numpy.abs(zs1 - zs) max_diff = numpy.max(diff) mean_diff = numpy.mean(diff) logging.info('linear - time {}, max diff - {}, mean diff - {}'.format( time.time() - start, max_diff, mean_diff)) instance.assertLessEqual( max_diff, 2, msg="Max difference should be less than 2 meters") instance.assertLessEqual( mean_diff, 0.5, msg="Mean difference should be (much) less than 0.5 meters.") # full cubic test with instance.subTest(msg="Full cubic interpolation test"): start = time.time() zs1 = gh.get(lats, lons, cubic=True) diff = numpy.abs(zs1 - zs) max_diff = numpy.max(diff) mean_diff = numpy.mean(diff) logging.info('cubic - time {}, max diff - {}, mean diff - {}'.format( time.time() - start, max_diff, mean_diff)) instance.assertLessEqual( max_diff, 2, msg="Max difference should be less than 2 meters") instance.assertLessEqual( mean_diff, 0.5, msg="Mean difference should be (much) less than 0.5 meters.")
def projection_set_to_dem(r_tgt_coa, r_dot_tgt_coa, arp_coa, varp_coa, scp, delta_hae_max, nlim, dem, dem_type, geoid_path, del_DISTrrc=10, del_HDlim=.001): ''' Transforms pixel row, col to geocentric value projected to dem via algorithm in SICD Image Projections document. gpp = projection_set_to_dem(r_tgt_coa, r_dot_tgt_coa, arp_coa, varp_coa, scp, delta_hae_max, nlim, dem, del_DISTrrc = 10, del_HDlim = .001): Inputs: r_tgt_coa - [N] range to the ARP at COA r_dot_tgt_coa - [N] range rate relative to the ARP at COA arp_coa - [Nx3] aperture reference position at t_coa varp_coa - [Nx3] velocity at t_coa scp - [3] scene center point (ECF meters) delta_hae_max - height threshold for convergence of iterative projection sequence. nlim - maximum number of iterations allowed. dem DTED/SRTM pathname or structure with lats/lons/elevations fields where are all are arrays of same size and elevation is height above WGS-84 ellipsoid. Only valid if projection_type is 'dem'. dem_type - One of 'DTED1', 'DTED2', 'SRTM1', 'SRTM2', 'SRTM2F' Defaults to SRTM2f geoid_path - Parent path to EGM2008 file(s) del_DISTrrc Maximum distance between adjacent points along the R/Rdot contour. Recommended value: 10.0 m. Only valid if projection_type is 'dem'. del_HDlim Height difference threshold for determining if a point on the R/Rdot contour is on the DEM surface (m). Recommended value: 0.001 m. Only valid if projection_type is 'dem'. Outputs: gpp - [Nx3] ECF Ground Points along the R/Rdot contour ''' ugpn = gc.wgs_84_norm(scp) look = np.sign( np.sum(ugpn * np.cross(arp_coa - scp, varp_coa, axis=1), axis=1))[0] if (len(dem) > 0 and os.path.exists(dem)): # Load DEM data for 5km buffer around points roughly projected to # height above ellipsoid provided in SICD SCP. gpos = gc.ecf_to_geodetic( projection_set_to_plane(r_tgt_coa, r_dot_tgt_coa, arp_coa, varp_coa, scp, ugpn)) LL = np.array([gpos[:, 0].min(), gpos[:, 1].min()]) - ( 5 * np.array([1, 1 / np.cos(gpos[:, 0].min() * (np.pi / 180.))]) / 111.111) UR = np.array([gpos[:, 0].max(), gpos[:, 1].max()]) + ( 5 * np.array([1, 1 / np.cos(gpos[:, 0].max() * (np.pi / 180.))]) / 111.111) coord = [[LL[0], LL[1]], [UR[0], UR[1]]] evaldem = DEM.DEM(coordinates=coord, masterpath=dem, dem_type='SRTM2F', log_level="WARNING") withinLat = (evaldem.lats_1D > LL[0]) & (evaldem.lats_1D < UR[0]) withinLon = (evaldem.lons_1D > LL[1]) & (evaldem.lons_1D < UR[1]) latindx = np.where(withinLat)[0] lonindx = np.where(withinLon)[0] aoicoords = np.empty((np.size(latindx) * np.size(lonindx), ), dtype=object) for i, v in enumerate(aoicoords): aoicoords[i] = [v, i] geoid_file = geoid_path + os.path.sep + 'egm2008-1.pgm' eval_egm = geoid.GeoidHeight(name=geoid_file) indx = 0 egm_elevlist = np.empty(np.size(latindx) * np.size(lonindx)) aoielevs = np.empty(np.size(latindx) * np.size(lonindx)) for lat_indx in range(0, latindx.size): for lon_indx in range(0, lonindx.size): local_lat = evaldem.lats_1D[latindx[lat_indx]] local_lon = evaldem.lons_1D[lonindx[lon_indx]] egm_elevlist[indx] = eval_egm.get(local_lat, local_lon, cubic=True) aoicoords[indx] = [local_lat, local_lon] indx = indx + 1 coordlist = aoicoords.tolist() aoielevs_dem = evaldem.elevate(coord=coordlist, method='nearest') for indx_elev in range(0, aoielevs.size): aoielevs[ indx_elev] = aoielevs_dem[indx_elev] + egm_elevlist[indx_elev] numPoints = r_tgt_coa.size gpp = np.zeros([numPoints, 3]) for i in range(numPoints): # Step 1 - Compute the center point and the radius of the R/RDot projection contour, Rrrc vMag = np.linalg.norm(varp_coa[i, ]) uVel = varp_coa[i, ] / vMag cos_dca = -1 * r_dot_tgt_coa[i] / vMag sin_dca = np.sqrt(1 - cos_dca * cos_dca) ctr = arp_coa[i, ] + r_tgt_coa[i] * cos_dca * uVel Rrrc = r_tgt_coa[i] * sin_dca # Step 2 - Compute unit vectors uRRX and uRRY dec_arp = np.linalg.norm(arp_coa[i, ]) uUP = arp_coa[i, ] / dec_arp RRY = np.cross(uUP, uVel) uRRY = RRY / np.linalg.norm(RRY) uRRX = np.cross(uRRY, uVel) # Step 3 - Project R/Rdot contour to constant height HAEmax Aa = projection_set_to_hae(r_tgt_coa[[i]], r_dot_tgt_coa[[i]], arp_coa[[i], :], varp_coa[[i], :], scp, aoielevs.max(), delta_hae_max, nlim) cos_CAa = np.dot(Aa - ctr, uRRX) / Rrrc # Step 4 - Project R/Rdot contour to constant height HAEmin Ab = projection_set_to_hae(r_tgt_coa[[i]], r_dot_tgt_coa[[i]], arp_coa[[i], :], varp_coa[[i], :], scp, aoielevs.min(), delta_hae_max, nlim) cos_CAb = np.dot(Ab - ctr, uRRX) / Rrrc sin_CAb = look * np.sqrt(1 - cos_CAb * cos_CAb) # Step 5 - Compute the step size for points along R/Rdot contour del_cos_rrc = del_DISTrrc * (1 / Rrrc) * np.abs(sin_CAb) del_cos_dem = del_DISTrrc * (1 / Rrrc) * (np.abs(sin_CAb) / cos_CAb) del_cos_CA = -1 * np.minimum(del_cos_rrc, del_cos_dem) # Step 6 - Compute Number of Points Along R/RDot contour npts = np.floor(((cos_CAa - cos_CAb) / del_cos_CA)) + 2 # Step 7 - Compute the set of NPTS along R/RDot contour cos_CAn = cos_CAb + np.arange(npts) * del_cos_CA sin_CAn = look * np.sqrt(1 - cos_CAn * cos_CAn) P = ctr + Rrrc * (np.outer(cos_CAn, uRRX) + np.outer(sin_CAn, uRRY)) # Step 8 & 9 - For Each Point convert from ECF to DEM coordinates and compute Delta Height # elevate handles: llh = gc.ecf_to_geodetic(P) egm_elevfinlist = np.empty(llh.shape[0]) for indx in range(llh.shape[0]): egm_elevfinlist[indx] = eval_egm.get(llh[indx, 0], llh[indx, 1], cubic=True) hgts = evaldem.elevate(coord=llh[:, 0:2]) del_h = llh[:, 2] - (hgts + egm_elevfinlist) # Step 10 - Solve for the points that are on the DEM in increasing height # (we may just take one for simplicity) # *Currently only finds first point (lowest WGS-84 HAE) # *Finding all solutions would require inspecting all zero crossings close_enough_vec = np.nonzero(np.abs(del_h) < del_HDlim)[0] if (len(close_enough_vec) > 0): gpp[i, :] = P[close_enough_vec[0], :] else: zero_cross = np.where(del_h[:-1] * del_h[1:] < 1)[0] if (zero_cross.size == 0): gpp[i, :] = np.nan else: zero_cross = zero_cross[0] frac = del_h[zero_cross] / (del_h[zero_cross] - del_h[zero_cross + 1]) cos_CA_S = cos_CAb + (zero_cross + frac) * del_cos_CA sin_CA_S = look * np.sqrt(1 - cos_CA_S * cos_CA_S) gpp[i, :] = ctr + Rrrc * (cos_CA_S * uRRX + sin_CA_S * uRRY) return gpp
def projection_set_to_dem(r_tgt_coa, r_dot_tgt_coa, arp_coa, varp_coa, scp, delta_hae_max, nlim, dem, dem_type, geoid_path, del_DISTrrc=10, del_HDlim=.001): ''' Transforms pixel row, col to geocentric value projected to dem via algorithm in SICD Image Projections document. spp = projection_set_to_dem(r_tgt_coa, r_dot_tgt_coa, arp_coa, varp_coa, scp, delta_hae_max, nlim, dem, del_DISTrrc = 10, del_HDlim = .001): Inputs: r_tgt_coa - [1xN] range to the ARP at COA r_dot_tgt_coa - [1xN] range rate relative to the ARP at COA arp_coa - [Nx3] aperture reference position at t_coa varp_coa - [Nx3] velocity at t_coa scp - [3] scene center point (ECF meters) delta_hae_max - height threshold for convergence of iterative projection sequence. nlim - maximum number of iterations allowed. dem DTED/SRTM pathname or structure with lats/lons/elevations fields where are all are arrays of same size and elevation is height above WGS-84 ellipsoid. Only valid if projection_type is 'dem'. dem_type - One of 'DTED1', 'DTED2', 'SRTM1', 'SRTM2', 'SRTM2F' Defaults to SRTM2f geoid_path - Parent path to EGM2008 file(s) del_DISTrrc Maximum distance between adjacent points along the R/Rdot contour. Recommended value: 10.0 m. Only valid if projection_type is 'dem'. del_HDlim Height difference threshold for determining if a point on the R/Rdot contour is on the DEM surface (m). Recommended value: 0.001 m. Only valid if projection_type is 'dem'. Outputs: spp - [3xN] ECF Ground Points along the R/Rdot contour ''' ugpn = gc.wgs_84_norm(scp) look = np.sign( np.sum(ugpn * np.cross(arp_coa - scp, varp_coa, axis=1), axis=1))[0] evaldem = None coord = None eval_egm = None try: if (len(dem) > 0 and os.path.exists(dem)): demstr = dem # if(type(dem)==str): # Load DEM data for 5km buffer around points roughly projected to # height above ellipsoid provided in SICD SCP. gpos = gc.ecf_to_geodetic( projection_set_to_plane(r_tgt_coa, r_dot_tgt_coa, arp_coa, varp_coa, scp, ugpn)) LL = np.array( [gpos.min(0)[0, ], gpos.min(0)[1, ]]) - (5 * np.array( [1, 1 / np.cos(gpos.min(0)[0, ] * (np.pi / 180.))]) / 111.111) UR = np.array( [gpos.max(0)[0, ], gpos.max(0)[1, ]]) + (5 * np.array( [1, 1 / np.cos(gpos.max(0)[0, ] * (np.pi / 180.))]) / 111.111) coord = [[LL[0], LL[1]], [UR[0], UR[1]]] evaldem = DEM.DEM(coordinates=coord, masterpath=demstr, dem_type='SRTM2F', log_level="WARNING") if (evaldem is None): print("DEM method unable to get DEM!") return withinLat = (evaldem.lats_1D > LL[0]) & (evaldem.lats_1D < UR[0]) withinLon = (evaldem.lons_1D > LL[1]) & (evaldem.lons_1D < UR[1]) latindx = np.where(withinLat)[0] lonindx = np.where(withinLon)[0] aoicoords = np.empty((np.size(latindx) * np.size(lonindx), ), dtype=object) for i, v in enumerate(aoicoords): aoicoords[i] = [v, i] geoid_file = geoid_path + os.path.sep + 'egm2008-1.pgm' eval_egm = geoid.GeoidHeight(name=geoid_file) indx = 0 egm_elevlist = np.empty(np.size(latindx) * np.size(lonindx)) aoielevs = np.empty(np.size(latindx) * np.size(lonindx)) for lat_indx in range(0, latindx.size): for lon_indx in range(0, lonindx.size): local_lat = evaldem.lats_1D[latindx[lat_indx]] local_lon = evaldem.lons_1D[lonindx[lon_indx]] # elev_latindx = latindx[lat_indx] # elev_lonindx = lonindx[lon_indx] egm_elevlist[indx] = eval_egm.get(local_lat, local_lon, cubic=True) aoicoords[indx] = [local_lat, local_lon] indx = indx + 1 coordlist = aoicoords.tolist() aoielevs_dem = evaldem.elevate(coord=coordlist, method='nearest') for indx_elev in range(0, aoielevs.size): aoielevs[indx_elev] = aoielevs_dem[indx_elev] + egm_elevlist[ indx_elev] except Exception as error: print(error) print("Unable to project to DEM") print("dem=", dem, " dem type=", type(dem)) print( "ERROR, check that dem set to valid path, or lats/lon/elevations fields." ) return hae_max = aoielevs_dem.max(0) hae_min = aoielevs_dem.min(0) numPoints = r_tgt_coa.size gpp = np.zeros([3, numPoints]) for i in range(numPoints): ''' Step 1 - Compute the center point and the radius of the R/RDot projection contour, Rrrc ''' # vMag = np.linalg.norm(varp_coa[:,i]) # uVel = varp_coa[:,i]/vMag # print('uVel=',uVel) vMag = np.linalg.norm(varp_coa[i, ]) uVel = varp_coa[i, ] / vMag cos_dca = -1 * r_dot_tgt_coa[i] / vMag sin_dca = np.sqrt(1 - cos_dca * cos_dca) ctr = arp_coa[i, ] + r_tgt_coa[i] * cos_dca * uVel Rrrc = r_tgt_coa[i] * sin_dca ''' Step 2 - Compute unit vectors uRRX and uRRY ''' dec_arp = np.linalg.norm(arp_coa[i, ]) uUP = arp_coa[i, ] / dec_arp RRY = np.cross(uUP, uVel) uRRY = RRY / np.linalg.norm(RRY) uRRX = np.cross(uRRY, uVel) ''' Step 3 - Project R/Rdot contour to constant height HAEmax ''' Aa = projection_set_to_hae(r_tgt_coa[i], r_dot_tgt_coa[i], arp_coa[i, ], varp_coa[i, ], scp, hae_max, delta_hae_max, nlim) cos_CAa = np.dot(Aa - ctr, uRRX) / Rrrc ''' Step 4 - Project R/Rdot contour to constant height HAEmin ''' Ab = projection_set_to_hae(r_tgt_coa[i], r_dot_tgt_coa[i], arp_coa[i, ], varp_coa[i, ], scp, hae_min, delta_hae_max, nlim) cos_CAb = np.dot(Ab - ctr, uRRX) / Rrrc sin_CAb = look * np.sqrt(1 - cos_CAb * cos_CAb) ''' Step 5 - Compute the step size for points along R/Rdot contour ''' del_cos_rrc = del_DISTrrc * (1 / Rrrc) * np.abs(sin_CAb) del_cos_dem = del_DISTrrc * (1 / Rrrc) * (np.abs(sin_CAb) / cos_CAb) del_cos_CA = -1 * np.minimum(del_cos_rrc, del_cos_dem) ''' Step 6 - Compute Number of Points Along R/RDot contour ''' npts = np.floor(((cos_CAa - cos_CAb) / del_cos_CA)) + 2 ''' Step 7 - Compute the set of NPTS along R/RDot contour ''' cos_CAn = cos_CAb + np.arange(npts) * del_cos_CA sin_CAn = look * np.sqrt(1 - cos_CAn * cos_CAn) P = ctr.reshape(ctr.shape[0], 1) + Rrrc * (uRRX.reshape(uRRX.shape[0], 1) * cos_CAn + uRRY.reshape(uRRY.shape[0], 1) * sin_CAn) ''' Step 8 & 9 - For Each Point convert from ECF to DEM coordinates and compute Delta Height elevate handles: x = DEM.lats y = DEM.lons z = DEM.elevations f = interpolate.interp2d(x,y,z) znew = f(llh[0], llh[1]) ''' llh = gc.ecf_to_geodetic(P[0], P[1], P[2]) latlon = [] egm_elevfinlist = np.empty(llh[0][0].size) for indx in range(llh[0][0].size): latlon.append([llh[0][0][indx], llh[1][0][indx]]) egm_elevfinlist[indx] = eval_egm.get(latlon[indx][0], latlon[indx][1], cubic=True) llarry = np.array(latlon) hgts = evaldem.elevate(coord=llarry, method='nearest') del_h = llh[2] - (hgts + egm_elevfinlist) ''' Step 10 - Solve for the points that are on the DEM in increasing height (we may just take one for simplicity) *Currently only finds first point (lowest WGS-84 HAE) *Finding all solutions would require inspecting all zero crossings ''' close_enough_vec = np.nonzero(np.abs(del_h) < del_HDlim)[0] if (len(close_enough_vec) > 0): close_enough = close_enough_vec[0] gpp[:, i] = P[:, close_enough] else: zero_cross_idx = np.where(del_h[0][:-1] * del_h[0][1:] < 1)[0] zero_cross = zero_cross_idx[0] if (zero_cross == 0): gpp[:, i] = np.nan else: frac = del_h[0][zero_cross] / (del_h[0][zero_cross] - del_h[0][zero_cross + 1]) cos_CA_S = cos_CAb + (zero_cross + frac) * del_cos_CA sin_CA_S = look * np.sqrt(1 - cos_CA_S * cos_CA_S) gpp[:, i] = ctr + Rrrc * (cos_CA_S * uRRX + sin_CA_S * uRRY) return (gpp)