示例#1
0
 def test_spherical_default_supported(self):
     cs_by_shape = {
         0: icoord_systems.GeogCS(6367470),
         6: icoord_systems.GeogCS(6371229)
     }
     for shape, expected in cs_by_shape.items():
         result = ellipsoid(shape, MDI, MDI, MDI)
         self.assertEqual(result, expected)
示例#2
0
    def test_netcdf_save_gridmapping(self):
        # Test saving CF-netCDF from a cubelist with various grid mappings.

        c1 = self.cubell
        c2 = self.cubell.copy()
        c3 = self.cubell.copy()

        coord_system = icoord_systems.GeogCS(6371229)
        coord_system2 = icoord_systems.GeogCS(6371228)
        coord_system3 = icoord_systems.RotatedGeogCS(30, 30)

        c1.add_dim_coord(
            iris.coords.DimCoord(np.arange(1, 3),
                                 'latitude',
                                 long_name='1',
                                 units='degrees',
                                 coord_system=coord_system), 1)
        c1.add_dim_coord(
            iris.coords.DimCoord(np.arange(1, 3),
                                 'longitude',
                                 long_name='1',
                                 units='degrees',
                                 coord_system=coord_system), 0)

        c2.add_dim_coord(
            iris.coords.DimCoord(np.arange(1, 3),
                                 'latitude',
                                 long_name='2',
                                 units='degrees',
                                 coord_system=coord_system2), 1)
        c2.add_dim_coord(
            iris.coords.DimCoord(np.arange(1, 3),
                                 'longitude',
                                 long_name='2',
                                 units='degrees',
                                 coord_system=coord_system2), 0)

        c3.add_dim_coord(
            iris.coords.DimCoord(np.arange(1, 3),
                                 'grid_latitude',
                                 long_name='3',
                                 units='degrees',
                                 coord_system=coord_system3), 1)
        c3.add_dim_coord(
            iris.coords.DimCoord(np.arange(1, 3),
                                 'grid_longitude',
                                 long_name='3',
                                 units='degrees',
                                 coord_system=coord_system3), 0)

        cubes = iris.cube.CubeList([c1, c2, c3])
        with self.temp_filename(suffix='.nc') as file_out:
            iris.save(cubes, file_out)

            # Check the netCDF file against CDL expected output.
            self.assertCDL(file_out,
                           ('netcdf', 'netcdf_save_gridmapmulti.cdl'))
示例#3
0
 def test_lat_lon_major_minor(self):
     major = 63781370
     minor = 63567523
     self.grid.semi_major_axis = major
     self.grid.semi_minor_axis = minor
     crs = pyke_rules.build_coordinate_system(self.grid)
     self.assertEqual(crs, icoord_systems.GeogCS(major, minor))
def _cube_data(data):
    """Returns a cube given a list of lat lon information."""
    cube = iris.cube.Cube(data)
    lat_lon_coord_system = icoord_systems.GeogCS(6371229)

    step = 4.0
    start = step / 2
    count = 90
    pts = start + numpy.arange(count, dtype=numpy.float32) * step
    lon_coord = icoords.DimCoord(pts,
                                 standard_name='longitude',
                                 units='degrees',
                                 coord_system=lat_lon_coord_system,
                                 circular=True)
    lon_coord.guess_bounds()

    start = -90
    step = 4.0
    count = 45
    pts = start + numpy.arange(count, dtype=numpy.float32) * step
    lat_coord = icoords.DimCoord(pts,
                                 standard_name='latitude',
                                 units='degrees',
                                 coord_system=lat_lon_coord_system)
    lat_coord.guess_bounds()

    cube.add_dim_coord(lat_coord, 0)
    cube.add_dim_coord(lon_coord, 1)
    return cube
示例#5
0
    def test_load_tmerc_grid_with_projection_origin(self):
        # Test loading a single CF-netCDF file with a transverse Mercator
        # grid_mapping that uses longitude_of_projection_origin and
        # scale_factor_at_projection_origin instead of
        # longitude_of_central_meridian and scale_factor_at_central_meridian.
        cube = iris.load_cube(
            tests.get_data_path((
                "NetCDF",
                "transverse_mercator",
                "projection_origin_attributes.nc",
            )))

        expected = icoord_systems.TransverseMercator(
            latitude_of_projection_origin=49.0,
            longitude_of_central_meridian=-2.0,
            false_easting=400000.0,
            false_northing=-100000.0,
            scale_factor_at_central_meridian=0.9996012717,
            ellipsoid=icoord_systems.GeogCS(semi_major_axis=6377563.396,
                                            semi_minor_axis=6356256.91),
        )
        self.assertEqual(
            cube.coord("projection_x_coordinate").coord_system, expected)
        self.assertEqual(
            cube.coord("projection_y_coordinate").coord_system, expected)
示例#6
0
def NAME_to_cube(filenames, callback):
    """Returns a generator of cubes given a list of filenames and a callback."""

    for filename in filenames:
        header, column_headings, data_arrays = load_NAME_III(filename)

        for i, data_array in enumerate(data_arrays):
            # turn the dictionary of column headers with a list of header information for each field into a dictionary of
            # headers for just this field. Ignore the first 4 columns of grid position (data was located with the data array).
            field_headings = dict([(k, v[i + 4]) for k, v in column_headings.iteritems()])

            # make an cube
            cube = iris.cube.Cube(data_array)

            # define the name and unit
            name = ('%s %s' % (field_headings['species'], field_headings['quantity'])).upper().replace(' ', '_')
            cube.rename(name)
            # Some units are badly encoded in the file, fix this by putting a space in between. (if gs is not found, then the
            # string will be returned unchanged)
            cube.units = field_headings['unit'].replace('gs', 'g s')

            # define and add the singular coordinates of the field (flight level, time etc.)
            cube.add_aux_coord(icoords.AuxCoord(field_headings['z_level'], long_name='flight_level', units='1'))

            # define the time unit and use it to serialise the datetime for the time coordinate
            time_unit = iris.unit.Unit('hours since epoch', calendar=iris.unit.CALENDAR_GREGORIAN)
            time_coord = icoords.AuxCoord(time_unit.date2num(field_headings['time']), standard_name='time', units=time_unit)
            cube.add_aux_coord(time_coord)

            # build a coordinate system which can be referenced by latitude and longitude coordinates
            lat_lon_coord_system = icoord_systems.GeogCS(6371229)

            # build regular latitude and longitude coordinates which have bounds
            start = header['X grid origin'] + header['X grid resolution']
            step = header['X grid resolution']
            count = header['X grid size']
            pts = start + np.arange(count, dtype=np.float32) * step
            lon_coord = icoords.DimCoord(pts, standard_name='longitude', units='degrees', coord_system=lat_lon_coord_system)
            lon_coord.guess_bounds()

            start = header['Y grid origin'] + header['Y grid resolution']
            step = header['Y grid resolution']
            count = header['Y grid size']
            pts = start + np.arange(count, dtype=np.float32) * step
            lat_coord = icoords.DimCoord(pts, standard_name='latitude', units='degrees', coord_system=lat_lon_coord_system)
            lat_coord.guess_bounds()

            # add the latitude and longitude coordinates to the cube, with mappings to data dimensions
            cube.add_dim_coord(lat_coord, 0)
            cube.add_dim_coord(lon_coord, 1)

            # implement standard iris callback capability. Although callbacks are not used in this example, the standard
            # mechanism for a custom loader to implement a callback is shown:
            cube = iris.io.run_callback(callback, cube, [header, field_headings, data_array], filename)

            # yield the cube created (the loop will continue when the next() element is requested)
            yield cube
示例#7
0
 def test_oblate_shape_3_7(self):
     for shape in [3, 7]:
         major, minor = 1, 10
         scale = 1
         result = ellipsoid(shape, major, minor, MDI)
         if shape == 3:
             # Convert km to m.
             scale = 1000
         expected = icoord_systems.GeogCS(major * scale, minor * scale)
         self.assertEqual(result, expected)
示例#8
0
    def _compute_extra_keys(self):
        """Compute our extra keys."""
        global unknown_string

        self.extra_keys = {}
        forecastTime = self.startStep

        # regular or rotated grid?
        try:
            longitudeOfSouthernPoleInDegrees = \
                self.longitudeOfSouthernPoleInDegrees
            latitudeOfSouthernPoleInDegrees = \
                self.latitudeOfSouthernPoleInDegrees
        except AttributeError:
            longitudeOfSouthernPoleInDegrees = 0.0
            latitudeOfSouthernPoleInDegrees = 90.0

        centre = gribapi.grib_get_string(self.grib_message, "centre")

        # default values
        self.extra_keys = {
            '_referenceDateTime': -1.0,
            '_phenomenonDateTime': -1.0,
            '_periodStartDateTime': -1.0,
            '_periodEndDateTime': -1.0,
            '_levelTypeName': unknown_string,
            '_levelTypeUnits': unknown_string,
            '_firstLevelTypeName': unknown_string,
            '_firstLevelTypeUnits': unknown_string,
            '_firstLevel': -1.0,
            '_secondLevelTypeName': unknown_string,
            '_secondLevel': -1.0,
            '_originatingCentre': unknown_string,
            '_forecastTime': None,
            '_forecastTimeUnit': unknown_string,
            '_coord_system': None,
            '_x_circular': False,
            '_x_coord_name': unknown_string,
            '_y_coord_name': unknown_string,
            # These are here to avoid repetition in the rules
            # files, and reduce the very long line lengths.
            '_x_points': None,
            '_y_points': None,
            '_cf_data': None
        }

        # cf phenomenon translation
        # Get centre code (N.B. self.centre has default type = string)
        centre_number = gribapi.grib_get_long(self.grib_message, "centre")
        # Look for a known grib1-to-cf translation (or None).
        cf_data = gptx.grib1_phenom_to_cf_info(
            table2_version=self.table2Version,
            centre_number=centre_number,
            param_number=self.indicatorOfParameter)
        self.extra_keys['_cf_data'] = cf_data

        # reference date
        self.extra_keys['_referenceDateTime'] = \
            datetime.datetime(int(self.year), int(self.month), int(self.day),
                              int(self.hour), int(self.minute))

        # forecast time with workarounds
        self.extra_keys['_forecastTime'] = forecastTime

        # verification date
        processingDone = self._get_processing_done()
        # time processed?
        if processingDone.startswith("time"):
            validityDate = str(self.validityDate)
            validityTime = "{:04}".format(int(self.validityTime))
            endYear = int(validityDate[:4])
            endMonth = int(validityDate[4:6])
            endDay = int(validityDate[6:8])
            endHour = int(validityTime[:2])
            endMinute = int(validityTime[2:4])

            # fixed forecastTime in hours
            self.extra_keys['_periodStartDateTime'] = \
                (self.extra_keys['_referenceDateTime'] +
                 datetime.timedelta(hours=int(forecastTime)))
            self.extra_keys['_periodEndDateTime'] = \
                datetime.datetime(endYear, endMonth, endDay, endHour,
                                  endMinute)
        else:
            self.extra_keys['_phenomenonDateTime'] = \
                self._get_verification_date()

        # originating centre
        # TODO #574 Expand to include sub-centre
        self.extra_keys['_originatingCentre'] = CENTRE_TITLES.get(
            centre, "unknown centre %s" % centre)

        # forecast time unit as a cm string
        # TODO #575 Do we want PP or GRIB style forecast delta?
        self.extra_keys['_forecastTimeUnit'] = self._timeunit_string()

        # shape of the earth

        # pre-defined sphere
        if self.shapeOfTheEarth == 0:
            geoid = coord_systems.GeogCS(semi_major_axis=6367470)

        # custom sphere
        elif self.shapeOfTheEarth == 1:
            geoid = coord_systems.GeogCS(
                self.scaledValueOfRadiusOfSphericalEarth *
                10**-self.scaleFactorOfRadiusOfSphericalEarth)

        # IAU65 oblate sphere
        elif self.shapeOfTheEarth == 2:
            geoid = coord_systems.GeogCS(6378160, inverse_flattening=297.0)

        # custom oblate spheroid (km)
        elif self.shapeOfTheEarth == 3:
            geoid = coord_systems.GeogCS(
                semi_major_axis=self.scaledValueOfEarthMajorAxis *
                10**-self.scaleFactorOfEarthMajorAxis * 1000.,
                semi_minor_axis=self.scaledValueOfEarthMinorAxis *
                10**-self.scaleFactorOfEarthMinorAxis * 1000.)

        # IAG-GRS80 oblate spheroid
        elif self.shapeOfTheEarth == 4:
            geoid = coord_systems.GeogCS(6378137, None, 298.257222101)

        # WGS84
        elif self.shapeOfTheEarth == 5:
            geoid = \
                coord_systems.GeogCS(6378137, inverse_flattening=298.257223563)

        # pre-defined sphere
        elif self.shapeOfTheEarth == 6:
            geoid = coord_systems.GeogCS(6371229)

        # custom oblate spheroid (m)
        elif self.shapeOfTheEarth == 7:
            geoid = coord_systems.GeogCS(
                semi_major_axis=self.scaledValueOfEarthMajorAxis *
                10**-self.scaleFactorOfEarthMajorAxis,
                semi_minor_axis=self.scaledValueOfEarthMinorAxis *
                10**-self.scaleFactorOfEarthMinorAxis)

        elif self.shapeOfTheEarth == 8:
            raise ValueError("unhandled shape of earth : grib earth shape = 8")

        else:
            raise ValueError("undefined shape of earth")

        gridType = gribapi.grib_get_string(self.grib_message, "gridType")

        if gridType in [
                "regular_ll", "regular_gg", "reduced_ll", "reduced_gg"
        ]:
            self.extra_keys['_x_coord_name'] = "longitude"
            self.extra_keys['_y_coord_name'] = "latitude"
            self.extra_keys['_coord_system'] = geoid
        elif gridType == 'rotated_ll':
            # TODO: Confirm the translation from angleOfRotation to
            # north_pole_lon (usually 0 for both)
            self.extra_keys['_x_coord_name'] = "grid_longitude"
            self.extra_keys['_y_coord_name'] = "grid_latitude"
            southPoleLon = longitudeOfSouthernPoleInDegrees
            southPoleLat = latitudeOfSouthernPoleInDegrees
            self.extra_keys['_coord_system'] = \
                coord_systems.RotatedGeogCS(
                    -southPoleLat,
                    math.fmod(southPoleLon + 180.0, 360.0),
                    self.angleOfRotation, geoid)
        elif gridType == 'polar_stereographic':
            self.extra_keys['_x_coord_name'] = "projection_x_coordinate"
            self.extra_keys['_y_coord_name'] = "projection_y_coordinate"

            if self.projectionCentreFlag == 0:
                pole_lat = 90
            elif self.projectionCentreFlag == 1:
                pole_lat = -90
            else:
                raise TranslationError("Unhandled projectionCentreFlag")

            # Note: I think the grib api defaults LaDInDegrees to 60 for grib1.
            self.extra_keys['_coord_system'] = \
                coord_systems.Stereographic(
                    pole_lat, self.orientationOfTheGridInDegrees, 0, 0,
                    self.LaDInDegrees, ellipsoid=geoid)

        elif gridType == 'lambert':
            self.extra_keys['_x_coord_name'] = "projection_x_coordinate"
            self.extra_keys['_y_coord_name'] = "projection_y_coordinate"

            flag_name = "projectionCenterFlag"

            if getattr(self, flag_name) == 0:
                pole_lat = 90
            elif getattr(self, flag_name) == 1:
                pole_lat = -90
            else:
                raise TranslationError("Unhandled projectionCentreFlag")

            LambertConformal = coord_systems.LambertConformal
            self.extra_keys['_coord_system'] = LambertConformal(
                self.LaDInDegrees,
                self.LoVInDegrees,
                0,
                0,
                secant_latitudes=(self.Latin1InDegrees, self.Latin2InDegrees),
                ellipsoid=geoid)
        else:
            raise TranslationError("unhandled grid type: {}".format(gridType))

        if gridType in ["regular_ll", "rotated_ll"]:
            self._regular_longitude_common()
            j_step = self.jDirectionIncrementInDegrees
            if not self.jScansPositively:
                j_step = -j_step
            self._y_points = (np.arange(self.Nj, dtype=np.float64) * j_step +
                              self.latitudeOfFirstGridPointInDegrees)

        elif gridType in ['regular_gg']:
            # longitude coordinate is straight-forward
            self._regular_longitude_common()
            # get the distinct latitudes, and make sure they are sorted
            # (south-to-north) and then put them in the right direction
            # depending on the scan direction
            latitude_points = gribapi.grib_get_double_array(
                self.grib_message, 'distinctLatitudes').astype(np.float64)
            latitude_points.sort()
            if not self.jScansPositively:
                # we require latitudes north-to-south
                self._y_points = latitude_points[::-1]
            else:
                self._y_points = latitude_points

        elif gridType in ["polar_stereographic", "lambert"]:
            # convert the starting latlon into meters
            cartopy_crs = self.extra_keys['_coord_system'].as_cartopy_crs()
            x1, y1 = cartopy_crs.transform_point(
                self.longitudeOfFirstGridPointInDegrees,
                self.latitudeOfFirstGridPointInDegrees, ccrs.Geodetic())

            if not np.all(np.isfinite([x1, y1])):
                raise TranslationError("Could not determine the first latitude"
                                       " and/or longitude grid point.")

            self._x_points = x1 + self.DxInMetres * np.arange(self.Nx,
                                                              dtype=np.float64)
            self._y_points = y1 + self.DyInMetres * np.arange(self.Ny,
                                                              dtype=np.float64)

        elif gridType in ["reduced_ll", "reduced_gg"]:
            self._x_points = self.longitudes
            self._y_points = self.latitudes

        else:
            raise TranslationError("unhandled grid type")
示例#9
0
 def test_lat_lon_earth_radius(self):
     earth_radius = 63700000
     self.grid.earth_radius = earth_radius
     crs = pyke_rules.build_coordinate_system(self.grid)
     self.assertEqual(crs, icoord_systems.GeogCS(earth_radius))
示例#10
0
def NAME_to_cube(filenames, callback):
    """
    Returns a generator of cubes given a list of filenames and a callback.
    """

    for filename in filenames:
        header, column_headings, data_arrays = load_NAME_III(filename)

        for i, data_array in enumerate(data_arrays):
            # turn the dictionary of column headers with a list of header
            # information for each field into a dictionary of headers for just
            # this field. Ignore the first 4 columns of grid position (data was
            # located with the data array).
            field_headings = dict(
                (k, v[i + 4]) for k, v in column_headings.items())

            # make an cube
            cube = iris.cube.Cube(data_array)

            # define the name and unit
            name = "%s %s" % (
                field_headings["species"],
                field_headings["quantity"],
            )
            name = name.upper().replace(" ", "_")
            cube.rename(name)
            # Some units are badly encoded in the file, fix this by putting a
            # space in between. (if gs is not found, then the string will be
            # returned unchanged)
            cube.units = field_headings["unit"].replace("gs", "g s")

            # define and add the singular coordinates of the field (flight
            # level, time etc.)
            cube.add_aux_coord(
                icoords.AuxCoord(
                    field_headings["z_level"],
                    long_name="flight_level",
                    units="1",
                ))

            # define the time unit and use it to serialise the datetime for the
            # time coordinate
            time_unit = Unit("hours since epoch", calendar=CALENDAR_GREGORIAN)
            time_coord = icoords.AuxCoord(
                time_unit.date2num(field_headings["time"]),
                standard_name="time",
                units=time_unit,
            )
            cube.add_aux_coord(time_coord)

            # build a coordinate system which can be referenced by latitude and
            # longitude coordinates
            lat_lon_coord_system = icoord_systems.GeogCS(6371229)

            # build regular latitude and longitude coordinates which have
            # bounds
            start = header["X grid origin"] + header["X grid resolution"]
            step = header["X grid resolution"]
            count = header["X grid size"]
            pts = start + np.arange(count, dtype=np.float32) * step
            lon_coord = icoords.DimCoord(
                pts,
                standard_name="longitude",
                units="degrees",
                coord_system=lat_lon_coord_system,
            )
            lon_coord.guess_bounds()

            start = header["Y grid origin"] + header["Y grid resolution"]
            step = header["Y grid resolution"]
            count = header["Y grid size"]
            pts = start + np.arange(count, dtype=np.float32) * step
            lat_coord = icoords.DimCoord(
                pts,
                standard_name="latitude",
                units="degrees",
                coord_system=lat_lon_coord_system,
            )
            lat_coord.guess_bounds()

            # add the latitude and longitude coordinates to the cube, with
            # mappings to data dimensions
            cube.add_dim_coord(lat_coord, 0)
            cube.add_dim_coord(lon_coord, 1)

            # implement standard iris callback capability. Although callbacks
            # are not used in this example, the standard mechanism for a custom
            # loader to implement a callback is shown:
            cube = iris.io.run_callback(callback, cube,
                                        [header, field_headings, data_array],
                                        filename)

            # yield the cube created (the loop will continue when the next()
            # element is requested)
            yield cube
示例#11
0
 def setUp(self):
     plt.figure()
     self.geog_cs = ics.GeogCS(6371229.0)
     self.plate_carree = self.geog_cs.as_cartopy_projection()
示例#12
0
    def test_netcdf_save_gridmapping(self):
        # Test saving CF-netCDF from a cubelist with various grid mappings.

        c1 = self.cubell
        c2 = self.cubell.copy()
        c3 = self.cubell.copy()

        coord_system = icoord_systems.GeogCS(6371229)
        coord_system2 = icoord_systems.GeogCS(6371228)
        coord_system3 = icoord_systems.RotatedGeogCS(30, 30)

        c1.add_dim_coord(
            iris.coords.DimCoord(
                np.arange(1, 3),
                "latitude",
                long_name="1",
                units="degrees",
                coord_system=coord_system,
            ),
            1,
        )
        c1.add_dim_coord(
            iris.coords.DimCoord(
                np.arange(1, 3),
                "longitude",
                long_name="1",
                units="degrees",
                coord_system=coord_system,
            ),
            0,
        )

        c2.add_dim_coord(
            iris.coords.DimCoord(
                np.arange(1, 3),
                "latitude",
                long_name="2",
                units="degrees",
                coord_system=coord_system2,
            ),
            1,
        )
        c2.add_dim_coord(
            iris.coords.DimCoord(
                np.arange(1, 3),
                "longitude",
                long_name="2",
                units="degrees",
                coord_system=coord_system2,
            ),
            0,
        )

        c3.add_dim_coord(
            iris.coords.DimCoord(
                np.arange(1, 3),
                "grid_latitude",
                long_name="3",
                units="degrees",
                coord_system=coord_system3,
            ),
            1,
        )
        c3.add_dim_coord(
            iris.coords.DimCoord(
                np.arange(1, 3),
                "grid_longitude",
                long_name="3",
                units="degrees",
                coord_system=coord_system3,
            ),
            0,
        )

        cubes = iris.cube.CubeList([c1, c2, c3])
        with self.temp_filename(suffix=".nc") as file_out:
            iris.save(cubes, file_out)

            # Check the netCDF file against CDL expected output.
            self.assertCDL(file_out,
                           ("netcdf", "netcdf_save_gridmapmulti.cdl"))
示例#13
0
 def test_spherical_shape_1(self):
     shape = 1
     radius = 10
     result = ellipsoid(shape, MDI, MDI, radius)
     expected = icoord_systems.GeogCS(radius)
     self.assertEqual(result, expected)
示例#14
0
     if short_name == "2t":
         color_plot_dist = values_range_for_plot / 20
     bounds = np.arange(total_min, total_max + color_plot_dist,
                        color_plot_dist)
     color_bar_dist = values_range_for_plot / 10
     cmap = plt.get_cmap(colormap)
 time_after_init = start_time_since_init + i * plot_interval
 if surface_bool == 0:
     print("plotting " + short_name + " at level " + str(level) +
           " for t - t_init = " + str(time_after_init) + " s ...")
 if surface_bool == 1:
     print("plotting " + short_name + " for t - t_init = " +
           str(time_after_init) + " s ...")
 if (projection == "Orthographic"):
     fig = plt.figure(figsize=(fig_size, fig_size))
     coord_sys = cs.GeogCS(6371229)
 if (projection == "Mollweide"):
     fig = plt.figure(figsize=(fig_size, 0.5 * fig_size))
     coord_sys = cs.GeogCS(6371229)
 if (projection == "EckertIII"):
     fig = plt.figure(figsize=(fig_size, 0.5 * fig_size))
     coord_sys = cs.GeogCS(6371229)
 if (projection == "Orthographic"):
     fig = plt.figure(figsize=(fig_size, fig_size))
     ax = plt.axes(projection=ccrs.Orthographic(central_latitude=0,
                                                central_longitude=0))
     coord_sys = cs.GeogCS(6371229)
 if (projection == "Mollweide"):
     fig = plt.figure(figsize=(fig_size, fig_size))
     ax = plt.axes(projection=ccrs.Mollweide())
     coord_sys = cs.GeogCS(6371229)
示例#15
0
    def _compute_extra_keys(self):
        """Compute our extra keys."""
        global unknown_string

        self.extra_keys = {}

        # work out stuff based on these values from the message
        edition = self.edition

        # time-processed forcast time is from reference time to start of period
        if edition == 2:
            forecastTime = self.forecastTime

            uft = np.uint32(forecastTime)
            BILL = 2**30

            # Workaround grib api's assumption that forecast time is positive.
            # Handles correctly encoded -ve forecast times up to one -1 billion.
            if hindcast_workaround:
                if 2 * BILL < uft < 3 * BILL:
                    msg = "Re-interpreting negative forecastTime from " \
                            + str(forecastTime)
                    forecastTime = -(uft - 2 * BILL)
                    msg += " to " + str(forecastTime)
                    warnings.warn(msg)

        else:
            forecastTime = self.startStep

        #regular or rotated grid?
        try:
            longitudeOfSouthernPoleInDegrees = self.longitudeOfSouthernPoleInDegrees
            latitudeOfSouthernPoleInDegrees = self.latitudeOfSouthernPoleInDegrees
        except AttributeError:
            longitudeOfSouthernPoleInDegrees = 0.0
            latitudeOfSouthernPoleInDegrees = 90.0

        centre = gribapi.grib_get_string(self.grib_message, "centre")

        #default values
        self.extra_keys = {
            '_referenceDateTime': -1.0,
            '_phenomenonDateTime': -1.0,
            '_periodStartDateTime': -1.0,
            '_periodEndDateTime': -1.0,
            '_levelTypeName': unknown_string,
            '_levelTypeUnits': unknown_string,
            '_firstLevelTypeName': unknown_string,
            '_firstLevelTypeUnits': unknown_string,
            '_firstLevel': -1.0,
            '_secondLevelTypeName': unknown_string,
            '_secondLevel': -1.0,
            '_originatingCentre': unknown_string,
            '_forecastTime': None,
            '_forecastTimeUnit': unknown_string,
            '_coord_system': None,
            '_x_circular': False,
            '_x_coord_name': unknown_string,
            '_y_coord_name': unknown_string,
            # These are here to avoid repetition in the rules files,
            # and reduce the very long line lengths.
            '_x_points': None,
            '_y_points': None,
            '_cf_data': None
        }

        # cf phenomenon translation
        if edition == 1:
            # Get centre code (N.B. self.centre has default type = string)
            centre_number = gribapi.grib_get_long(self.grib_message, "centre")
            # Look for a known grib1-to-cf translation (or None).
            cf_data = gptx.grib1_phenom_to_cf_info(
                table2_version=self.table2Version,
                centre_number=centre_number,
                param_number=self.indicatorOfParameter)
            self.extra_keys['_cf_data'] = cf_data
        elif edition == 2:
            # Don't attempt to interpret params if 'master tables version' is
            # 255, as local params may then have same codes as standard ones.
            if self.tablesVersion != 255:
                # Look for a known grib2-to-cf translation (or None).
                cf_data = gptx.grib2_phenom_to_cf_info(
                    param_discipline=self.discipline,
                    param_category=self.parameterCategory,
                    param_number=self.parameterNumber)
                self.extra_keys['_cf_data'] = cf_data

        #reference date
        self.extra_keys['_referenceDateTime'] = \
            datetime.datetime(int(self.year), int(self.month), int(self.day),
                              int(self.hour), int(self.minute))

        # forecast time with workarounds
        self.extra_keys['_forecastTime'] = forecastTime

        #verification date
        processingDone = self._get_processing_done()
        #time processed?
        if processingDone.startswith("time"):
            if self.edition == 1:
                validityDate = str(self.validityDate)
                validityTime = "{:04}".format(int(self.validityTime))
                endYear = int(validityDate[:4])
                endMonth = int(validityDate[4:6])
                endDay = int(validityDate[6:8])
                endHour = int(validityTime[:2])
                endMinute = int(validityTime[2:4])
            elif self.edition == 2:
                endYear = self.yearOfEndOfOverallTimeInterval
                endMonth = self.monthOfEndOfOverallTimeInterval
                endDay = self.dayOfEndOfOverallTimeInterval
                endHour = self.hourOfEndOfOverallTimeInterval
                endMinute = self.minuteOfEndOfOverallTimeInterval

            # fixed forecastTime in hours
            self.extra_keys['_periodStartDateTime'] = \
                (self.extra_keys['_referenceDateTime'] +
                 datetime.timedelta(hours=int(forecastTime)))
            self.extra_keys['_periodEndDateTime'] = \
                datetime.datetime(endYear, endMonth, endDay, endHour, endMinute)
        else:
            self.extra_keys[
                '_phenomenonDateTime'] = self._get_verification_date()

        #originating centre
        #TODO #574 Expand to include sub-centre
        self.extra_keys['_originatingCentre'] = CENTRE_TITLES.get(
            centre, "unknown centre %s" % centre)

        #forecast time unit as a cm string
        #TODO #575 Do we want PP or GRIB style forecast delta?
        self.extra_keys['_forecastTimeUnit'] = self._timeunit_string()

        #shape of the earth

        #pre-defined sphere
        if self.shapeOfTheEarth == 0:
            geoid = coord_systems.GeogCS(semi_major_axis=6367470)

        #custom sphere
        elif self.shapeOfTheEarth == 1:
            geoid = \
                coord_systems.GeogCS(self.scaledValueOfRadiusOfSphericalEarth * \
                                     self.scaleFactorOfRadiusOfSphericalEarth)

        #IAU65 oblate sphere
        elif self.shapeOfTheEarth == 2:
            geoid = coord_systems.GeogCS(6378160, inverse_flattening=297.0)

        #custom oblate spheroid (km)
        elif self.shapeOfTheEarth == 3:
            geoid = coord_systems.GeogCS(
                semi_major_axis=self.scaledValueOfEarthMajorAxis *
                self.scaleFactorOfEarthMajorAxis * 1000.0,
                semi_minor_axis=self.scaledValueOfEarthMinorAxis *
                self.scaleFactorOfEarthMinorAxis * 1000.0)

        #IAG-GRS80 oblate spheroid
        elif self.shapeOfTheEarth == 4:
            geoid = coord_systems.GeogCS(6378137, None, 298.257222101)

        #WGS84
        elif self.shapeOfTheEarth == 5:
            geoid = \
                coord_systems.GeogCS(6378137, inverse_flattening=298.257223563)

        #pre-defined sphere
        elif self.shapeOfTheEarth == 6:
            geoid = coord_systems.GeogCS(6371229)

        #custom oblate spheroid (m)
        elif self.shapeOfTheEarth == 7:
            geoid = coord_systems.GeogCS(
                semi_major_axis=self.scaledValueOfEarthMajorAxis *
                self.scaleFactorOfEarthMajorAxis,
                semi_minor_axis=self.scaledValueOfEarthMinorAxis *
                self.scaleFactorOfEarthMinorAxis)

        elif self.shapeOfTheEarth == 8:
            raise ValueError("unhandled shape of earth : grib earth shape = 8")

        else:
            raise ValueError("undefined shape of earth")

        gridType = gribapi.grib_get_string(self.grib_message, "gridType")

        if gridType == "regular_ll":
            self.extra_keys['_x_coord_name'] = "longitude"
            self.extra_keys['_y_coord_name'] = "latitude"
            self.extra_keys['_coord_system'] = geoid
        elif gridType == 'rotated_ll':
            # TODO: Confirm the translation from angleOfRotation to
            # north_pole_lon (usually 0 for both)
            self.extra_keys['_x_coord_name'] = "grid_longitude"
            self.extra_keys['_y_coord_name'] = "grid_latitude"
            southPoleLon = longitudeOfSouthernPoleInDegrees
            southPoleLat = latitudeOfSouthernPoleInDegrees
            self.extra_keys['_coord_system'] = \
                iris.coord_systems.RotatedGeogCS(
                                        -southPoleLat,
                                        math.fmod(southPoleLon + 180.0, 360.0),
                                        self.angleOfRotation, geoid)
        elif gridType == 'polar_stereographic':
            self.extra_keys['_x_coord_name'] = "projection_x_coordinate"
            self.extra_keys['_y_coord_name'] = "projection_y_coordinate"

            if self.projectionCentreFlag == 0:
                pole_lat = 90
            elif self.projectionCentreFlag == 1:
                pole_lat = -90
            else:
                raise TranslationError("Unhandled projectionCentreFlag")

            # Note: I think the grib api defaults LaDInDegrees to 60 for grib1.
            self.extra_keys['_coord_system'] = \
                iris.coord_systems.Stereographic(
                    pole_lat, self.orientationOfTheGridInDegrees, 0, 0,
                    self.LaDInDegrees, ellipsoid=geoid)

        else:
            raise TranslationError("unhandled grid type")

        # x and y points, and circularity
        if gridType in ["regular_ll", "rotated_ll"]:
            i_step = self.iDirectionIncrementInDegrees
            j_step = self.jDirectionIncrementInDegrees
            if self.iScansNegatively:
                i_step = -i_step
            if not self.jScansPositively:
                j_step = -j_step
            self._x_points = (np.arange(self.Ni, dtype=np.float64) * i_step +
                              self.longitudeOfFirstGridPointInDegrees)
            self._y_points = (np.arange(self.Nj, dtype=np.float64) * j_step +
                              self.latitudeOfFirstGridPointInDegrees)

            # circular x coord?
            if "longitude" in self.extra_keys['_x_coord_name'] and self.Ni > 1:
                # Is the gap from end to start smaller,
                #  or about equal to the max step?
                points = self._x_points
                gap = 360.0 - abs(points[-1] - points[0])
                max_step = abs(np.diff(points)).max()
                if gap <= max_step:
                    self.extra_keys['_x_circular'] = True
                else:
                    delta = 0.001
                    if abs(1.0 - gap / max_step) < delta:
                        self.extra_keys['_x_circular'] = True

        elif gridType == "polar_stereographic":

            # convert the starting latlon into meters
            cartopy_crs = self.extra_keys['_coord_system'].as_cartopy_crs()
            x1, y1 = cartopy_crs.transform_point(
                self.longitudeOfFirstGridPointInDegrees,
                self.latitudeOfFirstGridPointInDegrees, cartopy.crs.Geodetic())

            self._x_points = x1 + self.DxInMetres * np.arange(self.Nx,
                                                              dtype=np.float64)
            self._y_points = y1 + self.DyInMetres * np.arange(self.Ny,
                                                              dtype=np.float64)

        else:
            raise TranslationError("unhandled grid type")
示例#16
0
    def _compute_extra_keys(self):
        """Compute our extra keys."""
        global unknown_string

        self.extra_keys = {}
        forecastTime = self.startStep

        # regular or rotated grid?
        try:
            longitudeOfSouthernPoleInDegrees = \
                self.longitudeOfSouthernPoleInDegrees
            latitudeOfSouthernPoleInDegrees = \
                self.latitudeOfSouthernPoleInDegrees
        except AttributeError:
            longitudeOfSouthernPoleInDegrees = 0.0
            latitudeOfSouthernPoleInDegrees = 90.0

        centre = gribapi.grib_get_string(self.grib_message, "centre")

        # default values
        self.extra_keys = {'_referenceDateTime': -1.0,
                           '_phenomenonDateTime': -1.0,
                           '_periodStartDateTime': -1.0,
                           '_periodEndDateTime': -1.0,
                           '_levelTypeName': unknown_string,
                           '_levelTypeUnits': unknown_string,
                           '_firstLevelTypeName': unknown_string,
                           '_firstLevelTypeUnits': unknown_string,
                           '_firstLevel': -1.0,
                           '_secondLevelTypeName': unknown_string,
                           '_secondLevel': -1.0,
                           '_originatingCentre': unknown_string,
                           '_forecastTime': None,
                           '_forecastTimeUnit': unknown_string,
                           '_coord_system': None,
                           '_x_circular': False,
                           '_x_coord_name': unknown_string,
                           '_y_coord_name': unknown_string,
                           # These are here to avoid repetition in the rules
                           # files, and reduce the very long line lengths.
                           '_x_points': None,
                           '_y_points': None,
                           '_cf_data': None}

        # cf phenomenon translation
        # Get centre code (N.B. self.centre has default type = string)
        centre_number = gribapi.grib_get_long(self.grib_message, "centre")
        # Look for a known grib1-to-cf translation (or None).
        cf_data = gptx.grib1_phenom_to_cf_info(
            table2_version=self.table2Version,
            centre_number=centre_number,
            param_number=self.indicatorOfParameter)
        self.extra_keys['_cf_data'] = cf_data

        # reference date
        self.extra_keys['_referenceDateTime'] = \
            datetime.datetime(int(self.year), int(self.month), int(self.day),
                              int(self.hour), int(self.minute))

        # forecast time with workarounds
        self.extra_keys['_forecastTime'] = forecastTime

        # verification date
        processingDone = self._get_processing_done()
        # time processed?
        if processingDone.startswith("time"):
            validityDate = str(self.validityDate)
            validityTime = "{:04}".format(int(self.validityTime))
            endYear = int(validityDate[:4])
            endMonth = int(validityDate[4:6])
            endDay = int(validityDate[6:8])
            endHour = int(validityTime[:2])
            endMinute = int(validityTime[2:4])

            # fixed forecastTime in hours
            self.extra_keys['_periodStartDateTime'] = \
                (self.extra_keys['_referenceDateTime'] +
                 datetime.timedelta(hours=int(forecastTime)))
            self.extra_keys['_periodEndDateTime'] = \
                datetime.datetime(endYear, endMonth, endDay, endHour,
                                  endMinute)
        else:
            self.extra_keys['_phenomenonDateTime'] = \
                self._get_verification_date()

        # originating centre
        # TODO #574 Expand to include sub-centre
        self.extra_keys['_originatingCentre'] = CENTRE_TITLES.get(
            centre, "unknown centre %s" % centre)

        # forecast time unit as a cm string
        # TODO #575 Do we want PP or GRIB style forecast delta?
        self.extra_keys['_forecastTimeUnit'] = self._timeunit_string()

        # shape of the earth
        soe_code = self.shapeOfTheEarth
        # As this class is now *only* for GRIB1, 'shapeOfTheEarth' is not a
        # value read from the actual file :  It is really a GRIB2 param, and
        # the value is merely what eccodes (gribapi) gives as the default.
        # This was always = 6, until eccodes 0.19, when it changed to 0.
        # See https://jira.ecmwf.int/browse/ECC-811
        # The two represent different sized spherical earths.
        if soe_code not in (6, 0):
            raise ValueError('Unexpected shapeOfTheEarth value =', soe_code)

        soe_code = 6
        # *FOR NOW* maintain the old behaviour (radius=6371229) in all cases,
        # for backwards compatibility.
        # However, this does not match the 'radiusOfTheEarth' default from the
        # gribapi so is probably incorrect (see above issue ECC-811).
        # So we may change this in future.

        if soe_code == 0:
            # New supposedly-correct default value, matches 'radiusOfTheEarth'.
            geoid = coord_systems.GeogCS(semi_major_axis=6367470)
        elif soe_code == 6:
            # Old value, does *not* match the 'radiusOfTheEarth' parameter.
            geoid = coord_systems.GeogCS(6371229)

        gridType = gribapi.grib_get_string(self.grib_message, "gridType")

        if gridType in ["regular_ll", "regular_gg", "reduced_ll",
                        "reduced_gg"]:
            self.extra_keys['_x_coord_name'] = "longitude"
            self.extra_keys['_y_coord_name'] = "latitude"
            self.extra_keys['_coord_system'] = geoid
        elif gridType == 'rotated_ll':
            # TODO: Confirm the translation from angleOfRotation to
            # north_pole_lon (usually 0 for both)
            self.extra_keys['_x_coord_name'] = "grid_longitude"
            self.extra_keys['_y_coord_name'] = "grid_latitude"
            southPoleLon = longitudeOfSouthernPoleInDegrees
            southPoleLat = latitudeOfSouthernPoleInDegrees
            self.extra_keys['_coord_system'] = \
                coord_systems.RotatedGeogCS(
                    -southPoleLat,
                    math.fmod(southPoleLon + 180.0, 360.0),
                    self.angleOfRotation, geoid)
        elif gridType == 'polar_stereographic':
            self.extra_keys['_x_coord_name'] = "projection_x_coordinate"
            self.extra_keys['_y_coord_name'] = "projection_y_coordinate"

            if self.projectionCentreFlag == 0:
                pole_lat = 90
            elif self.projectionCentreFlag == 1:
                pole_lat = -90
            else:
                raise TranslationError("Unhandled projectionCentreFlag")

            # Note: I think the grib api defaults LaDInDegrees to 60 for grib1.
            self.extra_keys['_coord_system'] = \
                coord_systems.Stereographic(
                    pole_lat, self.orientationOfTheGridInDegrees, 0, 0,
                    self.LaDInDegrees, ellipsoid=geoid)

        elif gridType == 'lambert':
            self.extra_keys['_x_coord_name'] = "projection_x_coordinate"
            self.extra_keys['_y_coord_name'] = "projection_y_coordinate"

            flag_name = "projectionCenterFlag"

            if getattr(self, flag_name) == 0:
                pole_lat = 90
            elif getattr(self, flag_name) == 1:
                pole_lat = -90
            else:
                raise TranslationError("Unhandled projectionCentreFlag")

            LambertConformal = coord_systems.LambertConformal
            self.extra_keys['_coord_system'] = LambertConformal(
                self.LaDInDegrees, self.LoVInDegrees, 0, 0,
                secant_latitudes=(self.Latin1InDegrees, self.Latin2InDegrees),
                ellipsoid=geoid)
        else:
            raise TranslationError("unhandled grid type: {}".format(gridType))

        if gridType in ["regular_ll", "rotated_ll"]:
            self._regular_longitude_common()
            j_step = self.jDirectionIncrementInDegrees
            if not self.jScansPositively:
                j_step = -j_step
            self._y_points = (np.arange(self.Nj, dtype=np.float64) * j_step +
                              self.latitudeOfFirstGridPointInDegrees)

        elif gridType in ['regular_gg']:
            # longitude coordinate is straight-forward
            self._regular_longitude_common()
            # get the distinct latitudes, and make sure they are sorted
            # (south-to-north) and then put them in the right direction
            # depending on the scan direction
            latitude_points = gribapi.grib_get_double_array(
                    self.grib_message, 'distinctLatitudes').astype(np.float64)
            latitude_points.sort()
            if not self.jScansPositively:
                # we require latitudes north-to-south
                self._y_points = latitude_points[::-1]
            else:
                self._y_points = latitude_points

        elif gridType in ["polar_stereographic", "lambert"]:
            # convert the starting latlon into meters
            cartopy_crs = self.extra_keys['_coord_system'].as_cartopy_crs()
            x1, y1 = cartopy_crs.transform_point(
                self.longitudeOfFirstGridPointInDegrees,
                self.latitudeOfFirstGridPointInDegrees,
                ccrs.Geodetic())

            if not np.all(np.isfinite([x1, y1])):
                raise TranslationError("Could not determine the first latitude"
                                       " and/or longitude grid point.")

            self._x_points = x1 + self.DxInMetres * np.arange(self.Nx,
                                                              dtype=np.float64)
            self._y_points = y1 + self.DyInMetres * np.arange(self.Ny,
                                                              dtype=np.float64)

        elif gridType in ["reduced_ll", "reduced_gg"]:
            self._x_points = self.longitudes
            self._y_points = self.latitudes

        else:
            raise TranslationError("unhandled grid type")