From d8a705ceaffc3797b462c39b4ef3a3c110a7d0dc Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Fri, 4 Feb 2022 15:05:37 -0700 Subject: [PATCH 1/6] Per #1055, ci-run-unit, parse rotated lat/lon grids from CF-compliant NetCDF files. --- met/src/libcode/vx_data2d_nccf/nccf_file.cc | 210 +++++++++++++++++++- met/src/libcode/vx_data2d_nccf/nccf_file.h | 3 + 2 files changed, 202 insertions(+), 11 deletions(-) diff --git a/met/src/libcode/vx_data2d_nccf/nccf_file.cc b/met/src/libcode/vx_data2d_nccf/nccf_file.cc index 80ae51e415..d4213b0c91 100644 --- a/met/src/libcode/vx_data2d_nccf/nccf_file.cc +++ b/met/src/libcode/vx_data2d_nccf/nccf_file.cc @@ -179,8 +179,8 @@ bool NcCfFile::open(const char * filepath) // calling program. In the case of this example, we just exit with // an NC_ERR error code. - //FIXME: Commented out with NetcDf4 enabling - //NcError err(NcError::silent_nonfatal); + // FIXME: Commented out with NetCDF4 enabling + // NcError err(NcError::silent_nonfatal); // Open the file @@ -2266,6 +2266,11 @@ void NcCfFile::get_grid_mapping_polar_stereographic(const NcVar *grid_mapping_va } +//////////////////////////////////////////////////////////////////////// +// +// Reference: +// https://cfconventions.org/Data/cf-conventions/cf-conventions-1.9/cf-conventions.html#_rotated_pole +// //////////////////////////////////////////////////////////////////////// @@ -2273,9 +2278,178 @@ void NcCfFile::get_grid_mapping_rotated_latitude_longitude(const NcVar *grid_map { static const string method_name = "NcCfFile::get_grid_mapping_rotated_latitude_longitude()"; - mlog << Error << "\n" << method_name << " -> " - << "Rotated latitude longitude grid not handled in MET.\n\n"; - exit(1); + // grid_north_pole_latitude + + NcVarAtt *grid_np_lat_att = get_nc_att( + grid_mapping_var, (string)"grid_north_pole_latitude"); + if (IS_INVALID_NC_P(grid_np_lat_att)) + { + mlog << Error << "\n" << method_name << " -> " + << "Cannot get grid_north_pole_latitude attribute from " + << GET_NC_NAME_P(grid_mapping_var) << " variable.\n\n"; + exit(1); + } + + // grid_north_pole_longitude + + NcVarAtt *grid_np_lon_att = get_nc_att( + grid_mapping_var, (string)"grid_north_pole_longitude"); + if (IS_INVALID_NC_P(grid_np_lon_att)) + { + mlog << Error << "\n" << method_name << " -> " + << "Cannot get grid_north_pole_longitude attribute from " + << GET_NC_NAME_P(grid_mapping_var) << " variable.\n\n"; + exit(1); + } + + // Look for the grid_latitude and grid_longitude dimensions + + for (int dim_num = 0; dim_num < _numDims; ++dim_num) + { + // These dimensions are identified by the standard_name attribute + + const NcVar coord_var = get_var(_ncFile, _dims[dim_num]->getName().c_str()); + if (IS_INVALID_NC(coord_var)) + continue; + + const NcVarAtt *std_name_att = get_nc_att(&coord_var, (string)"standard_name"); + if (IS_INVALID_NC_P(std_name_att)) { + if (std_name_att) delete std_name_att; + continue; + } + + ConcatString dim_standard_name; + if (!get_att_value_chars(std_name_att, dim_standard_name)) { + if (std_name_att) delete std_name_att; + continue; + } + + if (std_name_att) delete std_name_att; + + // See if this is a grid_latitude or grid_longitude dimension + + if (dim_standard_name == "grid_latitude") + { + if (_yDim == 0) + { + _yDim = _dims[dim_num]; + + y_dim_var_name = GET_NC_NAME_P(_yDim).c_str(); + + for (int var_num = 0; var_num < Nvars; ++var_num) + { + if ( Var[var_num].name == GET_NC_NAME_P(_yDim)) + { + _yCoordVar = Var[var_num].var; + break; + } + } + } + else + { + mlog << Warning << "\n" << method_name << " -> " + << "Found multiple variables for grid_latitude, using \"" + << GET_NC_NAME_P(_yCoordVar) << "\".\n\n"; + } + } + + if (dim_standard_name == "grid_longitude") + { + if (_xDim == 0) + { + _xDim = _dims[dim_num]; + + x_dim_var_name = GET_NC_NAME_P(_xDim).c_str(); + for (int var_num = 0; var_num < Nvars; ++var_num) + { + if ( Var[var_num].name == GET_NC_NAME_P(_xDim)) + { + _xCoordVar = Var[var_num].var; + break; + } + } + } + else + { + mlog << Warning << "\n" << method_name << " -> " + << "Found multiple variables for grid_longitude, using \"" + << GET_NC_NAME_P(_xCoordVar) << "\".\n\n"; + } + } + + } + + if (_xDim == 0) + { + mlog << Error << "\n" << method_name << " -> " + << "Didn't find X dimension (degrees_east) in netCDF file.\n\n"; + exit(1); + } + + if (_yDim == 0) + { + mlog << Error << "\n" << method_name << " -> " + << "Didn't find Y dimension (degrees_north) in netCDF file.\n\n"; + exit(1); + } + + if (_xCoordVar == 0) + { + mlog << Error << "\n" << method_name << " -> " + << "Didn't find X coord variable (" << GET_NC_NAME_P(_xDim) + << ") in netCDF file.\n\n"; + exit(1); + } + + if (_yCoordVar == 0) + { + mlog << Error << "\n" << method_name << " -> " + << "Didn't find Y coord variable (" << GET_NC_NAME_P(_yDim) + << ") in netCDF file.\n\n"; + exit(1); + } + + long lon_counts = _xDim->getSize(); + long lat_counts = _yDim->getSize(); + if (get_data_size(_xCoordVar) != lon_counts || + get_data_size(_yCoordVar) != lat_counts) + { + mlog << Error << "\n" << method_name << " -> " + << "Coordinate variables don't match dimension sizes in netCDF file.\n\n"; + exit(1); + } + + LatLonData ll_data; + + ll_data = get_data_from_lat_lon_vars(_yCoordVar, _xCoordVar, lat_counts, lon_counts); + + // Fill in the Rotated LatLon data structure. + + RotatedLatLonData data; + + data.name = rotated_latlon_proj_type; + + // Derive south pole location from the north pole + data.true_lat_south_pole = -1.0 * get_att_value_double(grid_np_lat_att); + double np_lon = rescale_lon(get_att_value_double(grid_np_lon_att)); + data.true_lon_south_pole = rescale_lon(-1.0 * (180.0 - fabs(np_lon))); + + // Copied from the LatLon data structure + data.rot_lat_ll = ll_data.lat_ll; + data.rot_lon_ll = ll_data.lon_ll; + data.delta_rot_lat = ll_data.delta_lat; + data.delta_rot_lon = ll_data.delta_lon; + + // Grid dimension + data.Nlon = _xDim->getSize(); + data.Nlat = _yDim->getSize(); + + data.aux_rotation = 0; + + grid.set(data); + + if(grid_np_lat_att) delete grid_np_lat_att; + if(grid_np_lon_att) delete grid_np_lon_att; } @@ -2865,17 +3039,31 @@ bool NcCfFile::get_grid_from_dimensions() //////////////////////////////////////////////////////////////////////// + void NcCfFile::get_grid_from_lat_lon_vars(NcVar *lat_var, NcVar *lon_var, const long lat_counts, const long lon_counts) { static const string method_name = "NcCfFile::get_grid_from_lat_lon_vars()"; + LatLonData data = get_data_from_lat_lon_vars(lat_var, lon_var, lat_counts, lon_counts); + grid.set(data); // resets swap_to_north to false + if (data.delta_lat < 0) grid.set_swap_to_north(true); +} + + +//////////////////////////////////////////////////////////////////////// + + +LatLonData NcCfFile::get_data_from_lat_lon_vars(NcVar *lat_var, NcVar *lon_var, + const long lat_counts, const long lon_counts) { + static const string method_name = "get_data_from_lat_lon_vars()"; + // Figure out the dlat/dlon values from the dimension variables long x_size = get_data_size(lon_var); long y_size = get_data_size(lat_var); long latlon_counts = lon_counts*lat_counts; - bool two_dim_corrd = (x_size == latlon_counts) && (y_size == latlon_counts ); - if( !two_dim_corrd && (x_size != lon_counts || y_size != lat_counts)) + bool two_dim_coord = (x_size == latlon_counts) && (y_size == latlon_counts ); + if( !two_dim_coord && (x_size != lon_counts || y_size != lat_counts)) { mlog << Error << "\n" << method_name << " -> " << "Coordinate variables don't match dimension sizes in netCDF file.\n\n"; @@ -2885,7 +3073,7 @@ void NcCfFile::get_grid_from_lat_lon_vars(NcVar *lat_var, NcVar *lon_var, double lat_values[lat_counts]; double lon_values[lon_counts]; bool lat_first = false; - if (two_dim_corrd) { + if (two_dim_coord) { lat_first = (lat_counts == get_dim_size(lat_var, 0)); long cur[2], length[2]; cur[0] = cur[1] = 0; @@ -2992,7 +3180,7 @@ void NcCfFile::get_grid_from_lat_lon_vars(NcVar *lat_var, NcVar *lon_var, data.name = latlon_proj_type; data.lat_ll = lat_values[0]; - data.lon_ll = -lon_values[0]; + data.lon_ll = rescale_lon(-lon_values[0]); data.delta_lat = dlat; data.delta_lon = dlon; data.Nlat = lat_counts; @@ -3002,9 +3190,9 @@ void NcCfFile::get_grid_from_lat_lon_vars(NcVar *lat_var, NcVar *lon_var, data.lat_ll = lat_values[lat_counts-1]; } - grid.set(data); // resets swap_to_north to false - if (dlat < 0) grid.set_swap_to_north(true); + return(data); } + //////////////////////////////////////////////////////////////////////// diff --git a/met/src/libcode/vx_data2d_nccf/nccf_file.h b/met/src/libcode/vx_data2d_nccf/nccf_file.h index 71fa039a60..47b7719476 100644 --- a/met/src/libcode/vx_data2d_nccf/nccf_file.h +++ b/met/src/libcode/vx_data2d_nccf/nccf_file.h @@ -181,6 +181,9 @@ class NcCfFile { bool get_grid_from_dimensions(); void get_grid_from_lat_lon_vars(NcVar *lat_var, NcVar *lon_var, const long lat_counts, const long lon_counts); + + LatLonData get_data_from_lat_lon_vars(NcVar *lat_var, NcVar *lon_var, + const long lat_counts, const long lon_counts); }; From ee74821d1d9270733401e81c949e95d502691fc4 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Fri, 4 Feb 2022 19:25:14 -0700 Subject: [PATCH 2/6] Per #1055, ci-run-unit, update the Grid::serialize() member functions to let the caller specify the separator string, with a single space as the default. Write retriving a gridded data plane, also write out it's grid information. --- met/src/libcode/vx_data2d/data_class.cc | 18 +++++++++------- met/src/libcode/vx_grid/gaussian_grid.cc | 8 ++++---- met/src/libcode/vx_grid/gaussian_grid.h | 2 +- met/src/libcode/vx_grid/goes_grid.cc | 8 ++++---- met/src/libcode/vx_grid/goes_grid.h | 2 +- met/src/libcode/vx_grid/grid_base.cc | 4 ++-- met/src/libcode/vx_grid/grid_base.h | 4 ++-- met/src/libcode/vx_grid/latlon_grid.cc | 18 ++++++++-------- met/src/libcode/vx_grid/latlon_grid.h | 2 +- met/src/libcode/vx_grid/lc_grid.cc | 22 ++++++++++---------- met/src/libcode/vx_grid/lc_grid.h | 2 +- met/src/libcode/vx_grid/merc_grid.cc | 24 +++++++++++----------- met/src/libcode/vx_grid/merc_grid.h | 2 +- met/src/libcode/vx_grid/rot_latlon_grid.cc | 24 +++++++++++----------- met/src/libcode/vx_grid/rot_latlon_grid.h | 2 +- met/src/libcode/vx_grid/st_grid.cc | 18 ++++++++-------- met/src/libcode/vx_grid/st_grid.h | 2 +- 17 files changed, 83 insertions(+), 79 deletions(-) diff --git a/met/src/libcode/vx_data2d/data_class.cc b/met/src/libcode/vx_data2d/data_class.cc index c3da14d83f..612c0eb6e3 100644 --- a/met/src/libcode/vx_data2d/data_class.cc +++ b/met/src/libcode/vx_data2d/data_class.cc @@ -361,21 +361,25 @@ if ( vinfo->grid_attr().nxy() > 0 ) { } // - // Print a data summary + // Print the grid information and data summary // if ( mlog.verbosity_level() >= 4 ) { + mlog << Debug(4) << "\n" + << "Grid information:\n " + << Dest_Grid->serialize("\n ") << "\n"; + double min_v, max_v; dp.data_range(min_v, max_v); mlog << Debug(4) << "\n" << "Data plane information:\n" - << " plane min: " << min_v << "\n" - << " plane max: " << max_v << "\n" - << " valid time: " << unix_to_yyyymmdd_hhmmss(dp.valid()) << "\n" - << " lead time: " << sec_to_hhmmss(dp.lead()) << "\n" - << " init time: " << unix_to_yyyymmdd_hhmmss(dp.init()) << "\n" - << " accum time: " << sec_to_hhmmss(dp.accum()) << "\n\n"; + << " plane min: " << min_v << "\n" + << " plane max: " << max_v << "\n" + << " valid time: " << unix_to_yyyymmdd_hhmmss(dp.valid()) << "\n" + << " lead time: " << sec_to_hhmmss(dp.lead()) << "\n" + << " init time: " << unix_to_yyyymmdd_hhmmss(dp.init()) << "\n" + << " accum time: " << sec_to_hhmmss(dp.accum()) << "\n\n"; } diff --git a/met/src/libcode/vx_grid/gaussian_grid.cc b/met/src/libcode/vx_grid/gaussian_grid.cc index 61edbbb7f8..db2cc9c94c 100644 --- a/met/src/libcode/vx_grid/gaussian_grid.cc +++ b/met/src/libcode/vx_grid/gaussian_grid.cc @@ -370,19 +370,19 @@ return; //////////////////////////////////////////////////////////////////////// -ConcatString GaussianGrid::serialize() const +ConcatString GaussianGrid::serialize(const char *sep) const { ConcatString a; char junk[256]; -a << "Projection: Gaussian"; +a << "Projection: Gaussian" << sep; snprintf(junk, sizeof(junk), " Lon_Zero: %.4f", Lon_Zero); a << junk; -a << " Nx: " << Nx; -a << " Ny: " << Ny; +a << "Nx: " << Nx << sep; +a << "Ny: " << Ny; // // done diff --git a/met/src/libcode/vx_grid/gaussian_grid.h b/met/src/libcode/vx_grid/gaussian_grid.h index 1825f853e0..d190f7d117 100644 --- a/met/src/libcode/vx_grid/gaussian_grid.h +++ b/met/src/libcode/vx_grid/gaussian_grid.h @@ -75,7 +75,7 @@ class GaussianGrid : public GridRep { void dump(ostream &, int = 0) const; - ConcatString serialize() const; + ConcatString serialize(const char *sep=" ") const; GridInfo info() const; diff --git a/met/src/libcode/vx_grid/goes_grid.cc b/met/src/libcode/vx_grid/goes_grid.cc index 71e6ff61d5..ec8280b2e5 100644 --- a/met/src/libcode/vx_grid/goes_grid.cc +++ b/met/src/libcode/vx_grid/goes_grid.cc @@ -307,17 +307,17 @@ return; //////////////////////////////////////////////////////////////////////// -ConcatString GoesImagerGrid::serialize() const +ConcatString GoesImagerGrid::serialize(const char *sep) const { ConcatString a; char junk[256]; -a << "Projection: GoesImager"; +a << "Projection: GoesImager" << sep; -a << " Nx: " << Nx; -a << " Ny: " << Ny; +a << "Nx: " << Nx << sep; +a << "Ny: " << Ny; //snprintf(junk, sizeof(junk), " Lat_LL: %.3f", Lat_LL); a << junk; //snprintf(junk, sizeof(junk), " Lon_LL: %.3f", Lon_LL); a << junk; diff --git a/met/src/libcode/vx_grid/goes_grid.h b/met/src/libcode/vx_grid/goes_grid.h index 291428fa33..3ecb68a099 100644 --- a/met/src/libcode/vx_grid/goes_grid.h +++ b/met/src/libcode/vx_grid/goes_grid.h @@ -66,7 +66,7 @@ class GoesImagerGrid : public GridRep { void dump(ostream &, int = 0) const; - ConcatString serialize() const; + ConcatString serialize(const char *sep=" ") const; GridInfo info () const; diff --git a/met/src/libcode/vx_grid/grid_base.cc b/met/src/libcode/vx_grid/grid_base.cc index ece0332ce1..3d26ef18af 100644 --- a/met/src/libcode/vx_grid/grid_base.cc +++ b/met/src/libcode/vx_grid/grid_base.cc @@ -900,13 +900,13 @@ return ( rep->name() ); //////////////////////////////////////////////////////////////////////// -ConcatString Grid::serialize() const +ConcatString Grid::serialize(const char *sep) const { ConcatString s; -if ( rep ) s = rep->serialize(); +if ( rep ) s = rep->serialize(sep); return ( s ); diff --git a/met/src/libcode/vx_grid/grid_base.h b/met/src/libcode/vx_grid/grid_base.h index 5a225f6275..801de26c5b 100644 --- a/met/src/libcode/vx_grid/grid_base.h +++ b/met/src/libcode/vx_grid/grid_base.h @@ -162,7 +162,7 @@ class GridRep : public GridInterface { virtual void dump(ostream &, int = 0) const = 0; - virtual ConcatString serialize() const = 0; + virtual ConcatString serialize(const char *sep=" ") const = 0; virtual GridInfo info() const = 0; @@ -240,7 +240,7 @@ class Grid : public GridInterface { ConcatString name() const; - ConcatString serialize() const; + ConcatString serialize(const char *sep=" ") const; GridInfo info() const; diff --git a/met/src/libcode/vx_grid/latlon_grid.cc b/met/src/libcode/vx_grid/latlon_grid.cc index 11e779cc66..7ba1ea13ec 100644 --- a/met/src/libcode/vx_grid/latlon_grid.cc +++ b/met/src/libcode/vx_grid/latlon_grid.cc @@ -286,7 +286,7 @@ return; //////////////////////////////////////////////////////////////////////// -ConcatString LatLonGrid::serialize() const +ConcatString LatLonGrid::serialize(const char *sep) const { @@ -294,18 +294,18 @@ ConcatString a; char junk[256]; -a << "Projection: Lat/Lon"; +a << "Projection: Lat/Lon" << sep; -a << " Nx: " << Nx; -a << " Ny: " << Ny; +a << "Nx: " << Nx << sep; +a << "Ny: " << Ny << sep; -snprintf(junk, sizeof(junk), " lat_ll: %.3f", lat_ll); a << junk; -snprintf(junk, sizeof(junk), " lon_ll: %.3f", lon_ll); a << junk; +snprintf(junk, sizeof(junk), "lat_ll: %.3f", lat_ll); a << junk << sep; +snprintf(junk, sizeof(junk), "lon_ll: %.3f", lon_ll); a << junk << sep; -snprintf(junk, sizeof(junk), " delta_lat: %.3f", delta_lat); a << junk; -snprintf(junk, sizeof(junk), " delta_lon: %.3f", delta_lon); a << junk; +snprintf(junk, sizeof(junk), "delta_lat: %.3f", delta_lat); a << junk << sep; +snprintf(junk, sizeof(junk), "delta_lon: %.3f", delta_lon); a << junk << sep; -snprintf(junk, sizeof(junk), " wrapLon: %s", bool_to_string(wrapLon)); a << junk; +snprintf(junk, sizeof(junk), "wrapLon: %s", bool_to_string(wrapLon)); a << junk; // // done diff --git a/met/src/libcode/vx_grid/latlon_grid.h b/met/src/libcode/vx_grid/latlon_grid.h index 21c86d6e2c..611c618f2a 100644 --- a/met/src/libcode/vx_grid/latlon_grid.h +++ b/met/src/libcode/vx_grid/latlon_grid.h @@ -71,7 +71,7 @@ class LatLonGrid : public GridRep { void dump(ostream &, int = 0) const; - ConcatString serialize() const; + ConcatString serialize(const char *sep=" ") const; GridInfo info() const; diff --git a/met/src/libcode/vx_grid/lc_grid.cc b/met/src/libcode/vx_grid/lc_grid.cc index 41a2e375e9..1033b6edf4 100644 --- a/met/src/libcode/vx_grid/lc_grid.cc +++ b/met/src/libcode/vx_grid/lc_grid.cc @@ -551,29 +551,29 @@ return; //////////////////////////////////////////////////////////////////////// -ConcatString LambertGrid::serialize() const +ConcatString LambertGrid::serialize(const char *sep) const { ConcatString a; char junk[256]; -a << "Projection: Lambert Conformal"; +a << "Projection: Lambert Conformal" << sep; -a << " Nx: " << Nx; -a << " Ny: " << Ny; +a << "Nx: " << Nx << sep; +a << "Ny: " << Ny << sep; -snprintf(junk, sizeof(junk), " Lat_LL: %.3f", Lat_LL); a << junk; -snprintf(junk, sizeof(junk), " Lon_LL: %.3f", Lon_LL); a << junk; +snprintf(junk, sizeof(junk), "Lat_LL: %.3f", Lat_LL); a << junk << sep; +snprintf(junk, sizeof(junk), "Lon_LL: %.3f", Lon_LL); a << junk << sep; -snprintf(junk, sizeof(junk), " Lon_orient: %.3f", Lon_orient); a << junk; +snprintf(junk, sizeof(junk), "Lon_orient: %.3f", Lon_orient); a << junk << sep; -snprintf(junk, sizeof(junk), " Alpha: %.3f", Alpha); a << junk; +snprintf(junk, sizeof(junk), "Alpha: %.3f", Alpha); a << junk << sep; -snprintf(junk, sizeof(junk), " Cone: %.3f", Cone); a << junk; +snprintf(junk, sizeof(junk), "Cone: %.3f", Cone); a << junk << sep; -snprintf(junk, sizeof(junk), " Bx: %.4f", Bx); a << junk; -snprintf(junk, sizeof(junk), " By: %.4f", By); a << junk; +snprintf(junk, sizeof(junk), "Bx: %.4f", Bx); a << junk << sep; +snprintf(junk, sizeof(junk), "By: %.4f", By); a << junk; // // done diff --git a/met/src/libcode/vx_grid/lc_grid.h b/met/src/libcode/vx_grid/lc_grid.h index 0b3fefdaaa..d31ffc2e61 100644 --- a/met/src/libcode/vx_grid/lc_grid.h +++ b/met/src/libcode/vx_grid/lc_grid.h @@ -112,7 +112,7 @@ class LambertGrid : public GridRep { void dump(ostream &, int = 0) const; - ConcatString serialize() const; + ConcatString serialize(const char *sep=" ") const; GridInfo info () const; diff --git a/met/src/libcode/vx_grid/merc_grid.cc b/met/src/libcode/vx_grid/merc_grid.cc index d89a7898e4..9fdfb92975 100644 --- a/met/src/libcode/vx_grid/merc_grid.cc +++ b/met/src/libcode/vx_grid/merc_grid.cc @@ -503,29 +503,29 @@ return; //////////////////////////////////////////////////////////////////////// -ConcatString MercatorGrid::serialize() const +ConcatString MercatorGrid::serialize(const char *sep) const { ConcatString a; char junk[256]; -a << "Projection: Mercator"; +a << "Projection: Mercator" << sep; -a << " Nx: " << Nx; -a << " Ny: " << Ny; +a << "Nx: " << Nx << sep; +a << "Ny: " << Ny << sep; -snprintf(junk, sizeof(junk), " Lat_LL_radians: %.4f", Lat_LL_radians); a << junk; -snprintf(junk, sizeof(junk), " Lon_LL_radians: %.4f", Lon_LL_radians); a << junk; +snprintf(junk, sizeof(junk), "Lat_LL_radians: %.4f", Lat_LL_radians); a << junk << sep; +snprintf(junk, sizeof(junk), "Lon_LL_radians: %.4f", Lon_LL_radians); a << junk << sep; -snprintf(junk, sizeof(junk), " Lat_UR_radians: %.4f", Lat_UR_radians); a << junk; -snprintf(junk, sizeof(junk), " Lon_UR_radians: %.4f", Lon_UR_radians); a << junk; +snprintf(junk, sizeof(junk), "Lat_UR_radians: %.4f", Lat_UR_radians); a << junk << sep; +snprintf(junk, sizeof(junk), "Lon_UR_radians: %.4f", Lon_UR_radians); a << junk << sep; -snprintf(junk, sizeof(junk), " Mx: %.4f", Mx); a << junk; -snprintf(junk, sizeof(junk), " My: %.4f", My); a << junk; +snprintf(junk, sizeof(junk), "Mx: %.4f", Mx); a << junk << sep; +snprintf(junk, sizeof(junk), "My: %.4f", My); a << junk << sep; -snprintf(junk, sizeof(junk), " Bx: %.4f", Bx); a << junk; -snprintf(junk, sizeof(junk), " By: %.4f", By); a << junk; +snprintf(junk, sizeof(junk), "Bx: %.4f", Bx); a << junk << sep; +snprintf(junk, sizeof(junk), "By: %.4f", By); a << junk; // // done diff --git a/met/src/libcode/vx_grid/merc_grid.h b/met/src/libcode/vx_grid/merc_grid.h index f46a9c3bf8..c40ab09afd 100644 --- a/met/src/libcode/vx_grid/merc_grid.h +++ b/met/src/libcode/vx_grid/merc_grid.h @@ -95,7 +95,7 @@ class MercatorGrid : public GridRep { void dump(ostream &, int = 0) const; - ConcatString serialize() const; + ConcatString serialize(const char *sep=" ") const; GridInfo info() const; diff --git a/met/src/libcode/vx_grid/rot_latlon_grid.cc b/met/src/libcode/vx_grid/rot_latlon_grid.cc index 12b6286135..e4cb6a46fb 100644 --- a/met/src/libcode/vx_grid/rot_latlon_grid.cc +++ b/met/src/libcode/vx_grid/rot_latlon_grid.cc @@ -300,7 +300,7 @@ return; //////////////////////////////////////////////////////////////////////// -ConcatString RotatedLatLonGrid::serialize() const +ConcatString RotatedLatLonGrid::serialize(const char *sep) const { @@ -308,23 +308,23 @@ ConcatString a; char junk[256]; -a << "Projection: Rotated Lat/Lon"; +a << "Projection: Rotated Lat/Lon" << sep; -a << " Nx: " << Nx; -a << " Ny: " << Ny; +a << "Nx: " << Nx << sep; +a << "Ny: " << Ny << sep; -snprintf(junk, sizeof(junk), " rot_lat_ll: %.3f", RData.rot_lat_ll); a << junk; -snprintf(junk, sizeof(junk), " rot_lon_ll: %.3f", RData.rot_lon_ll); a << junk; +snprintf(junk, sizeof(junk), "rot_lat_ll: %.3f", RData.rot_lat_ll); a << junk << sep; +snprintf(junk, sizeof(junk), "rot_lon_ll: %.3f", RData.rot_lon_ll); a << junk << sep; -snprintf(junk, sizeof(junk), " delta_rot_lat: %.3f", RData.delta_rot_lat); a << junk; -snprintf(junk, sizeof(junk), " delta_rot_lon: %.3f", RData.delta_rot_lon); a << junk; +snprintf(junk, sizeof(junk), "delta_rot_lat: %.3f", RData.delta_rot_lat); a << junk << sep; +snprintf(junk, sizeof(junk), "delta_rot_lon: %.3f", RData.delta_rot_lon); a << junk << sep; -snprintf(junk, sizeof(junk), " wrapLon: %s", bool_to_string(wrapLon)); a << junk; +snprintf(junk, sizeof(junk), "wrapLon: %s", bool_to_string(wrapLon)); a << junk << sep; -snprintf(junk, sizeof(junk), " true_lat_south_pole: %.3f", RData.true_lat_south_pole); a << junk; -snprintf(junk, sizeof(junk), " true_lon_south_pole: %.3f", RData.true_lon_south_pole); a << junk; +snprintf(junk, sizeof(junk), "true_lat_south_pole: %.3f", RData.true_lat_south_pole); a << junk << sep; +snprintf(junk, sizeof(junk), "true_lon_south_pole: %.3f", RData.true_lon_south_pole); a << junk << sep; -snprintf(junk, sizeof(junk), " aux_rotation: %.3f", RData.aux_rotation); a << junk; +snprintf(junk, sizeof(junk), "aux_rotation: %.3f", RData.aux_rotation); a << junk; // // done diff --git a/met/src/libcode/vx_grid/rot_latlon_grid.h b/met/src/libcode/vx_grid/rot_latlon_grid.h index 79a75e1869..d4c52bb56d 100644 --- a/met/src/libcode/vx_grid/rot_latlon_grid.h +++ b/met/src/libcode/vx_grid/rot_latlon_grid.h @@ -64,7 +64,7 @@ class RotatedLatLonGrid : public LatLonGrid { void dump(ostream &, int = 0) const; - ConcatString serialize() const; + ConcatString serialize(const char *sep=" ") const; GridInfo info() const; diff --git a/met/src/libcode/vx_grid/st_grid.cc b/met/src/libcode/vx_grid/st_grid.cc index 3b64e4a14d..e6a4e9a631 100644 --- a/met/src/libcode/vx_grid/st_grid.cc +++ b/met/src/libcode/vx_grid/st_grid.cc @@ -483,26 +483,26 @@ return; //////////////////////////////////////////////////////////////////////// -ConcatString StereographicGrid::serialize() const +ConcatString StereographicGrid::serialize(const char *sep) const { ConcatString a; char junk[256]; -a << "Projection: Stereographic"; +a << "Projection: Stereographic" << sep; -a << " Nx: " << Nx; -a << " Ny: " << Ny; +a << "Nx: " << Nx << sep; +a << "Ny: " << Ny << sep; -a << " IsNorthHemisphere: " << ( IsNorthHemisphere ? "true" : "false"); +a << "IsNorthHemisphere: " << ( IsNorthHemisphere ? "true" : "false") << sep; -snprintf(junk, sizeof(junk), " Lon_orient: %.3f", Lon_orient); a << junk; +snprintf(junk, sizeof(junk), "Lon_orient: %.3f", Lon_orient); a << junk << sep; -snprintf(junk, sizeof(junk), " Bx: %.3f", Bx); a << junk; -snprintf(junk, sizeof(junk), " By: %.3f", By); a << junk; +snprintf(junk, sizeof(junk), "Bx: %.3f", Bx); a << junk << sep; +snprintf(junk, sizeof(junk), "By: %.3f", By); a << junk << sep; -snprintf(junk, sizeof(junk), " Alpha: %.4f", Alpha); a << junk; +snprintf(junk, sizeof(junk), "Alpha: %.4f", Alpha); a << junk; // // done diff --git a/met/src/libcode/vx_grid/st_grid.h b/met/src/libcode/vx_grid/st_grid.h index 9fecdba445..0c26daac69 100644 --- a/met/src/libcode/vx_grid/st_grid.h +++ b/met/src/libcode/vx_grid/st_grid.h @@ -93,7 +93,7 @@ class StereographicGrid : public GridRep { void dump(ostream &, int = 0) const; - ConcatString serialize() const; + ConcatString serialize(const char *sep=" ") const; GridInfo info() const; From 8d09093c5115c5a42f5e518319b092e0524f0cf4 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Fri, 4 Feb 2022 19:57:56 -0700 Subject: [PATCH 3/6] Per #1055, add a test to unit_regrid.xml to demonstrate the processing of the rotated lat/lon grid from a CF-compliant NetCDF file. --- test/xml/unit_regrid.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/xml/unit_regrid.xml b/test/xml/unit_regrid.xml index 512b4f0a6c..869be79a53 100644 --- a/test/xml/unit_regrid.xml +++ b/test/xml/unit_regrid.xml @@ -373,4 +373,20 @@ + + + + &MET_BIN;/regrid_data_plane + \ + &DATA_DIR_MODEL;/nccf/india_rotated_unrotated_grid.nc \ + "latlon 250 220 -10 54 0.25 0.25" \ + &OUTPUT_DIR;/regrid/regrid_data_plane_NC_ROT_LAT_LON.nc \ + -field 'name="number_of_lightning_flashes"; level="(*,*)";' \ + -method MAX -width 5 + + + &OUTPUT_DIR;/regrid/regrid_data_plane_NC_ROT_LAT_LON.nc + + + From b0ba93de945fa5a11e887b3e97e30d2d5867a7d8 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Fri, 4 Feb 2022 22:30:14 -0700 Subject: [PATCH 4/6] Per #1055, fix 4 minor unrelated typos in the MODE chapter of the user's guide. --- met/docs/Users_Guide/mode.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/met/docs/Users_Guide/mode.rst b/met/docs/Users_Guide/mode.rst index ac42611764..ab341e7cce 100644 --- a/met/docs/Users_Guide/mode.rst +++ b/met/docs/Users_Guide/mode.rst @@ -967,16 +967,16 @@ The dimensions and variables included in the mode NetCDF files are described in - Number of Forecast Simple Boundary Points * - fcst_simp_bdy :raw-html:`
` \_lat - fcst_simp_bdy - - Forecast Simple Boundary PoLatitude + - Forecast Simple Boundary Latitude * - fcst_simp_bdy :raw-html:`
` \_lon - fcst_simp_bdy - - Forecast Simple Boundary PoLongitude + - Forecast Simple Boundary Longitude * - fcst_simp_bdy_x - fcst_simp_bdy - - Forecast Simple Boundary PoX-Coordinate + - Forecast Simple Boundary X-Coordinate * - fcst_simp_bdy_y - fcst_simp_bdy - - Forecast Simple Boundary PoY-Coordinate + - Forecast Simple Boundary Y-Coordinate * - fcst_simp_hull :raw-html:`
` \_start - fcst_simp - Forecast Simple Convex Hull Starting Index From 44a7a3b5a409cff9bf9bc99ef68252cdb55ce874 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Fri, 4 Feb 2022 22:34:06 -0700 Subject: [PATCH 5/6] Per #1055, ci-run-unit, fix bug in get_data_from_lat_lon_vars() by adding a swap_to_north return argument. If the parsed dlat value is < 0, need to swap the data from south to north. --- met/src/libcode/vx_data2d_nccf/nccf_file.cc | 27 +++++++++++++++------ met/src/libcode/vx_data2d_nccf/nccf_file.h | 3 ++- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/met/src/libcode/vx_data2d_nccf/nccf_file.cc b/met/src/libcode/vx_data2d_nccf/nccf_file.cc index 3d6c6eb3da..53880b043c 100644 --- a/met/src/libcode/vx_data2d_nccf/nccf_file.cc +++ b/met/src/libcode/vx_data2d_nccf/nccf_file.cc @@ -2419,12 +2419,13 @@ void NcCfFile::get_grid_mapping_rotated_latitude_longitude(const NcVar *grid_map exit(1); } - LatLonData ll_data; - - ll_data = get_data_from_lat_lon_vars(_yCoordVar, _xCoordVar, lat_counts, lon_counts); - - // Fill in the Rotated LatLon data structure. + // Store spacing in LatLon data structure + bool swap_to_north; + LatLonData ll_data = get_data_from_lat_lon_vars(_yCoordVar, _xCoordVar, + lat_counts, lon_counts, + swap_to_north); + // Fill in the Rotated LatLon data structure RotatedLatLonData data; data.name = rotated_latlon_proj_type; @@ -3044,9 +3045,13 @@ void NcCfFile::get_grid_from_lat_lon_vars(NcVar *lat_var, NcVar *lon_var, const long lat_counts, const long lon_counts) { static const string method_name = "NcCfFile::get_grid_from_lat_lon_vars()"; - LatLonData data = get_data_from_lat_lon_vars(lat_var, lon_var, lat_counts, lon_counts); + bool swap_to_north; + LatLonData data = get_data_from_lat_lon_vars(lat_var, lon_var, + lat_counts, lon_counts, + swap_to_north); + grid.set(data); // resets swap_to_north to false - if (data.delta_lat < 0) grid.set_swap_to_north(true); + if (swap_to_north) grid.set_swap_to_north(true); } @@ -3054,7 +3059,8 @@ void NcCfFile::get_grid_from_lat_lon_vars(NcVar *lat_var, NcVar *lon_var, LatLonData NcCfFile::get_data_from_lat_lon_vars(NcVar *lat_var, NcVar *lon_var, - const long lat_counts, const long lon_counts) { + const long lat_counts, const long lon_counts, + bool &swap_to_north) { static const string method_name = "get_data_from_lat_lon_vars()"; // Figure out the dlat/dlon values from the dimension variables @@ -3185,10 +3191,15 @@ LatLonData NcCfFile::get_data_from_lat_lon_vars(NcVar *lat_var, NcVar *lon_var, data.delta_lon = dlon; data.Nlat = lat_counts; data.Nlon = lon_counts; + if (dlat < 0) { + swap_to_north = true; data.delta_lat = -dlat; data.lat_ll = lat_values[lat_counts-1]; } + else { + swap_to_north = false; + } return(data); diff --git a/met/src/libcode/vx_data2d_nccf/nccf_file.h b/met/src/libcode/vx_data2d_nccf/nccf_file.h index 54c497e10c..e2acf3ee64 100644 --- a/met/src/libcode/vx_data2d_nccf/nccf_file.h +++ b/met/src/libcode/vx_data2d_nccf/nccf_file.h @@ -183,7 +183,8 @@ class NcCfFile { const long lat_counts, const long lon_counts); LatLonData get_data_from_lat_lon_vars(NcVar *lat_var, NcVar *lon_var, - const long lat_counts, const long lon_counts); + const long lat_counts, const long lon_counts, + bool &swap_to_north); }; From 2735b409193e3a1572919128fe400c9081b1a64c Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Tue, 8 Feb 2022 11:49:36 -0700 Subject: [PATCH 6/6] Totally unrelated to #1055, but just tweaking the documentation very slightly to avoid confusion. --- met/docs/Users_Guide/tc-gen.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/met/docs/Users_Guide/tc-gen.rst b/met/docs/Users_Guide/tc-gen.rst index 74271ec9e1..bf91572fcc 100644 --- a/met/docs/Users_Guide/tc-gen.rst +++ b/met/docs/Users_Guide/tc-gen.rst @@ -64,7 +64,7 @@ Required arguments for tc_gen 3. The **-shape source** argument is the path to one or more NHC genesis warning area shapefiles, an ASCII file list containing them, or a top-level directory with files matching the regular expression "gtwo_areas.*.shp". The genesis warning areas and corresponding 2, 5, and 7 day probability values area verified against the **-track** data. -Note: The **-genesis**, **-edeck**, or **-shape** options must be used at least once. +Note: At least one of the **-genesis**, **-edeck**, or **-shape** command line options are required. 4. The **-track source** argument is one or more ATCF reference track files or an ASCII file list or top-level directory containing them, with files ending in ".dat". This tool processes either Best track data from bdeck files, or operational track data (e.g. CARQ) from adeck files, or both. Providing both bdeck and adeck files will result in a richer dataset to match with the **-genesis** files. Both adeck and bdeck data should be provided using the **-track** option. The **-track** option must be used at least once.