def init_xy(self, lon_min, lat_min, lon_max, lat_max, target_cellsize_meters): """Create & initialize x/y dimensions/coordinate vars. Args: lon_min: Minimum longitude of domain. lat_min: Minimum latitude of domain. lon_max: Maximum longitude of domain. lat_max: Maximum latitude of domain. target_cellsize_meters: Target cell size, in meters. Actual calculated cell sizes will be approximations of this. """ # Calculate actual x/y cell sizes cellsize_x, cellsize_y = RegularGrid.calc_cellsizes( lon_min, lat_min, lon_max, lat_max, target_cellsize_meters) # Build a regular grid using calculated cell sizes and given extent reg_grid = RegularGrid(lon_min, lat_min, lon_max, lat_max, cellsize_x, cellsize_y) # Create NetCDF dimensions & coordinate variables using dimension sizes # from regular grid self.create_dims_coord_vars(len(reg_grid.y_coords), len(reg_grid.x_coords)) # Populate NetCDF coordinate variables using regular grid coordinates self.var_x[:] = reg_grid.x_coords[:] self.var_y[:] = reg_grid.y_coords[:] self.nc_file.gridSpacingLongitude = cellsize_x self.nc_file.gridSpacingLatitude = cellsize_y return reg_grid
def test_calc_gridpoints(reg_grid_values): """Test grid point calculation""" reg_grid = RegularGrid(reg_grid_values.lon_min, reg_grid_values.lat_min, reg_grid_values.lon_max, reg_grid_values.lat_max, reg_grid_values.expected_cellsize_x, reg_grid_values.expected_cellsize_y) print(f"Calculated x_coords: {reg_grid.x_coords}") print(f"Calculated y_coords: {reg_grid.y_coords}") assert numpy.allclose(reg_grid.x_coords, reg_grid_values.expected_x_coords) assert numpy.allclose(reg_grid.x_coords, reg_grid_values.expected_x_coords)
def test_calc_cellsizes(reg_grid_values): """Test cell size calculation""" cellsize_x, cellsize_y = RegularGrid.calc_cellsizes( reg_grid_values.lon_min, reg_grid_values.lat_min, reg_grid_values.lon_max, reg_grid_values.lat_max, reg_grid_values.target_cellsize_meters) print(f"Calculated cellsize_x: {cellsize_x}") print(f"Calculated cellsize_y: {cellsize_y}") assert_approx_equal(cellsize_x, reg_grid_values.expected_cellsize_x) assert_approx_equal(cellsize_y, reg_grid_values.expected_cellsize_y)
def init_xy_with_subsets(self, lon_min, lat_min, lon_max, lat_max, target_cellsize_meters, subset_grid_shp, subset_grid_field_name=None): """Create & initialize x/y dimensions/coordinate vars and subset vars. Args: lon_min: Minimum longitude of domain. lat_min: Minimum latitude of domain. lon_max: Maximum longitude of domain. lat_max: Maximum latitude of domain. target_cellsize_meters: Target cell size, in meters. Actual calculated cell sizes will be approximations of this. subset_grid_shp: Path to subset grid polygon shapefile used to define subgrid domains. subset_grid_field_name: Optional, default None) Shapefile field name to be stored in the index file. Raises: Exception when given subset grid shapefile does not exist or does not include any grid polygons intersecting with given extent. Returns: Instance of `RegularGrid` representing the extended generated grid whose extent matches the union of all intersecting subset grid polygons. """ shp = ogr.Open(subset_grid_shp) layer = shp.GetLayer() # Create OGR Geometry from ocean model grid extent ring = ogr.Geometry(ogr.wkbLinearRing) ring.AddPoint(lon_min, lat_max) ring.AddPoint(lon_min, lat_min) ring.AddPoint(lon_max, lat_min) ring.AddPoint(lon_max, lat_max) ring.AddPoint(lon_min, lat_max) # Create polygon ofs_poly = ogr.Geometry(ogr.wkbPolygon) ofs_poly.AddGeometry(ring) # Get the EPSG value from the import shapefile and transform to WGS84 spatial_ref = layer.GetSpatialRef() shp_srs = spatial_ref.GetAttrValue('AUTHORITY', 1) source = osr.SpatialReference() source.ImportFromEPSG(int(shp_srs)) target = osr.SpatialReference() target.ImportFromEPSG(4326) transform = osr.CoordinateTransformation(source, target) ofs_poly.Transform(transform) # Find the intersection between grid polygon and ocean model grid extent subset_polys = {} fids = [] fields = {} fid = 0 for feature in layer: geom = feature.GetGeometryRef() if ofs_poly.Intersects(geom): subset_polys[fid] = geom.ExportToJson() if subset_grid_field_name is not None: field_name = feature.GetField(str(subset_grid_field_name)) fields.update({fid: field_name}) fids.append(fid) else: fids.append(fid) fid += 1 if len(fids) == 0: raise Exception( 'Given subset grid shapefile contains no polygons that intersect with model domain; cannot proceed.' ) # Use a single subset polygon to calculate x/y cell sizes. This ensures # that cells do not fall on the border between two grid polygons. single_polygon = ogr.Geometry(ogr.wkbMultiPolygon) single_polygon.AddGeometry( ogr.CreateGeometryFromJson(subset_polys[fids[0]])) sp_x_min, sp_x_max, sp_y_min, sp_y_max = single_polygon.GetEnvelope() cellsize_x, cellsize_y = RegularGrid.calc_cellsizes( sp_x_min, sp_y_min, sp_x_max, sp_y_max, target_cellsize_meters) # Combine identified subset grid polygons into single multipolygon to # calculate full extent of all combined subset grids multipolygon = ogr.Geometry(ogr.wkbMultiPolygon) for fid in fids: multipolygon.AddGeometry( ogr.CreateGeometryFromJson(subset_polys[fid])) (x_min, x_max, y_min, y_max) = multipolygon.GetEnvelope() full_reg_grid = RegularGrid(x_min, y_min, x_max, y_max, cellsize_x, cellsize_y) # Create NetCDF dimensions & coordinate variables using dimension sizes # from regular grid self.create_dims_coord_vars(len(full_reg_grid.y_coords), len(full_reg_grid.x_coords)) # Populate NetCDF coordinate variables using regular grid coordinates self.var_x[:] = full_reg_grid.x_coords[:] self.var_y[:] = full_reg_grid.y_coords[:] self.nc_file.gridSpacingLongitude = full_reg_grid.cellsize_x self.nc_file.gridSpacingLatitude = full_reg_grid.cellsize_y # Create subgrid dimension/variables self.create_subgrid_dims_vars(len(subset_polys), subset_grid_field_name) # Calculate subgrid mask ranges, populate subgrid ID for subgrid_index, fid in enumerate(fids): self.var_subgrid_id[subgrid_index] = fid if subset_grid_field_name is not None: self.var_subgrid_name[subgrid_index] = fields[fid] # Convert OGR geometry to shapely geometry subset_poly_shape = shape(json.loads(subset_polys[fid])) min_x_coord = subset_poly_shape.bounds[0] max_x_coord = subset_poly_shape.bounds[2] min_y_coord = subset_poly_shape.bounds[1] max_y_coord = subset_poly_shape.bounds[3] subgrid_x_min = None subgrid_x_max = None subgrid_y_min = None subgrid_y_max = None for i, x in enumerate(self.var_x): if x >= min_x_coord: subgrid_x_min = i break count_x = round( ((max_x_coord - min_x_coord) / full_reg_grid.cellsize_x)) for i, y in enumerate(self.var_y): if y >= min_y_coord: subgrid_y_min = i break count_y = round( ((max_y_coord - min_y_coord) / full_reg_grid.cellsize_y)) subgrid_x_max = subgrid_x_min + count_x - 1 subgrid_y_max = subgrid_y_min + count_y - 1 self.var_subgrid_x_min[subgrid_index] = subgrid_x_min self.var_subgrid_x_max[subgrid_index] = subgrid_x_max self.var_subgrid_y_min[subgrid_index] = subgrid_y_min self.var_subgrid_y_max[subgrid_index] = subgrid_y_max return full_reg_grid