예제 #1
0
def generate_corners(center, hpixels, vpixels):
    map_x, map_y = center[0], center[1]
    c_lon, c_lat = retrievals.standard_bmap(map_x, map_y, inverse=True)
    satlat = sat_lat
    satlon = sat_lon
    altitude = sat_alt
    map_x, map_y = retrievals.transform(satlat,
                                        satlon,
                                        lon=c_lon,
                                        lat=c_lat,
                                        inv=True)

    # find how far off of nadir the satellite must point to view the centre
    # of the FOV
    # retrivals.distance = distance along earth from sub-satellite point
    offsetx = retrievals.distance(altitude, arc_length=map_x, inv=True)
    offsety = retrievals.distance(altitude, arc_length=map_y, inv=True)

    # find orthographic coordinates of the positions of the pixels in the FOV
    x_increments = np.array([-(hpixels) / 2, hpixels / 2])
    y_increments = np.array([-(vpixels) / 2, vpixels / 2])
    x_increments, y_increments = np.meshgrid(x_increments, y_increments)

    x_posns = retrievals.distance(altitude, position=x_increments + offsetx)
    y_posns = retrievals.distance(altitude, position=y_increments + offsety)

    # for some reason these came out in the wrong order
    # so we must flip second last items
    x = x_posns.flatten()
    sorted_xs = np.array([x[0], x[1], x[3], x[2]])
    y = y_posns.flatten()
    sorted_ys = np.array([y[0], y[1], y[3], y[2]])
    # convert back into lats, lons
    lats, lons = retrievals.transform(satlat,
                                      satlon,
                                      inv=False,
                                      x=sorted_xs,
                                      y=sorted_ys)
    # now convert to mappable coordinates so we can map easier later
    map_x, map_y = retrievals.standard_bmap(lons, lats)
    # print(type(map_x), type(map_y))
    vertices = np.array(list(zip(map_x.filled(), map_y.filled())))

    return vertices
예제 #2
0
def clear_mask(sat_lat, sat_lon, time):
    """
        Produces a mask of clear land as well as coordinates,
        where the coordinates are given in orthographic projection coordinates
    """

    cloud_thresh = 0.1
    # get clouds
    # cloudmask has indices for [lat, lon]
    cloudmask = clouds.Clouds(time).get_clouds(time, (90, -90), out="")
    cloudmask = (cloudmask > cloud_thresh).astype(int)

    # 'invert' cloudmask so we have clear = 1, cloud = 0
    cloudmask = 1 - cloudmask

    # get land: use same resolution as MERRA data for easy comparison
    # that resolution is 0.5 x 0.625
    landmask = np.load("../earth_data/landmask_full_05deg_lat_0625deg_lon.npy")

    # now multiply elementwise to get
    # 1 = clear and land, 0 = cloudy or water (or both)
    clear = cloudmask * landmask

    # transform lons, lats into x and y orthographic projection coords
    lons = np.arange(-180., 180. + 1e-8, 0.625)
    lats = np.arange(-90, 90 + 1e-8, 0.5)
    zero_index = list(lats).index(0)
    lats = lats[zero_index:]
    clear = clear[zero_index:, :]

    # transpose clear so you also access it as [lon_index, lat_index]
    clear = clear.T

    # access coords as [lon_index, lat_index] too
    coords = np.empty(clear.shape, dtype=tuple)
    for i, lon in enumerate(lons):
        for j, lat in enumerate(lats):
            # convert to ortho coords
            x, y = retrievals.transform(sat_lat,
                                        sat_lon,
                                        lon=lon,
                                        lat=lat,
                                        inv=True)
            coords[i, j] = (x, y)
    return Mask(clear, coords)
예제 #3
0
 def closest_value(self, sat_lat, sat_lon, lat, lon):
     # returns the value of the mask at the closest point to the coords
     # closest defined in terms of orthographic projection coordinates,
     # I hope that is a good metric!
     x0, y0 = retrievals.transform(sat_lat,
                                   sat_lon,
                                   lat=lat,
                                   lon=lon,
                                   inv=True)
     point = np.array([np.array([x0, y0])])
     # efficient function for distance calculation
     distances = scipy.spatial.distance.cdist(self.flat_coords, point)
     # take first match if any ambiguity
     index = np.where(distances == np.min(distances))[0][0]
     min_coords = self.flat_coords[index]
     x = min_coords[0]
     y = min_coords[1]
     x_index = np.where(self.xs == x)[0][0]
     y_index = np.where(self.ys == y)[0][0]
     value = self.mask[x_index, y_index]
     return value
예제 #4
0
def shift_lattice(fname, index, action, lattice_inds, shift):
    """
    shift the position of a lattice point,
    so as to optimize its position, likely
    to avoid water/ice
    
    The shifts are given in units of pixels,
    as this is the linear space, whereas trying
    to work in distances or lat/lon would be 
    more difficult to track.
    
    
    args:
    
      fname - lattice file name. The full path
              does not need to be given, just the
              actual file name.
              
      index - Index for the segment of the obs.
              period
              
      action - one of 'hshift','vshift' or 'shift'.
               indicates what kind of shift will be
               used: horizontal, vertical or both.
               
      lattice_inds - a single int or list of ints,
                     indicating which lattice points
                     will be shifted
                     
      shift - size of the shift in pixels. For 'hshift'
              and 'vshift', this will be a float. for 
              'shift', it will be a tuple of (hshift, vshift)
              
              
    example:
    
      shift the lattice points with indices from 24 to 32
      from the 0th segment of the lattice file, with a
      vertical shift of 64 pixels:
      
      shift_lattice('lattice_128x128_095W.npz', 0, 'vshift', range(24,33), 64)
      
    """
    # load the lattice
    fpath = os.path.join('../earth_data/', fname)
    lattice = np.load(fpath)

    lattice_name = 'coords_{}'.format(index)

    # only shift in the horizontal direction
    if action == 'hshift':

        # load needed params from the lattice file
        lattice_points = lattice[lattice_name]
        satpoint = lattice['satpoints'][index]

        params = lattice['params']
        psize = params[2]
        refalt = params[3]

        lat, lon = lattice_points.T

        satrad, satlat, satlon = orbit_utils.latlon(satpoint)
        satalt = satrad - re_km

        # find the current pointing offset to the centre of the FOV
        x_inv, y_inv = retrievals.transform(satlat,
                                            satlon,
                                            lat=lat,
                                            lon=lon,
                                            inv=True)

        h_inv = retrievals.distance(satalt,
                                    arc_length=x_inv,
                                    inv=True,
                                    pixel_size=psize,
                                    ref_altitude=refalt)

        # add the specified shift to the lattice point
        h_inv[lattice_inds] += shift
        h_fwd = h_inv

        # recalculate lat/lon position of the lattice point
        x_fwd = retrievals.distance(satalt,
                                    position=h_fwd,
                                    pixel_size=psize,
                                    ref_altitude=refalt)
        y_fwd = y_inv

        lat_new, lon_new = retrievals.transform(satlat,
                                                satlon,
                                                x=x_fwd,
                                                y=y_fwd)

        # get new array of lattice points
        new_lattice_points = np.array([lat_new, lon_new]).T

    # only shift in the vertical direction
    elif action == 'vshift':

        lattice_points = lattice[lattice_name]
        satpoint = lattice['satpoints'][index]

        params = lattice['params']
        psize = params[2]
        refalt = params[3]

        lat, lon = lattice_points.T

        satrad, satlat, satlon = orbit_utils.latlon(satpoint)
        satalt = satrad - re_km

        x_inv, y_inv = retrievals.transform(satlat,
                                            satlon,
                                            lat=lat,
                                            lon=lon,
                                            inv=True)

        v_inv = retrievals.distance(satalt,
                                    arc_length=y_inv,
                                    inv=True,
                                    pixel_size=psize,
                                    ref_altitude=refalt)

        v_inv[lattice_inds] += shift
        v_fwd = v_inv

        x_fwd = x_inv
        y_fwd = retrievals.distance(satalt,
                                    position=v_fwd,
                                    pixel_size=psize,
                                    ref_altitude=refalt)

        lat_new, lon_new = retrievals.transform(satlat,
                                                satlon,
                                                x=x_fwd,
                                                y=y_fwd)

        new_lattice_points = np.array([lat_new, lon_new]).T

    # shift in both the vertical and
    # horizontal directions
    elif action == 'shift':

        # shift is a tuple ordered as
        # (horizontal shift, vertical shift)
        hshift, vshift = shift

        lattice_points = lattice[lattice_name]
        satpoint = lattice['satpoints'][index]

        params = lattice['params']
        psize = params[2]
        refalt = params[3]

        lat, lon = lattice_points.T

        satrad, satlat, satlon = orbit_utils.latlon(satpoint)
        satalt = satrad - re_km

        x_inv, y_inv = retrievals.transform(satlat,
                                            satlon,
                                            lat=lat,
                                            lon=lon,
                                            inv=True)

        h_inv = retrievals.distance(satalt,
                                    arc_length=x_inv,
                                    inv=True,
                                    pixel_size=psize,
                                    ref_altitude=refalt)
        v_inv = retrievals.distance(satalt,
                                    arc_length=y_inv,
                                    inv=True,
                                    pixel_size=psize,
                                    ref_altitude=refalt)

        h_inv[lattice_inds] += hshift
        v_inv[lattice_inds] += vshift
        h_fwd = h_inv
        v_fwd = v_inv

        x_fwd = retrievals.distance(satalt,
                                    position=h_fwd,
                                    pixel_size=psize,
                                    ref_altitude=refalt)
        y_fwd = retrievals.distance(satalt,
                                    position=v_fwd,
                                    pixel_size=psize,
                                    ref_altitude=refalt)

        lat_new, lon_new = retrievals.transform(satlat,
                                                satlon,
                                                x=x_fwd,
                                                y=y_fwd)
        new_lattice_points = np.array([lat_new, lon_new]).T

    # save the new lattice points to the lattice file
    file_kwds = {}
    for key in lattice.files:
        if key != lattice_name:
            file_kwds[key] = lattice[key]
        else:
            file_kwds[key] = new_lattice_points

    np.savez(fpath, **file_kwds)
예제 #5
0
def generate_lattice(sim_file,
                     hpixels,
                     vpixels,
                     lattice_min=42.5,
                     lattice_max=90.,
                     lf_threshold=0.01,
                     **mission_args):
    """
    Generate a lattice of the centres
    of FOVs, indicating where a satellite
    will point when it is making observations.
    
    The shape of the lattice will depend
    on the orbit that is used, the size
    of the FOVs, and the revisit profile
    for the observation periods. 
    
    other functions within this module
    can be used to edit the lattice that
    is generated, to optimize it to avoid
    observing over water.
    
    the lattice gets saved as a numpy
    .npz file
    
    args:
    
        sim_file - name of the .csv file in which
                   the orbit data is stored.
                   
        hpixels - number of pixels in the FOV in the
                  horizontal direction
                  
        vpixels - number of pixels in the FOv in the
                  vertical direction
                  
        lattice_min - minimum latitude at the
                      centre for the FOV to get 
                      accepted
                      
        lattice_max - maximum latitude at the
                      centre for the FOV to get
                      accepted
                      
        lf_threshold - minimum land fraction for
                       the FOV to be accepted
                       
        mission_args - kwargs to pass to the mission
                       instance
    """
    # intialize the mission instance, and use the first
    # three full orbits (for TAP, change to two for molniya)
    # for satellite positions
    mission = retrievals.Mission(sim_file, **mission_args)

    a1, a2, a3 = mission.satellite.get_apogees()[2:5]
    files = []
    for a in [a1, a2, a3]:

        # initialize the observation period,
        # with no lattice file passed, as it
        # will get created now
        obs = retrievals.ObservationPeriod(mission, a, None)

        # get relevant times
        obs_periods = len(obs.revisit_profile)
        obs_start = obs.apogee_time - obs.obs_length / 2
        obs_times = [
            obs_start + np.sum(obs.revisit_profile[:s])
            for s in np.arange(obs_periods)
        ]
        obs_middle = [
            obs_times[k] + obs.revisit_profile[k] / 2
            for k in np.arange(obs_periods)
        ]

        # calculate the satellite position
        # at the centre of each segment of the obs. period
        satpoints = obs.satellite(obs_middle)

        # get satellite lat/lon, and altitude
        satrad, satlat, satlon = orbit_utils.latlon_arr(satpoints,
                                                        axis='first')
        satalt = satrad - re_km

        # save some params that will get passed
        # on to the lattice file
        psize = obs.pixel_size
        refalt = obs.ref_altitude
        hp = obs.hpixels
        vp = obs.vpixels

        # create the lattices, for each segment
        # of the observation period
        coords = []
        masks = []
        for k in range(obs_periods):
            # find the maximum offset the satellite can have,
            # based on its altitude and pixel size
            # offset = farthest from nadir that the satellite can point
            # while still looking at the Earth
            max_offset = retrievals.distance(satalt[k],
                                             arc_length=re_km,
                                             inv=True,
                                             pixel_size=psize,
                                             ref_altitude=refalt)

            # create the lattice
            h_grid = np.append(
                np.arange(0., -max_offset, -hp)[::-1],
                np.arange(hp, max_offset, hp))
            v_grid = np.append(
                np.arange(0., -max_offset, -vp)[::-1],
                np.arange(vp, max_offset, vp))

            h_grid, v_grid = np.meshgrid(h_grid, v_grid)

            # get the lat/lon locations of the lattice points
            x_grid = retrievals.distance(satalt[k],
                                         position=h_grid,
                                         pixel_size=psize,
                                         ref_altitude=refalt)
            y_grid = retrievals.distance(satalt[k],
                                         position=v_grid,
                                         pixel_size=psize,
                                         ref_altitude=refalt)

            lat, lon = retrievals.transform(satlat[k],
                                            satlon[k],
                                            x=x_grid,
                                            y=y_grid)

            # uncomment to show a quick scatter
            # of the lattice points
            # it doesn't look like much though!
            """
            m = Basemap(projection='cyl')
            m.scatter(lon, lat)
            plt.show()
            """
            # get rid of any masked points, where the
            # FOVs extend to the other side of the Earth

            lat = lat.compressed()
            lon = lon.compressed()

            # mask points that fall outisde the
            # allowed latitude range
            lat_mask = np.ma.masked_outside(lat, lattice_min, lattice_max).mask

            lat = np.ma.array(lat, mask=lat_mask).compressed()
            lon = np.ma.array(lon, mask=lat_mask).compressed()

            # filter the lattice points by land
            # fraction, so that those completely
            # over water are excluded
            lf_mask = np.zeros(lat.size, dtype=np.bool)
            for j in range(lat.size):

                gs = retrievals.GridSquare(lat[j], lon[j], satpoints[k], hp,
                                           vp, psize, refalt)

                if not np.sum(gs.pixel_lats.mask):

                    land_frac = gs.get_land_fraction(obs.landmask,
                                                     obs.landmask_res)[0]
                    if land_frac < lf_threshold: lf_mask[j] = True

                else:
                    lf_mask[j] = True

            lat = np.ma.array(lat, mask=lf_mask).compressed()
            lon = np.ma.array(lon, mask=lf_mask).compressed()

            # create the coordinate and mask arrays
            # for the lattice file
            coords.append(np.array([lat, lon]).T)
            masks.append(np.zeros(lat.size, dtype=np.bool))
        """
        read the data into the .npz file, which
        is organized as follows:

        coords_k - the lattice point coordinates for
                   the kth segment of the obs. period

        mask_k - the lattice mask for the kth segment
                 of the obs. period

        satpoints - the cartesian satellite coords for
                    the middle of each segment

        params - Other parameters needed to find FOV
                 shape and individual pixel locations
        """
        file_kwds = {
            'satpoints': satpoints,
            'params': np.array([hp, vp, psize, refalt])
        }
        for k in range(obs_periods):
            file_kwds['coords_{}'.format(k)] = coords[k]
            file_kwds['mask_{}'.format(k)] = masks[k]

        apogee_lon = orbit_utils.latlon(mission.satellite(a)[0])[2]
        lon_key = '{lon:03}{sign}'.format(lon=int(abs(round(apogee_lon))),
                                          sign='W' if apogee_lon < 0. else 'E')

        lattice_fname = '../earth_data/TAP_lattice_{h}x{v}_{lon}.npz'.format(
            h=hp, v=vp, lon=lon_key)
        np.savez(lattice_fname, **file_kwds)
        files.append(lattice_fname)
    return files
예제 #6
0
def generate_clear_lattice(mission,
                           time,
                           index,
                           lattice_min=42.5,
                           lattice_max=90.,
                           lf_threshold=0.01):
    """
    Generate a lattice of the centres
    of FOVs, indicating where a satellite
    will point when it is making observations.
    
    The shape of the lattice will depend
    on the orbit that is used, the size
    of the FOVs, and the revisit profile
    for the observation periods. 
    
    other functions within this module
    can be used to edit the lattice that
    is generated, to optimize it to avoid
    observing over water.
    
    args:
    
        sim_file - name of the .csv file in which
                   the orbit data is stored.
                   
        hpixels - number of pixels in the FOV in the
                  horizontal direction
                  
        vpixels - number of pixels in the FOv in the
                  vertical direction
                  
        lattice_min - minimum latitude at the
                      centre for the FOV to get 
                      accepted
                      
        lattice_max - maximum latitude at the
                      centre for the FOV to get
                      accepted
                      
        lf_threshold - minimum land fraction for
                       the FOV to be accepted
    """
    obs = retrievals.ObservationPeriod(mission, time, None)  #  --> 4s
    # calculate the satellite position at the centre of the obs. period
    satpoint = obs.satellite(time)

    # get satellite lat/lon, and altitude
    satrad, satlat, satlon = orbit_utils.latlon_arr(satpoint, axis='first')
    satalt = satrad - re_km
    satlat, satlon, satalt = satlat[0], satlon[0], satalt[0]
    cm = clear_mask(satlat, satlon, time)  # --> 12s

    # save some params that will get passed
    # on to the lattice file
    psize = obs.pixel_size
    refalt = obs.ref_altitude
    hp = obs.hpixels
    vp = obs.vpixels

    # find the maximum offset the satellite can have,
    # based on its altitude and pixel size
    # offset = farthest from nadir that the satellite can point
    # while still looking at the Earth
    max_offset = retrievals.distance(satalt,
                                     arc_length=re_km,
                                     inv=True,
                                     pixel_size=psize,
                                     ref_altitude=refalt)

    # create the lattice
    h_grid = np.append(
        np.arange(0., -max_offset, -hp)[::-1], np.arange(hp, max_offset, hp))
    v_grid = np.append(
        np.arange(0., -max_offset, -vp)[::-1], np.arange(vp, max_offset, vp))

    h_grid, v_grid = np.meshgrid(h_grid, v_grid)
    # get the lat/lon locations of the lattice points
    x_grid = retrievals.distance(satalt,
                                 position=h_grid,
                                 pixel_size=psize,
                                 ref_altitude=refalt)
    y_grid = retrievals.distance(satalt,
                                 position=v_grid,
                                 pixel_size=psize,
                                 ref_altitude=refalt)

    lat, lon = retrievals.transform(satlat, satlon, x=x_grid, y=y_grid)

    # get rid of any masked points, where the
    # FOVs extend to the other side of the Earth

    lat = lat.compressed()
    lon = lon.compressed()

    # uncomment to show a quick scatter
    # of the lattice points
    """
    m = Basemap(projection='ortho', lat_0=90, lon_0=-95)
    plon, plat = m(lon, lat)
    m.scatter(plon, plat)
    plt.show()
    """
    # mask points that fall outisde the
    # allowed latitude range
    lat_mask = np.ma.masked_outside(lat, lattice_min, lattice_max).mask

    lat = np.ma.array(lat, mask=lat_mask).compressed()
    lon = np.ma.array(lon, mask=lat_mask).compressed()
    """
    m = Basemap(projection='ortho', lat_0=90, lon_0=-95)
    plon, plat = m(lon, lat)
    m.scatter(plon, plat)
    plt.show()
    """

    # filter the lattice points by land
    # fraction, so that those completely
    # over water are excluded

    # also filter by cloud fraction
    lf_mask = np.zeros(lat.size, dtype=np.bool)
    satpoint = satpoint[0]  # there's only one satpoint but it's inside []

    start = t.clock()
    for j, point in enumerate(zip(lat, lon)):
        la, lo = point
        gs = retrievals.GridSquare(la, lo, satpoint, hp, vp, psize, refalt)
        if np.all(gs.pixel_lats.mask == 0):  # i.e. if no points are masked
            land_frac = gs.get_land_fraction(obs.landmask, obs.landmask_res)[0]
            if land_frac < lf_threshold:
                # not enough land
                lf_mask[j] = True
            else:
                # do a cloud check -- this will be T/F; Cloudy/Not Cloudy
                cloud_value = cm.closest_value(satlat, satlon, la, lo)
                if cloud_value:
                    # bad pixel, too cloudy
                    lf_mask[j] = True
                else:
                    pass  # pixel is clear
        else:
            # bad pixel already
            lf_mask[j] = True
    print(t.clock() - start, 'g')  # --> 360s

    lat = np.ma.array(lat, mask=lf_mask).compressed()
    lon = np.ma.array(lon, mask=lf_mask).compressed()

    #m = Basemap(projection='ortho', lat_0=90, lon_0=-95)
    #plon, plat = m(lon, lat)
    #m.scatter(plon, plat)
    #plt.show()

    return (lat, lon)