Пример #1
0
def xyz2llz(x, y, z):
    """convert earth-centered, earth-fixed (ECEF) cartesian x, y, z to latitude, longitude, and altitude

    code is based on:

    https://www.mathworks.com/matlabcentral/fileexchange/7941-convert-cartesian--ecef--coordinates-to-lat--lon--alt?
                 focused=5062924&tab=function

     Keyword arguments:
     x: x-coordinate  normalized to the radius of Earh
     y: y-coordinate  normalized to the radius of Earh
     z: z-coordinate  normalized to the radius of Earh

     Return values:
     lat: latitude (deg)
     lon: longitude (deg)
     depth: depth (km)
    """

    # World Geodetic System 1984, WGS 84
    erad = np.float64(
        6378137.0)  # Radius of the Earth in meters (equatorial radius, WGS84)
    rad = 1  # sphere radius
    e = np.float64(8.1819190842622e-2)

    # convert to radius
    x = x * erad / rad
    y = y * erad / rad
    z = z * erad / rad

    b = np.sqrt(erad * erad * (1 - e * e))
    ep = np.sqrt((erad * erad - b * b) / (b * b))
    p = np.sqrt(x * x + y * y)
    th = np.arctan2(erad * z, b * p)
    lon = np.arctan2(y, x)
    lat = np.arctan2((z + ep * ep * b * np.sin(th) * np.sin(th) * np.sin(th)),
                     (p - e * e * erad * np.cos(th) * np.cos(th) * np.cos(th)))
    N = erad / np.sqrt(1.0 - e * e * np.sin(lat) * np.sin(lat))
    alt = p / np.cos(lat) - N

    lon = np.mod(lon, (math.pi * 2.0))
    lon = np.rad2deg(lon)
    lon = utils.lon_180(lon)
    lat = np.rad2deg(lat)
    alt = -1 * alt / 1000.0  # depth as negative alt

    return lat, lon, alt
Пример #2
0
def read_geocsv_model_2d(model_file,
                         ll,
                         ur,
                         inc,
                         roughness,
                         unit_factor=1,
                         base=0,
                         extent=False):
    """Read in a 3-D Earth model in the GeoCSV format.

      Keyword arguments:
      model_file: model file
      ll: lower-left coordinate
      ur: upper-right coordinate
      inc: grid sampling interval
      roughness: set the variable as depth and use this for exaggeration
      extent: should only compute model extent? (True or False)

      Return values:
      x: x-coordinate  normalized to the radius of the Earth
      y: y-coordinate  normalized to the radius of the Earth
      z: z-coordinate  normalized to the radius of the Earth
      meta: file metadata information
     """

    # model data and metadata

    (params, lines) = read_geocsv(model_file, is_2d=True)

    # model data
    raw_data = []
    for line in lines:
        raw_data.append(line.split(params['delimiter']))
    data = [list(utils.str2float(sublist)) for sublist in raw_data]

    # model variables
    variables = []
    for this_param in list(params.keys()):
        if params[this_param] not in (
                params['longitude_column'],
                params['latitude_column']) and '_column' in this_param:
            variables.append(params[this_param])

    # index to the variables
    var_index = {}
    for i, val in enumerate(params['header']):
        if val == params['longitude_column']:
            lon_index = i
        elif val == params['latitude_column']:
            lat_index = i
        else:
            for this_var in variables:
                if val == params[this_var + '_column']:
                    var_index[this_var] = i
                    break

    lat = list(set(get_column(data, lat_index)))
    lon = list(set(get_column(data, lon_index)))
    lon.sort(key=float)
    lon, lon_map = utils.lon_180(lon, fix_gap=True)
    depth = [base]

    # get coordinates sorted otherwise inc will not function properly
    lat.sort(key=float)
    lon.sort(key=float)

    # select the values within the ranges (this is to get a count only)
    latitude, longitude, depth2 = get_points_in_area(lat, lon, depth, ll, ur,
                                                     inc)

    # model data grid definition
    V = {}
    nx = len(longitude)
    ny = len(depth2)
    nz = len(latitude)
    if extent:
        return nx - 1, ny - 1, nz - 1

    meta = {
        'depth': [],
        'lat': [100, -100],
        'lon': [400, -400],
        'source': model_file
    }

    for l, var_val in enumerate(variables):
        X = np.zeros((nx, ny, nz))
        X[:] = np.nan
        Y = np.zeros((nx, ny, nz))
        Y[:] = np.nan
        Z = np.zeros((nx, ny, nz))
        Z[:] = np.nan

        # we want the grid to be in x, y z order (longitude, depth, latitude)
        data.sort(key=itemgetter(lat_index, lon_index))
        for i, values in enumerate(data):
            lon_val = float(lon_map[utils.float_key(values[lon_index])])
            lat_val = float(values[lat_index])
            this_value = float(values[var_index[var_val]])
            if this_value is None:
                depth_val = base
            else:
                depth_val = base + float(this_value) * float(
                    roughness) * float(unit_factor)

            if lon_val in longitude and lat_val in latitude:
                meta['lon'] = [
                    min(meta['lon'][0], lon_val),
                    max(meta['lon'][0], lon_val)
                ]
                meta['lat'] = [
                    min(meta['lat'][0], lat_val),
                    max(meta['lat'][0], lat_val)
                ]
                if depth_val not in meta['depth']:
                    meta['depth'].append(depth_val)
                x, y, z = llz2xyz(lat_val, lon_val, depth_val)

                ii = longitude.index(lon_val)
                jj = 0
                kk = latitude.index(lat_val)
                X[ii, jj, kk] = x
                Y[ii, jj, kk] = y
                Z[ii, jj, kk] = z

                if var_val not in list(V.keys()):
                    V[var_val] = np.zeros((nx, ny, nz))
                    V[var_val][:] = np.nan
                if '_'.join([var_val, 'missing_value']) in params:
                    # skip the designated missing_value
                    if float(this_value) == float(params['_'.join(
                        [var_val, 'missing_value'])]):
                        continue
                V[var_val][ii, jj, kk] = float(this_value)

    return X, Y, Z, V, meta
Пример #3
0
def read_netcdf_model(model_file,
                      lat_variable,
                      lon_variable,
                      depth_variable,
                      ll,
                      ur,
                      depth_min,
                      depth_max,
                      roughness,
                      inc,
                      extent=False):
    """read in an EMC Earth model in the netCDF format

      Keyword arguments:
      model_file: model file
      ll: lower-left coordinate
      ur: upper-right coordinate
      depth_min: minimum depth
      depth_max: maximum depth
      inc: grid sampling interval

      Return values:
      x: x-coordinate  normalized to the radius of the Earth
      y: y-coordinate  normalized to the radius of the Earth
      z: z-coordinate  normalized to the radius of the Earth
      meta: file metadata information
    """

    # ParaView on some platforms does not have SciPy module
    if not utils.support_nc():
        print(
            "[ERR] Cannot read netCDF files on this platform, try GeoCSV format!"
        )
        return [], [], [], [], {}

    # NetCDF files, when opened read-only, return arrays that refer directly to memory-mapped data on disk:
    print(f"[INFO] Reading model file {model_file}")
    data = netcdf.netcdf_file(model_file, 'r')
    variables = []
    for name in list(data.variables.keys()):
        if name not in (depth_variable, lon_variable, lat_variable):
            variables.append(name)

    # expects variables be a function of latitude, longitude and depth, find the order
    var = variables[0]
    for i, value in enumerate(data.variables[var].dimensions):
        if value == depth_variable:
            depth_index = i
        elif value == lon_variable:
            lon_index = i
        else:
            lat_index = i

    lat = data.variables[lat_variable][:].copy()
    lon = data.variables[lon_variable][:].copy()
    lon, lon_map = utils.lon_180(lon, fix_gap=True)

    depth = data.variables[depth_variable][:].copy()

    # select the values within the ranges (this is to get a count only)
    latitude, longitude, depth2 = get_points_in_volume(lat, lon, depth, ll, ur,
                                                       inc, depth_min,
                                                       depth_max)

    # model data grid definition
    V = {}
    nx = len(longitude)
    ny = len(depth2)
    nz = len(latitude)
    if extent:
        return nx - 1, ny - 1, nz - 1
    index = [-1, -1, -1]
    meta = {
        'depth': [],
        'lat': [100, -100],
        'lon': [400, -400],
        'source': model_file
    }

    missing_value = None
    if hasattr(data.variables[var], 'missing_value'):
        missing_value = float(data.variables[var].missing_value)

    for l, var_val in enumerate(variables):
        X = np.zeros((nx, ny, nz))
        Y = np.zeros((nx, ny, nz))
        Z = np.zeros((nx, ny, nz))
        v = np.zeros((nx, ny, nz))
        data_in = data.variables[var_val][:].copy()

        # increment longitudes, we want to keep the first and last longitude regardless of inc
        for i, lon_val in enumerate(lon):
            for j, depth_val in enumerate(depth):
                for k, lat_val in enumerate(lat):
                    if lon_val in longitude and lat_val in latitude and depth_val in depth2:
                        meta['lon'] = [
                            min(meta['lon'][0], lon_val),
                            max(meta['lon'][0], lon_val)
                        ]
                        meta['lat'] = [
                            min(meta['lat'][0], lat_val),
                            max(meta['lat'][0], lat_val)
                        ]
                        if depth_val not in meta['depth']:
                            meta['depth'].append(depth_val)
                        x, y, z = llz2xyz(lat_val, lon_val,
                                          depth_val * roughness)
                        ii = longitude.index(lon_val)
                        jj = depth2.index(depth_val)
                        kk = latitude.index(lat_val)

                        X[ii, jj, kk] = x
                        Y[ii, jj, kk] = y
                        Z[ii, jj, kk] = z

                        index[depth_index] = j
                        index[lat_index] = k
                        index[lon_index] = i

                        this_value = data_in[index[0]][index[1]][index[2]]

                        if this_value is None:
                            v[ii, jj, kk] = None
                        elif this_value is not None:
                            if this_value == missing_value:
                                v[ii, jj, kk] = None
                                this_value = None
                            else:
                                v[ii, jj, kk] = this_value
                        else:
                            v[ii, jj, kk] = this_value

        V[var_val] = v
    data.close()
    return X, Y, Z, V, meta
Пример #4
0
def read_geocsv_model_3d(model_file,
                         ll,
                         ur,
                         depth_min,
                         depth_max,
                         roughness,
                         inc,
                         extent=False):
    """Read in a 3-D Earth model in the GeoCSV format.

      Keyword arguments:
      model_file: model file
      ll: lower-left coordinate
      ur: upper-right coordinate
      depth_min: minimum depth
      depth_max: maximum depth
      inc: grid sampling interval
      extent: provide model extent only (True or False)

      Return values:
      x: x-coordinate  normalized to the radius of the Earth
      y: y-coordinate  normalized to the radius of the Earth
      z: z-coordinate  normalized to the radius of the Earth
      meta: file metadata information
     """

    # model data and metadata
    (params, lines) = read_geocsv(model_file)

    # model data
    data = []
    for line in lines:
        data.append(line.split(params['delimiter']))

    # model variables
    depth_variable = params['depth_column']
    lat_variable = params['latitude_column']
    lon_variable = params['longitude_column']
    elev_variable = params['elevation_column']
    variables = []
    for this_param in list(params.keys()):
        if params[this_param] not in (
                depth_variable, lon_variable, lat_variable,
                elev_variable) and '_column' in this_param:
            variables.append(params[this_param])

    # index to the variables
    var_index = {}
    for i, val in enumerate(params['header']):
        if val == depth_variable:
            depth_index = i
        elif val == lon_variable:
            lon_index = i
        elif val == lat_variable:
            lat_index = i
        else:
            var_index[val] = i

    lat = np.array(list(set(get_column(data, lat_index))), dtype=float)
    lon = np.array(list(set(get_column(data, lon_index))), dtype=float)

    # -180/180 models are the norm, so we convert 0/360 models to -180/180 first to unify
    # the rest of the code for all models
    data = np.ndarray.tolist(np.asfarray(data))
    if utils.lon_is_360(lon):
        for i, values in enumerate(data):
            if float(values[lon_index]) > 180.0:
                data[i][lon_index] = float(values[lon_index]) - 360.0
        lon = np.array(list(set(get_column(data, lon_index))), dtype=float)

    # we want the grid to be in x, y z order (longitude, depth, latitude)
    data.sort(key=itemgetter(lon_index, depth_index, lat_index))

    lon.sort()
    lon, lon_map = utils.lon_180(lon, fix_gap=True)
    depth = np.array(list(set(get_column(data, depth_index))), dtype=float)

    # get coordinates sorted one last time
    lat.sort()
    lon.sort()
    depth.sort()

    # select the coordinates within the ranges (this is to get a count only)
    latitude = []
    longitude = []
    depth2 = []
    last_i = -1

    for i, lon_val in enumerate(lon):
        if i != 0 and i != len(lon) - 1 and i != last_i + inc:
            continue

        last_i = i
        for j, depth_val in enumerate(depth):
            last_k = -1
            for k, lat_val in enumerate(lat):
                if k != 0 and k != len(lat) - 1 and k != last_k + inc:
                    continue
                last_k = k

                if utils.isValueIn(float(lat_val), ll[0], ur[0]) and utils.isLongitudeIn(
                        float(lon_val), ll[1], ur[1]) and \
                        utils.isValueIn(float(depth_val), depth_min, depth_max):
                    if float(lon_map[utils.float_key(
                            lon_val)]) not in longitude:
                        longitude.append(
                            float(lon_map[utils.float_key(lon_val)]))
                    if float(depth_val) not in depth2:
                        depth2.append(float(depth_val))
                    if float(lat_val) not in latitude:
                        latitude.append(float(lat_val))

    # model data grid definition
    V = {}
    nx = len(longitude)
    ny = len(depth2)
    nz = len(latitude)
    if extent:
        return nx - 1, ny - 1, nz - 1

    meta = {
        'depth': [],
        'lat': [100, -100],
        'lon': [400, -400],
        'source': model_file
    }

    X = np.zeros((nx, ny, nz))
    X[:] = np.nan
    Y = np.zeros((nx, ny, nz))
    Y[:] = np.nan
    Z = np.zeros((nx, ny, nz))
    Z[:] = np.nan

    for i, values in enumerate(data):
        lon_val = float(lon_map[utils.float_key(values[lon_index])])
        lat_val = float(values[lat_index])
        depth_val = float(values[depth_index])

        if lon_val in longitude and lat_val in latitude and depth_val in depth2:
            meta['lon'] = [
                min(meta['lon'][0], lon_val),
                max(meta['lon'][0], lon_val)
            ]
            meta['lat'] = [
                min(meta['lat'][0], lat_val),
                max(meta['lat'][0], lat_val)
            ]
            if depth_val not in meta['depth']:
                meta['depth'].append(depth_val)
            x, y, z = llz2xyz(lat_val, lon_val, depth_val * roughness)

            ii = longitude.index(lon_val)
            jj = depth2.index(depth_val)
            kk = latitude.index(lat_val)
            X[ii, jj, kk] = x
            Y[ii, jj, kk] = y
            Z[ii, jj, kk] = z

            for l, var_val in enumerate(variables):
                if var_val not in list(V.keys()):
                    V[var_val] = np.zeros((nx, ny, nz))
                    V[var_val][:] = np.nan
                if '_'.join([var_val, 'missing_value']) in params:
                    # skip the designated missing_value
                    if float(values[var_index[var_val]]) == float(
                            params['_'.join([var_val, 'missing_value'])]):
                        continue
                V[var_val][ii, jj, kk] = float(values[var_index[var_val]])

    return X, Y, Z, V, meta
Пример #5
0
def read_slab_file(model_file, ll, ur, inc=1, depth_factor=-1, extent=False):
    """read in a 2-D netCDF Slab file

    Keyword arguments:
    model_file: model file
    ll: lower-left coordinate
    ur: upper-right coordinate
    inc: grid sampling interval

    RReturn values:
    X: x-coordinate  normalized to the radius of Earh
    Y: y-coordinate  normalized to the radius of Earh
    Z: z-coordinate  normalized to the radius of Earh
    label: file label
    """

    # ParaView on some systems does not have SciPy module
    if not utils.support_nc():
        print(
            "[ERR] Cannot read netCDF files on this platform, try GeoCSV format!"
        )
        return [], [], [], [], ''

    z_variable = 'z'
    lon_variable = 'x'
    lat_variable = 'y'
    depth = 0

    # model data
    data = netcdf.netcdf_file(model_file, 'r')
    lat = data.variables[lat_variable][:].copy()
    lon = data.variables[lon_variable][:].copy()
    lon = utils.lon_180(lon)
    elevation_data = data.variables[z_variable][:].copy()

    data.close()
    dep = [depth]
    variables = [z_variable]

    # select the values within the ranges (this is to get a count only)
    latitude, longitude, depth2 = get_points_in_area(lat, lon, dep, ll, ur,
                                                     inc)

    # model data grid definition
    V = {}
    nx = len(longitude)
    ny = len(depth2)
    nz = len(latitude)

    if extent:
        return nx - 1, ny - 1, nz - 1

    label = ''
    if len(depth2):
        label = "%0.1f-%0.1fkm" % (min(depth2), max(depth2))

    for l, var_value in enumerate(variables):
        X = np.zeros((nx, ny, nz))
        Y = np.zeros((nx, ny, nz))
        Z = np.zeros((nx, ny, nz))
        v = np.zeros((nx, ny, nz))

        for i, lon_val in enumerate(lon):
            for j, depth_val in enumerate(dep):
                for k, lat_val in enumerate(lat):
                    if lon_val in longitude and lat_val in latitude and depth_val in depth2:
                        ii = longitude.index(lon_val)
                        jj = depth2.index(depth_val)
                        kk = latitude.index(lat_val)
                        x, y, z = llz2xyz(lat[k], lon[i],
                                          elevation_data[k][i] * depth_factor)
                        X[ii, jj, kk] = x
                        Y[ii, jj, kk] = y
                        Z[ii, jj, kk] = z

                        v[ii, jj, kk] = elevation_data[k][i]

    V[z_variable] = v
    return X, Y, Z, V, label
Пример #6
0
def read_netcdf_topo_file(model_file,
                          ll,
                          ur,
                          inc,
                          roughness,
                          lon_var='longitude',
                          lat_var='latitude',
                          elev_var='elevation',
                          base=0,
                          unit_factor=1,
                          extent=False):
    """read in etopo, a 2-D netCDF topo file

    Keyword arguments:
    model_file: model file
    ll: lower-left coordinate
    ur: upper-right coordinate
    inc: grid sampling interval
    roughness: set the variable as depth and use this for exaggeration
    extent: should only compute model extent? (True or False)

    RReturn values:
    X: x-coordinate  normalized to the radius of Earh
    Y: y-coordinate  normalized to the radius of Earh
    Z: z-coordinate  normalized to the radius of Earh
    label: file label
    """

    # ParaView on some platforms does not have SciPy module

    if not utils.support_nc():
        print("[ERR] Sorry, cannot read netCDF files on this platform!")
        return 0, 0, 0

    z_variable = elev_var
    lon_variable = lon_var
    lat_variable = lat_var
    depth = base
    # model data
    data = netcdf.netcdf_file(model_file, 'r')
    lat = data.variables[lat_variable][:].copy()
    lon = data.variables[lon_variable][:].copy()
    lon = utils.lon_180(lon)
    elevation_data = data.variables[z_variable][:].copy()

    data.close()
    dep = [depth]
    variables = [z_variable]

    # select the values within the ranges (this is to get a count only)
    latitude, longitude, depth2 = get_points_in_area(lat, lon, dep, ll, ur,
                                                     inc)

    # model data grid definition
    V = {}
    nx = len(longitude)
    ny = len(depth2)
    nz = len(latitude)

    if extent:
        return nx - 1, ny - 1, nz - 1

    label = ''
    if hasattr(data, 'description'):
        label = data.description
    elif hasattr(data, 'title'):
        label = data.title

    for l, var_value in enumerate(variables):
        X = np.zeros((nx, ny, nz))
        Y = np.zeros((nx, ny, nz))
        Z = np.zeros((nx, ny, nz))
        v = np.zeros((nx, ny, nz))

        for i, lon_val in enumerate(lon):
            for j, depth_val in enumerate(dep):
                for k, lat_val in enumerate(lat):
                    if lon_val in longitude and lat_val in latitude and depth_val in depth2:
                        ii = longitude.index(lon_val)
                        jj = depth2.index(depth_val)
                        kk = latitude.index(lat_val)
                        # "+" since it is elevation but we already making roughness negative to make it positive up
                        x, y, z = llz2xyz(
                            lat_val, lon_val, depth_val +
                            (elevation_data[k][i] * roughness * unit_factor))
                        X[ii, jj, kk] = x
                        Y[ii, jj, kk] = y
                        Z[ii, jj, kk] = z

                        v[ii, jj,
                          kk] = elevation_data[k][i] * float(unit_factor)

    V[z_variable] = v
    return X, Y, Z, V, label