def set_crop_params(self): """ List of <CropParameter> instances """ logging.info(' Reading crop parameters') self.crop_params = crop_parameters.read_crop_parameters( self.crop_params_path) # Filter crop parameters based on skip and test lists # Filtering could happen in read_crop_parameters() if self.crop_skip_list or self.crop_test_list: # Leave bare soil "crop" parameters # Used in initialize_crop_cycle() non_crop_list = [44] # non_crop_list = [44,45,46,55,56,57] self.crop_params = { k: v for k, v in self.crop_params.iteritems() if ((self.crop_skip_list and k not in self.crop_skip_list) or ( self.crop_test_list and k in self.crop_test_list) or ( k in non_crop_list)) }
def main(ini_path): """Interpolate Preliminary Calibration Zones to All Zones Args: ini_path (str): file path of the project INI file Returns: None """ logging.info('\nInterpolating Calibration Data from Subset Point Data') # INI path crop_et_sec = 'CROP_ET' config = util.read_ini(ini_path, section=crop_et_sec) try: project_ws = config.get(crop_et_sec, 'project_folder') except: logging.error('project_folder parameter must be set in the INI file, ' 'exiting') return False try: gis_ws = config.get(crop_et_sec, 'gis_folder') except: logging.error('gis_folder parameter must be set in the INI file, ' 'exiting') return False try: et_cells_path = config.get(crop_et_sec, 'cells_path') except: logging.error('et_cells_path parameter must be set in the INI file, ' 'exiting') return False try: calibration_ws = config.get(crop_et_sec, 'spatial_cal_folder') except: calibration_ws = os.path.join(project_ws, 'calibration') try: crop_params_name = config.get(crop_et_sec, 'crop_params_name') except: logging.error('crop_params_name parameter must be set in the INI file, ' 'exiting') return False # Sub folder names static_ws = os.path.join(project_ws, 'static') crop_params_path = os.path.join(static_ws, crop_params_name) crop_et_ws = config.get(crop_et_sec, 'crop_et_folder') bin_ws = os.path.join(crop_et_ws, 'bin') # Check input folders if not os.path.exists(calibration_ws): logging.critical('\nERROR: The calibration folder does not exist. ' '\n Run build_spatial_crop_params.py') sys.exit() # Check input folders if not os.path.isdir(project_ws): logging.critical('\nERROR: The project folder does not exist' '\n {}'.format(project_ws)) sys.exit() elif not os.path.isdir(gis_ws): logging.critical('\nERROR: The GIS folder does not exist' '\n {}'.format(gis_ws)) sys.exit() logging.info('\nGIS Workspace: {}'.format(gis_ws)) # ET cells field names cell_id_field = 'CELL_ID' cell_station_id_field = 'STATION_ID' # cell_name_field = 'CELL_NAME' # crop_acres_field = 'CROP_ACRES' # Do distance calculations in decimal degrees to match Arc script gcs_osr = osr.SpatialReference() gcs_osr.ImportFromEPSG(4326) # Read in the cell locations and values et_cells_data = defaultdict(dict) input_driver = _arcpy.get_ogr_driver(et_cells_path) input_ds = input_driver.Open(et_cells_path, 0) input_lyr = input_ds.GetLayer() input_osr = input_lyr.GetSpatialRef() # gcs_osr = input_osr.CloneGeogCS() for input_ftr in input_lyr: input_fid = input_ftr.GetFID() logging.debug(' FID: {}'.format(input_fid)) input_id = input_ftr.GetField(input_ftr.GetFieldIndex(cell_id_field)) input_geom = input_ftr.GetGeometryRef() centroid_geom = input_geom.Clone() # Do distance calculations in decimal degrees to match Arc script centroid_geom.Transform( osr.CoordinateTransformation(input_osr, gcs_osr)) centroid_geom = centroid_geom.Centroid() et_cells_data[input_id]['X'] = centroid_geom.GetX() et_cells_data[input_id]['Y'] = centroid_geom.GetY() input_ds = None # Read crop parameters using ET Demands functions/methods logging.info('\nReading default crop parameters') sys.path.append(bin_ws) import crop_parameters crop_param_dict = crop_parameters.read_crop_parameters(crop_params_path) # Get list of crops specified in ET cells crop_field_list = [ field for field in _arcpy.list_fields(et_cells_path) if re.match('CROP_\d{2}', field)] crop_number_list = [int(f.split('_')[1]) for f in crop_field_list] logging.info('Cell crop numbers: {}'.format( ', '.join(list(util.ranges(crop_number_list))))) logging.debug('Cell crop fields: {}'.format(', '.join(crop_field_list))) # Get Crop Names for each Crop in crop_number_list crop_name_list = [] logging.debug('\nBuilding crop name list') for crop_num in crop_number_list: try: crop_param = crop_param_dict[crop_num] except: continue # logging.info('{:>2d} {}'.format(crop_num, crop_param.name)) logging.debug('{}'.format(crop_param)) # Replace other characters with spaces, then remove multiple spaces crop_name = re.sub('[-"().,/~]', ' ', str(crop_param.name).lower()) crop_name = ' '.join(crop_name.strip().split()).replace(' ', '_') crop_name_list.append(crop_name) # Location of preliminary calibration .shp files (ADD AS INPUT ARG?) prelim_calibration_ws = os.path.join(calibration_ws, 'preliminary_calibration') logging.info('\nInterpolating calibration parameters') for crop_num, crop_name in zip(crop_number_list, crop_name_list): # Preliminary calibration .shp subset_cal_file = os.path.join( prelim_calibration_ws, 'crop_{0:02d}_{1}{2}').format(crop_num, crop_name, '.shp') final_cal_file = os.path.join( calibration_ws, 'crop_{0:02d}_{1}{2}').format(crop_num, crop_name, '.shp') if not _arcpy.exists(subset_cal_file): logging.info( '\nCrop No: {} Preliminary calibration file not found. ' 'skipping.'.format(crop_num)) continue logging.info('\nInterpolating Crop: {:02d}'.format(crop_num)) # Params to Interpolate # param_list = ['T30_CGDD', 'CGDD_EFC', 'CGDD_TERM', 'KillFrostC'] param_list = ['MAD_Init', 'MAD_Mid', 'T30_CGDD', 'PL_GU_Date', 'CGDD_Tbase', 'CGDD_EFC', 'CGDD_Term', 'Time_EFC', 'Time_Harv', 'KillFrostC'] # Read in the calibration locations and values subset_cal_data = defaultdict(dict) input_driver = _arcpy.get_ogr_driver(subset_cal_file) input_ds = input_driver.Open(subset_cal_file, 0) input_lyr = input_ds.GetLayer() input_osr = input_lyr.GetSpatialRef() # gcs_osr = input_osr.CloneGeogCS() for input_ftr in input_lyr: input_fid = input_ftr.GetFID() logging.debug(' FID: {}'.format(input_fid)) input_id = input_ftr.GetField(input_ftr.GetFieldIndex( cell_id_field)) input_geom = input_ftr.GetGeometryRef() centroid_geom = input_geom.Clone() # Do distance calculations in decimal degrees to match Arc script centroid_geom.Transform( osr.CoordinateTransformation(input_osr, gcs_osr)) centroid_geom = centroid_geom.Centroid() subset_cal_data[input_id]['X'] = centroid_geom.GetX() subset_cal_data[input_id]['Y'] = centroid_geom.GetY() for f in param_list: subset_cal_data[input_id][f] = input_ftr.GetField( input_ftr.GetFieldIndex(f)) input_ds = None # Compute interpolated calibration parameters final_cal_data = defaultdict(dict) for cell_id, cell_dict in et_cells_data.items(): final_cal_data[cell_id] = {} logging.debug(' {}'.format(cell_id)) # Precompute distances to all subset cells weight = {} for subset_id, subset_dict in subset_cal_data.items(): distance = math.sqrt( (subset_dict['X'] - cell_dict['X']) ** 2 + (subset_dict['Y'] - cell_dict['Y']) ** 2) try: weight[subset_id] = distance ** -2.0 except: weight[subset_id] = 0 weight_total = sum(weight.values()) # Brute force IDW using all subset cell for param in param_list: # If any weight is zero, use the values directly # There is probably a better way of flagging these d0 = [id for id, w in weight.items() if w == 0] if d0: final_cal_data[cell_id][param] = subset_cal_data[ d0[0]][param] else: final_cal_data[cell_id][param] = sum([ data[param] * weight[id] for id, data in subset_cal_data.items()]) final_cal_data[cell_id][param] /= weight_total # Overwrite values in calibration .shp with interpolated values output_ds = input_driver.Open(final_cal_file, 1) output_lyr = output_ds.GetLayer() for output_ftr in output_lyr: output_id = output_ftr.GetField( output_ftr.GetFieldIndex(cell_id_field)) for param in param_list: output_ftr.SetField( input_ftr.GetFieldIndex(param), round(final_cal_data[output_id][param], 1)) output_lyr.SetFeature(output_ftr) output_ds = None
def main(ini_path, zone_type='gridmet', overwrite_flag=False): """Interpolate Preliminary Calibration Zones to All Zones Args: ini_path (str): file path of the project INI file zone_type (str): Zone type (huc8, huc10, county, gridmet) overwrite_flag (bool): If True (default), overwrite existing files Returns: None """ logging.info('\nInterpolating Calibration Data from Subset Point Data') # INI path crop_et_sec = 'CROP_ET' config = util.read_ini(ini_path, section=crop_et_sec) try: project_ws = config.get(crop_et_sec, 'project_folder') except: logging.error('project_folder parameter must be set in the INI file, ' 'exiting') return False try: gis_ws = config.get(crop_et_sec, 'gis_folder') except: logging.error('gis_folder parameter must be set in the INI file, ' 'exiting') return False try: et_cells_path = config.get(crop_et_sec, 'cells_path') except: logging.error('et_cells_path parameter must be set in the INI file, ' 'exiting') return False try: calibration_ws = config.get(crop_et_sec, 'spatial_cal_folder') except: calibration_ws = os.path.join(project_ws, 'calibration') # Sub folder names static_ws = os.path.join(project_ws, 'static') crop_params_path = os.path.join(static_ws, 'CropParams.txt') crop_et_ws = config.get(crop_et_sec, 'crop_et_folder') bin_ws = os.path.join(crop_et_ws, 'bin') # Check input folders if not os.path.exists(calibration_ws): logging.critical('ERROR: The calibration folder does not exist. ' 'Run build_spatial_crop_params_arcpy.py, exiting') sys.exit() # Check input folders if not os.path.isdir(project_ws): logging.critical('ERROR: The project folder does not exist' '\n {}'.format(project_ws)) sys.exit() elif not os.path.isdir(gis_ws): logging.critical('ERROR: The GIS folder does not exist' '\n {}'.format(gis_ws)) sys.exit() logging.info('\nGIS Workspace: {}'.format(gis_ws)) # Check input zone type (GRIDMET ONLY FOR NOW!!!!) if zone_type == 'gridmet': station_zone_field = 'GRIDMET_ID' station_id_field = 'GRIDMET_ID' # DEADBEEF - Added for testing elif zone_type == 'huc8': station_zone_field = 'HUC8' station_id_field = 'STATION_ID' else: logging.error( '\nFUNCTION ONLY SUPPORTS GRIDMET ZONE TYPE AT THIS TIME') sys.exit() arcpy.env.overwriteOutput = overwrite_flag arcpy.CheckOutExtension('Spatial') cells_dd_path = os.path.join(gis_ws, 'ETCells_dd.shp') cells_ras_path = os.path.join(gis_ws, 'ETCells_ras.img') arcpy.Project_management(et_cells_path, cells_dd_path, arcpy.SpatialReference('WGS 1984')) temp_path = os.path.join(calibration_ws, 'temp') if not os.path.exists(temp_path): os.makedirs(temp_path) temp_pt_file = os.path.join(temp_path, 'temp_pt_file.shp') # Read crop parameters using ET Demands functions/methods logging.info('\nReading Default Crop Parameters') sys.path.append(bin_ws) import crop_parameters crop_param_dict = crop_parameters.read_crop_parameters(crop_params_path) # Get list of crops specified in ET cells crop_field_list = [ field.name for field in arcpy.ListFields(et_cells_path) if re.match('CROP_\d{2}', field.name) ] logging.debug('Cell crop fields: {}'.format(', '.join(crop_field_list))) crop_number_list = [ int(f_name.split('_')[1]) for f_name in crop_field_list ] crop_number_list = [crop_num for crop_num in crop_number_list] logging.info('Cell crop numbers: {}'.format(', '.join( list(util.ranges(crop_number_list))))) # Get Crop Names for each Crop in crop_number_list crop_name_list = [] logging.debug('\nBuilding crop name list') for crop_num in crop_number_list: try: crop_param = crop_param_dict[crop_num] except: continue # logging.info('{:>2d} {}'.format(crop_num, crop_param.name)) logging.debug('{}'.format(crop_param)) # Replace other characters with spaces, then remove multiple spaces crop_name = re.sub('[-"().,/~]', ' ', str(crop_param.name).lower()) crop_name = ' '.join(crop_name.strip().split()).replace(' ', '_') crop_name_list.append(crop_name) # Set arcpy environmental parameters arcpy.env.extent = cells_dd_path arcpy.env.outputCoordinateSystem = cells_dd_path # Convert cells_dd to cells_ras # (0.041666667 taken from GEE GRIDMET tiff) HARDCODED FOR NOW arcpy.FeatureToRaster_conversion(cells_dd_path, station_id_field, cells_ras_path, 0.041666667) # Location of preliminary calibration .shp files (ADD AS INPUT ARG?) prelim_calibration_ws = os.path.join(calibration_ws, 'preliminary_calibration') logging.info('\nInterpolating calibration parameters') for crop_num, crop_name in zip(crop_number_list, crop_name_list): # Preliminary calibration .shp subset_cal_file = os.path.join( prelim_calibration_ws, 'crop_{0:02d}_{1}{2}'.format(crop_num, crop_name, '.shp')) final_cal_file = os.path.join( calibration_ws, 'crop_{0:02d}_{1}{2}'.format(crop_num, crop_name, '.shp')) if not arcpy.Exists(subset_cal_file): logging.info( '\nCrop No: {} preliminary calibration file not found. ' 'Skipping.'.format(crop_num)) continue logging.info('\nInterpolating Crop: {:02d}'.format(crop_num)) # Polygon to Point arcpy.FeatureToPoint_management(subset_cal_file, temp_pt_file, "CENTROID") # Change Processing Extent to match final calibration file # arcpy.env.extent = cells_dd_path # arcpy.env.outputCoordinateSystem = cells_dd_path arcpy.env.snapRaster = cells_ras_path cell_size = arcpy.Raster(cells_ras_path).meanCellHeight # Params to Interpolate # Full list # param_list = ['MAD_Init', 'MAD_Mid', 'T30_CGDD', # 'PL_GU_Date', 'CGDD_Tbase', 'CGDD_EFC', # 'CGDD_Term', 'Time_EFC', 'Time_Harv', 'KillFrostC'] # Short list param_list = ['T30_CGDD', 'CGDD_EFC', 'CGDD_TERM', 'KillFrostC'] # Create final pt file based on cells raster for ExtractMultiValuesToPoints final_pt_path = os.path.join(temp_path, 'final_pt.shp') arcpy.RasterToPoint_conversion(cells_ras_path, final_pt_path, 'VALUE') # Empty list to fill with idw raster paths ras_list = [] for param in param_list: outIDW_ras = arcpy.sa.Idw(temp_pt_file, param, cell_size) outIDW_ras_path = os.path.join(temp_path, '{}{}'.format(param, '.img')) outIDW_ras.save(outIDW_ras_path) ras_list.append(outIDW_ras_path) # Extract all idw raster values to point .shp arcpy.sa.ExtractMultiValuesToPoints(final_pt_path, ras_list, 'NONE') # Read Interpolated Point Attribute table into dictionary ('GRID_CODE' is key) # https://gist.github.com/tonjadwyer/0e4162b1423c404dc2a50188c3b3c2f5 def make_attribute_dict(fc, key_field, attr_list=['*']): attdict = {} fc_field_objects = arcpy.ListFields(fc) fc_fields = [ field.name for field in fc_field_objects if field.type != 'Geometry' ] if attr_list == ['*']: valid_fields = fc_fields else: valid_fields = [ field for field in attr_list if field in fc_fields ] # Ensure that key_field is always the first field in the field list cursor_fields = [key_field ] + list(set(valid_fields) - set([key_field])) with arcpy.da.SearchCursor(fc, cursor_fields) as cursor: for row in cursor: attdict[row[0]] = dict(zip(cursor.fields, row)) return attdict cal_dict = make_attribute_dict(final_pt_path, 'GRID_CODE', param_list) # Overwrite values in calibration .shp with values from interpolated dictionary fields = ['CELL_ID'] + param_list with arcpy.da.UpdateCursor(final_cal_file, fields) as cursor: for row in cursor: for param_i, param in enumerate(param_list): row[param_i + 1] = round( cal_dict[int(row[0])][fields[param_i + 1]], 1) cursor.updateRow(row)
def main(ini_path, zone_type='huc8', area_threshold=10, dairy_cuttings=5, beef_cuttings=4, crop_str='', remove_empty_flag=True, overwrite_flag=False, cleanup_flag=False): """Build a feature class for each crop and set default crop parameters Apply the values in the CropParams.txt as defaults to every cell Args: ini_path (str): file path of the project INI file zone_type (str): Zone type (huc8, huc10, county) area_threshold (float): CDL area threshold [acres] dairy_cuttings (int): Initial number of dairy hay cuttings beef_cuttings (int): Initial number of beef hay cuttings crop_str (str): comma separate list or range of crops to compare overwrite_flag (bool): If True, overwrite existing output rasters cleanup_flag (bool): If True, remove temporary files Returns: None """ logging.info('\nCalculating ET-Demands Spatial Crop Parameters') remove_empty_flag = True # Input paths # DEADBEEF - For now, get cropET folder from INI file # This function may eventually be moved into the main cropET code config = util.read_ini(ini_path, section='CROP_ET') crop_et_sec = 'CROP_ET' project_ws = config.get(crop_et_sec, 'project_folder') gis_ws = config.get(crop_et_sec, 'gis_folder') cells_path = config.get(crop_et_sec, 'cells_path') # try: cells_path = config.get(crop_et_sec, 'cells_path') # except: cells_path = os.path.join(gis_ws, 'ETCells.shp') stations_path = config.get(crop_et_sec, 'stations_path') crop_et_ws = config.get(crop_et_sec, 'crop_et_folder') bin_ws = os.path.join(crop_et_ws, 'bin') try: template_ws = config.get(crop_et_sec, 'template_folder') except: template_ws = os.path.join(os.path.dirname(crop_et_ws), 'static') try: calibration_ws = config.get(crop_et_sec, 'spatial_cal_folder') except: calibration_ws = os.path.join(project_ws, 'calibration') # Sub folder names static_ws = os.path.join(project_ws, 'static') pmdata_ws = os.path.join(project_ws, 'pmdata') crop_params_path = os.path.join(static_ws, 'CropParams.txt') # Input units cell_elev_units = 'FEET' station_elev_units = 'FEET' # Field names cell_id_field = 'CELL_ID' cell_name_field = 'CELL_NAME' crop_acres_field = 'CROP_ACRES' dairy_cutting_field = 'Dairy_Cut' beef_cutting_field = 'Beef_Cut' # Only keep the following ET Cell fields keep_field_list = [cell_id_field, cell_name_field, 'AG_ACRES'] # keep_field_list = ['NLDAS_ID', 'CELL_ID', 'HUC8', 'COUNTY', 'AG_ACRES'] # keep_field_list = ['FIPS', 'COUNTY'] # The maximum crop name was ~50 characters string_field_len = 50 # Check input folders if not os.path.isdir(crop_et_ws): logging.error(('ERROR: The INI cropET folder ' + 'does not exist\n {}').format(crop_et_ws)) sys.exit() elif not os.path.isdir(bin_ws): logging.error('\nERROR: The Bin workspace {0} ' + 'does not exist\n'.format(bin_ws)) sys.exit() elif not os.path.isdir(project_ws): logging.error(('ERROR: The project folder ' + 'does not exist\n {}').format(project_ws)) sys.exit() elif not os.path.isdir(gis_ws): logging.error( ('ERROR: The GIS folder ' + 'does not exist\n {}').format(gis_ws)) sys.exit() if '.gdb' not in calibration_ws and not os.path.isdir(calibration_ws): os.makedirs(calibration_ws) logging.info('\nGIS Workspace: {0}'.format(gis_ws)) logging.info('Project Workspace: {0}'.format(project_ws)) logging.info('CropET Workspace: {0}'.format(crop_et_ws)) logging.info('Bin Workspace: {0}'.format(bin_ws)) logging.info('Calib. Workspace: {0}'.format(calibration_ws)) # Check input files if not os.path.isfile(crop_params_path): logging.error('\nERROR: The crop parameters file {} ' + 'does not exist\n'.format(crop_params_path)) sys.exit() elif not arcpy.Exists(cells_path): logging.error(('\nERROR: The ET Cell shapefile {} ' + 'does not exist\n').format(cells_path)) sys.exit() elif not os.path.isfile(stations_path) or not arcpy.Exists(stations_path): logging.error(('ERROR: The NLDAS station shapefile ' + 'does not exist\n %s').format(stations_path)) sys.exit() logging.debug('Crop Params Path: {0}'.format(crop_params_path)) logging.debug('ET Cells Path: {0}'.format(cells_path)) logging.debug('Stations Path: {0}'.format(stations_path)) # For now, only allow calibration parameters in separate shapefiles ext = '.shp' # # Build output geodatabase if necessary # if calibration_ws.endswith('.gdb'): # .debug('GDB Path: {0}'.format(calibration_ws)) # = '' # arcpy.Exists(calibration_ws) and overwrite_flag: # try: arcpy.Delete_management(calibration_ws) # except: pass # calibration_ws is not None and not arcpy.Exists(calibration_ws): # arcpy.CreateFileGDB_management( # os.path.dirname(calibration_ws), # os.path.basename(calibration_ws)) # else: # = '.shp' # Field Name, Property, Field Type # Property is the string of the CropParameter class property value # It will be used to access the property using getattr dairy_cutting_field = 'Dairy_Cut' beef_cutting_field = 'Beef_Cut' param_list = [ # ['Name', 'name', 'STRING'], # ['ClassNum', 'class_number', 'LONG'], # ['IsAnnual', 'is_annual', 'SHORT'], # ['IrrigFlag', 'irrigation_flag', 'SHORT'], # ['IrrigDays', 'days_after_planting_irrigation', 'LONG'], # ['Crop_FW', 'crop_fw', 'LONG'], # ['WinterCov', 'winter_surface_cover_class', 'SHORT'], # ['CropKcMax', 'kc_max', 'FLOAT'], ['MAD_Init', 'mad_initial', 'LONG'], ['MAD_Mid', 'mad_midseason', 'LONG'], # ['RootDepIni', 'rooting_depth_initial', 'FLOAT'], # ['RootDepMax', 'rooting_depth_max', 'FLOAT'], # ['EndRootGrw', 'end_of_root_growth_fraction_time', 'FLOAT'], # ['HeightInit', 'height_initial', 'FLOAT'], # ['HeightMax', 'height_max', 'FLOAT'], # ['CurveNum', 'curve_number', 'LONG'], # ['CurveName', 'curve_name', 'STRING'], # ['CurveType', 'curve_type', 'SHORT'], # ['PL_GU_Flag', 'flag_for_means_to_estimate_pl_or_gu', 'SHORT'], ['T30_CGDD', 't30_for_pl_or_gu_or_cgdd', 'FLOAT'], ['PL_GU_Date', 'date_of_pl_or_gu', 'FLOAT'], ['CGDD_Tbase', 'tbase', 'FLOAT'], ['CGDD_EFC', 'cgdd_for_efc', 'LONG'], ['CGDD_Term', 'cgdd_for_termination', 'LONG'], ['Time_EFC', 'time_for_efc', 'LONG'], ['Time_Harv', 'time_for_harvest', 'LONG'], ['KillFrostC', 'killing_frost_temperature', 'Float'], # ['InvokeStrs', 'invoke_stress', 'SHORT'], # ['CN_Coarse', 'cn_coarse_soil', 'LONG'], # ['CN_Medium', 'cn_medium_soil', 'LONG'], # ['CN_Fine', 'cn_fine_soil', 'LONG'] ] # if calibration_ws.endswith('.gdb'): # _cutting_field = 'Dairy_Cuttings' # _cutting_field = 'Beef_Cuttings' # _list = [ # # ['Name', 'name', 'STRING'], # # ['Class_Number', 'class_number', 'LONG'], # # ['Is_Annual', 'is_annual', 'SHORT'], # # ['Irrigation_Flag', 'irrigation_flag', 'SHORT'], # # ['Irrigation_Days', 'days_after_planting_irrigation', 'LONG'], # # ['Crop_FW', 'crop_fw', 'LONG'], # # ['Winter_Cover_Class', 'winter_surface_cover_class', 'SHORT'], # # ['Crop_Kc_Max', 'kc_max', 'FLOAT'], # # ['MAD_Initial', 'mad_initial', 'LONG'], # # ['MAD_Midseason', 'mad_midseason', 'LONG'], # # ['Root_Depth_Ini', 'rooting_depth_initial', 'FLOAT'], # # ['Root_Depth_Max', 'rooting_depth_max', 'FLOAT'], # # ['End_Root_Growth', 'end_of_root_growth_fraction_time', 'FLOAT'], # # ['Height_Initial', 'height_initial', 'FLOAT'], # # ['Height_Maximum', 'height_max', 'FLOAT'], # # ['Curve_Number', 'curve_number', 'LONG'], # # ['Curve_Name', 'curve_name', 'STRING'], # # ['Curve_Type', 'curve_type', 'SHORT'], # # ['PL_GU_Flag', 'flag_for_means_to_estimate_pl_or_gu', 'SHORT'], # ['T30_CGDD', 't30_for_pl_or_gu_or_cgdd', 'FLOAT'], # ['PL_GU_Date', 'date_of_pl_or_gu', 'FLOAT'], # ['CGDD_Tbase', 'tbase', 'FLOAT'], # ['CGDD_EFC', 'cgdd_for_efc', 'LONG'], # ['CGDD_Termination', 'cgdd_for_termination', 'LONG'], # ['Time_EFC', 'time_for_efc', 'LONG'], # ['Time_Harvest', 'time_for_harvest', 'LONG'], # ['Killing_Crost_C', 'killing_frost_temperature', 'Float'], # # ['Invoke_Stress', 'invoke_stress', 'SHORT'], # # ['CN_Coarse_Soil', 'cn_coarse_soil', 'LONG'], # # ['CN_Medium_Soil', 'cn_medium_soil', 'LONG'], # # ['CN_Fine_Soil', 'cn_fine_soil', 'LONG'] # ] # Allow user to subset crops and cells from INI try: crop_skip_list = sorted( list(util.parse_int_set(config.get(crop_et_sec, 'crop_skip_list')))) except: crop_skip_list = [] try: crop_test_list = sorted( list(util.parse_int_set(config.get(crop_et_sec, 'crop_test_list')))) except: crop_test_list = [] try: cell_skip_list = config.get(crop_et_sec, 'cell_skip_list').split(',') cell_skip_list = sorted([c.strip() for c in cell_skip_list]) except: cell_skip_list = [] try: cell_test_list = config.get(crop_et_sec, 'cell_test_list').split(',') cell_test_list = sorted([c.strip() for c in cell_test_list]) except: cell_test_list = [] # Overwrite INI crop list with user defined values # Could also append to the INI crop list if crop_str: try: crop_test_list = sorted(list(util.parse_int_set(crop_str))) # try: # crop_test_list = sorted(list(set( # crop_test_list + list(util.parse_int_set(crop_str))) except: pass # Don't build crop parameter files for non-crops crop_skip_list = sorted( list(set(crop_skip_list + [44, 45, 46, 55, 56, 57]))) # crop_test_list = sorted(list(set(crop_test_list + [46]))) logging.debug('\ncrop_test_list = {0}'.format(crop_test_list)) logging.debug('crop_skip_list = {0}'.format(crop_skip_list)) logging.debug('cell_test_list = {0}'.format(cell_test_list)) logging.debug('cell_test_list = {0}'.format(cell_test_list)) # Read crop parameters using ET Demands functions/methods logging.info('\nReading Default Crop Parameters') sys.path.append(bin_ws) import crop_parameters crop_param_dict = crop_parameters.read_crop_parameters(crop_params_path) # arcpy.CheckOutExtension('Spatial') # arcpy.env.pyramid = 'NONE 0' arcpy.env.overwriteOutput = overwrite_flag arcpy.env.parallelProcessingFactor = 8 # Get list of crops specified in ET cells # Currently this may only be crops with CDL acreage crop_field_list = [ field.name for field in arcpy.ListFields(cells_path) if re.match('CROP_\d{2}', field.name) ] logging.debug('Cell crop fields: {}'.format(', '.join(crop_field_list))) crop_number_list = [ int(f_name.split('_')[1]) for f_name in crop_field_list ] crop_number_list = [ crop_num for crop_num in crop_number_list if not ((crop_test_list and crop_num not in crop_test_list) or (crop_skip_list and crop_num in crop_skip_list)) ] logging.info('Cell crop numbers: {}'.format(', '.join( list(util.ranges(crop_number_list))))) # Get crop acreages for each cell crop_acreage_dict = defaultdict(dict) field_list = [cell_id_field] + crop_field_list with arcpy.da.SearchCursor(cells_path, field_list) as cursor: for row in cursor: for i, crop_num in enumerate(crop_number_list): crop_acreage_dict[crop_num][row[0]] = row[i + 1] # Make an empty template crop feature class logging.info('') crop_template_path = os.path.join(calibration_ws, 'crop_00_template' + ext) if overwrite_flag and arcpy.Exists(crop_template_path): logging.debug('Overwriting template crop feature class') arcpy.Delete_management(crop_template_path) if arcpy.Exists(crop_template_path): logging.info('Template crop feature class already exists, skipping') else: logging.info('Building template crop feature class') arcpy.CopyFeatures_management(cells_path, crop_template_path) # Remove unneeded et cell fields for field in arcpy.ListFields(crop_template_path): if (field.name not in keep_field_list and field.editable and not field.required): logging.debug(' Delete field: {0}'.format(field.name)) arcpy.DeleteField_management(crop_template_path, field.name) field_list = [f.name for f in arcpy.ListFields(crop_template_path)] # Add crop acreage field if crop_acres_field not in field_list: logging.debug(' Add field: {0}'.format(crop_acres_field)) arcpy.AddField_management(crop_template_path, crop_acres_field, 'Float') arcpy.CalculateField_management(crop_template_path, crop_acres_field, '0', 'PYTHON_9.3') # Add crop parameter fields if necessary for param_field, param_method, param_type in param_list: logging.debug(' Add field: {0}'.format(param_field)) if param_field not in field_list: arcpy.AddField_management(crop_template_path, param_field, param_type) # if dairy_cutting_field not in field_list: # .debug(' Add field: {0}'.format(dairy_cutting_field)) # .AddField_management(crop_template_path, dairy_cutting_field, 'Short') # .CalculateField_management( # crop_template_path, dairy_cutting_field, dairy_cuttings, 'PYTHON') # if beef_cutting_field not in field_list: # .debug(' Add field: {0}'.format(beef_cutting_field)) # .AddField_management(crop_template_path, beef_cutting_field, 'Short') # .CalculateField_management( # crop_template_path, beef_cutting_field, beef_cuttings, 'PYTHON') # Add an empty/zero crop field for the field mappings below # if len(arcpy.ListFields(cells_path, 'CROP_EMPTY')) == 0: # .AddField_management(cells_path, 'CROP_EMPTY', 'Float') # .CalculateField_management( # cells_path, 'CROP_EMPTY', '0', 'PYTHON_9.3') # Process each crop logging.info('\nBuild crop feature classes') for crop_num in crop_number_list: try: crop_param = crop_param_dict[crop_num] except: continue logging.info('{0:>2d} {1}'.format(crop_num, crop_param)) # Replace other characters with spaces, then remove multiple spaces crop_name = re.sub('[-"().,/~]', ' ', str(crop_param.name).lower()) crop_name = ' '.join(crop_name.strip().split()).replace(' ', '_') crop_path = os.path.join( calibration_ws, 'crop_{0:02d}_{1}{2}'.format(crop_num, crop_name, ext)) crop_field = 'CROP_{0:02d}'.format(crop_num) # Skip if all zone crop areas are below threshold if all( [v < area_threshold for v in crop_acreage_dict[crop_num].values()]): logging.info(' All crop acreaeges below threshold, skipping crop') continue # Remove existing shapefiles if necessary if overwrite_flag and arcpy.Exists(crop_path): logging.debug(' Overwriting: {}'.format( os.path.basename(crop_path))) arcpy.Delete_management(crop_path) # Don't check skip list until after existing files are removed # if ((crop_test_list and crop_num not in crop_test_list) or # _skip_list and crop_num in crop_skip_list)): # .debug(' Skipping') # # Copy ET cells for each crop if needed if arcpy.Exists(crop_path): logging.debug(' Shapefile already exists, skipping') continue else: # logging.debug(' {0}'.format(crop_path)) arcpy.Copy_management(crop_template_path, crop_path) # Remove extra fields # for field in arcpy.ListFields(crop_path): # field.name not in keep_field_list: # # logging.debug(' {0}'.format(field.name)) # arcpy.DeleteField_management(crop_path, field.name) # Add alfalfa cutting field if crop_num in [1, 2, 3, 4]: if len(arcpy.ListFields(crop_path, dairy_cutting_field)) == 0: logging.debug(' Add field: {0}'.format(dairy_cutting_field)) arcpy.AddField_management(crop_path, dairy_cutting_field, 'Short') arcpy.CalculateField_management(crop_path, dairy_cutting_field, dairy_cuttings, 'PYTHON') if len(arcpy.ListFields(crop_path, beef_cutting_field)) == 0: logging.debug(' Add field: {0}'.format(beef_cutting_field)) arcpy.AddField_management(crop_path, beef_cutting_field, 'Short') arcpy.CalculateField_management(crop_path, beef_cutting_field, beef_cuttings, 'PYTHON') # Write default crop parameters to file field_list = [p[0] for p in param_list] + [cell_id_field, crop_acres_field] with arcpy.da.UpdateCursor(crop_path, field_list) as cursor: for row in cursor: # Skip and/or remove zones without crop acreage if crop_acreage_dict[crop_num][row[-2]] < area_threshold: if remove_empty_flag: cursor.deleteRow() continue # Write parameter values for i, (param_field, param_method, param_type) in enumerate(param_list): row[i] = getattr(crop_param, param_method) # Write crop acreage row[-1] = crop_acreage_dict[crop_num][row[-2]] cursor.updateRow(row)
def main(ini_path, area_threshold=10, dairy_cuttings=5, beef_cuttings=4, crop_str='', overwrite_flag=False): """Build a feature class for each crop and set default crop parameters Apply the values in the CropParams.txt as defaults to every cell Parameters ---------- ini_path : str File path of the parameter INI file. area_threshold : float CDL area threshold [acres]. dairy_cuttings : int Initial number of dairy hay cuttings. beef_cuttings : int Initial number of beef hay cuttings. crop_str : str Comma separated list or range of crops to compare (no spaces, ex: 1,2,4-6) overwrite_flag : bool If True, overwrite existing output rasters. Returns ------- None """ logging.info('\nCalculating ET-Demands Spatial Crop Parameters') remove_empty_flag = True # Input paths # DEADBEEF - For now, get cropET folder from INI file # This function may eventually be moved into the main cropET code crop_et_sec = 'CROP_ET' config = util.read_ini(ini_path, section=crop_et_sec) try: project_ws = config.get(crop_et_sec, 'project_folder') except: logging.error('project_folder parameter must be set in the INI file, ' 'exiting') return False try: gis_ws = config.get(crop_et_sec, 'gis_folder') except: logging.error('gis_folder parameter must be set in the INI file, ' 'exiting') return False try: cells_path = config.get(crop_et_sec, 'cells_path') except: # cells_path = os.path.join(gis_ws, 'ETCells.shp') logging.error('et_cells_path parameter must be set in the INI file, ' 'exiting') return False try: stations_path = config.get(crop_et_sec, 'stations_path') except: logging.error('stations_path parameter must be set in the INI file, ' 'exiting') return False try: crop_params_name = config.get(crop_et_sec, 'crop_params_name') except: logging.error( 'crop_params_name parameter must be set in the INI file, ' 'exiting') return False crop_et_ws = config.get(crop_et_sec, 'crop_et_folder') bin_ws = os.path.join(crop_et_ws, 'bin') try: calibration_ws = config.get(crop_et_sec, 'spatial_cal_folder') except: calibration_ws = os.path.join(project_ws, 'calibration') # Sub folder names static_ws = os.path.join(project_ws, 'static') crop_params_path = os.path.join(static_ws, crop_params_name) # ET cells field names cell_id_field = 'CELL_ID' cell_name_field = 'CELL_NAME' crop_acres_field = 'CROP_ACRES' # Only keep the following ET Cell fields keep_field_list = [cell_id_field, cell_name_field, 'AG_ACRES'] # keep_field_list = ['CELL_ID', 'STATION_ID', 'HUC8', 'HUC10', 'GRIDMET_ID', # 'COUNTYNAME', 'AG_ACRES'] # keep_field_list = ['FIPS', 'COUNTYNAME'] # Check input folders if not os.path.isdir(crop_et_ws): logging.error('\nERROR: The INI cropET folder does not exist' '\n {}'.format(crop_et_ws)) sys.exit() elif not os.path.isdir(bin_ws): logging.error('\nERROR: The bin workspace does not exist' '\n {}'.format(bin_ws)) sys.exit() elif not os.path.isdir(project_ws): logging.error('\nERROR: The project folder does not exist' '\n {}'.format(project_ws)) sys.exit() elif not os.path.isdir(gis_ws): logging.error('\nERROR: The GIS folder does not exist' '\n {}'.format(gis_ws)) sys.exit() if '.gdb' not in calibration_ws and not os.path.isdir(calibration_ws): os.makedirs(calibration_ws) logging.info('\nGIS Workspace: {}'.format(gis_ws)) logging.info('Project Workspace: {}'.format(project_ws)) logging.info('CropET Workspace: {}'.format(crop_et_ws)) logging.info('Bin Workspace: {}'.format(bin_ws)) logging.info('Calib. Workspace: {}'.format(calibration_ws)) # Check input files if not os.path.isfile(crop_params_path): logging.error('\nERROR: The crop parameters file does not exist' '\n {}'.format(crop_params_path)) sys.exit() elif not os.path.isfile(cells_path): logging.error('\nERROR: The ET Cell shapefile does not exist' '\n {}'.format(cells_path)) sys.exit() elif not os.path.isfile(stations_path): logging.error('\nERROR: The weather station shapefile does not exist' '\n {}'.format(stations_path)) sys.exit() logging.debug('Crop Params Path: {}'.format(crop_params_path)) logging.debug('ET Cells Path: {}'.format(cells_path)) logging.debug('Stations Path: {}'.format(stations_path)) # For now, only allow calibration parameters in separate shapefiles ext = '.shp' # # Build output geodatabase if necessary # if calibration_ws.endswith('.gdb'): # logging.debug('GDB Path: {}'.format(calibration_ws)) # ext = '' # _arcpy.exists(calibration_ws) and overwrite_flag: # try: _arcpy.delete(calibration_ws) # except: pass # if calibration_ws is not None and not _arcpy.exists(calibration_ws): # arcpy.CreateFileGDB_management( # os.path.dirname(calibration_ws), # os.path.basename(calibration_ws)) # else: # ext = '.shp' # Field Name, Property, Field Type # Property is the string of the CropParameter class property value # It will be used to access the property using getattr dairy_cutting_field = 'Dairy_Cut' beef_cutting_field = 'Beef_Cut' param_list = [ # ['Name', 'name', ogr.OFTString], # ['ClassNum', 'class_number', ogr.OFTInteger], # ['IsAnnual', 'is_annual', 'SHORT'], # ['IrrigFlag', 'irrigation_flag', 'SHORT'], # ['IrrigDays', 'days_after_planting_irrigation', ogr.OFTInteger], # ['Crop_FW', 'crop_fw', ogr.OFTInteger], # ['WinterCov', 'winter_surface_cover_class', 'SHORT'], # ['CropKcMax', 'kc_max', ogr.OFTReal], ['MAD_Init', 'mad_initial', ogr.OFTInteger], ['MAD_Mid', 'mad_midseason', ogr.OFTInteger], # ['RootDepIni', 'rooting_depth_initial', ogr.OFTReal], # ['RootDepMax', 'rooting_depth_max', ogr.OFTReal], # ['EndRootGrw', 'end_of_root_growth_fraction_time', ogr.OFTReal], # ['HeightInit', 'height_initial', ogr.OFTReal], # ['HeightMax', 'height_max', ogr.OFTReal], # ['CurveNum', 'curve_number', ogr.OFTInteger], # ['CurveName', 'curve_name', ogr.OFTString], # ['CurveType', 'curve_type', 'SHORT'], # ['PL_GU_Flag', 'flag_for_means_to_estimate_pl_or_gu', 'SHORT'], ['T30_CGDD', 't30_for_pl_or_gu_or_cgdd', ogr.OFTReal], ['PL_GU_Date', 'date_of_pl_or_gu', ogr.OFTReal], ['CGDD_Tbase', 'tbase', ogr.OFTReal], ['CGDD_EFC', 'cgdd_for_efc', ogr.OFTInteger], ['CGDD_Term', 'cgdd_for_termination', ogr.OFTInteger], ['Time_EFC', 'time_for_efc', ogr.OFTInteger], ['Time_Harv', 'time_for_harvest', ogr.OFTInteger], ['KillFrostC', 'killing_frost_temperature', ogr.OFTReal], # ['InvokeStrs', 'invoke_stress', 'SHORT'], # ['CN_Coarse', 'cn_coarse_soil', ogr.OFTInteger], # ['CN_Medium', 'cn_medium_soil', ogr.OFTInteger], # ['CN_Fine', 'cn_fine_soil', ogr.OFTInteger] ] # if calibration_ws.endswith('.gdb'): # dairy_cutting_field = 'Dairy_Cuttings' # beef_cutting_field = 'Beef_Cuttings' # param_list = [ # # ['Name', 'name', 'STRING'], # # ['Class_Number', 'class_number', ogr.OFTInteger], # # ['Is_Annual', 'is_annual', 'SHORT'], # # ['Irrigation_Flag', 'irrigation_flag', 'SHORT'], # # ['Irrigation_Days', 'days_after_planting_irrigation', ogr.OFTInteger], # # ['Crop_FW', 'crop_fw', ogr.OFTInteger], # # ['Winter_Cover_Class', 'winter_surface_cover_class', 'SHORT'], # # ['Crop_Kc_Max', 'kc_max', ogr.OFTReal], # # ['MAD_Initial', 'mad_initial', ogr.OFTInteger], # # ['MAD_Midseason', 'mad_midseason', ogr.OFTInteger], # # ['Root_Depth_Ini', 'rooting_depth_initial', ogr.OFTReal], # # ['Root_Depth_Max', 'rooting_depth_max', ogr.OFTReal], # # ['End_Root_Growth', 'end_of_root_growth_fraction_time', ogr.OFTReal], # # ['Height_Initial', 'height_initial', ogr.OFTReal], # # ['Height_Maximum', 'height_max', ogr.OFTReal], # # ['Curve_Number', 'curve_number', ogr.OFTInteger], # # ['Curve_Name', 'curve_name', ogr.OFTString], # # ['Curve_Type', 'curve_type', 'SHORT'], # # ['PL_GU_Flag', 'flag_for_means_to_estimate_pl_or_gu', 'SHORT'], # ['T30_CGDD', 't30_for_pl_or_gu_or_cgdd', ogr.OFTReal], # ['PL_GU_Date', 'date_of_pl_or_gu', ogr.OFTReal], # ['CGDD_Tbase', 'tbase', ogr.OFTReal], # ['CGDD_EFC', 'cgdd_for_efc', ogr.OFTInteger], # ['CGDD_Termination', 'cgdd_for_termination', ogr.OFTInteger], # ['Time_EFC', 'time_for_efc', ogr.OFTInteger], # ['Time_Harvest', 'time_for_harvest', ogr.OFTInteger], # ['Killing_Crost_C', 'killing_frost_temperature', ogr.OFTReal], # # ['Invoke_Stress', 'invoke_stress', 'SHORT'], # # ['CN_Coarse_Soil', 'cn_coarse_soil', ogr.OFTInteger], # # ['CN_Medium_Soil', 'cn_medium_soil', ogr.OFTInteger], # # ['CN_Fine_Soil', 'cn_fine_soil', ogr.OFTInteger] # ] crop_add_list = [] if crop_str: try: crop_add_list = sorted(list(util.parse_int_set(crop_str))) # try: # crop_test_list = sorted(list(set( # crop_test_list + list(util.parse_int_set(crop_str))) except: pass # Don't build crop parameter files for non-crops crop_skip_list = sorted(list(set([44, 45, 46, 55, 56, 57]))) # crop_test_list = sorted(list(set(crop_test_list + [46]))) logging.info('\ncrop_add_list = {}'.format(crop_add_list)) # Read crop parameters using ET Demands functions/methods logging.info('\nReading default crop parameters') sys.path.append(bin_ws) import crop_parameters crop_param_dict = crop_parameters.read_crop_parameters(crop_params_path) # Get list of crops specified in ET cells # Currently this may only be crops with CDL acreage crop_field_list = sorted([ field for field in _arcpy.list_fields(cells_path) if re.match('CROP_\d{2}', field) ]) crop_number_list = [int(f.split('_')[-1]) for f in crop_field_list] logging.info('Cell crop numbers: {}'.format(', '.join( list(util.ranges(crop_number_list))))) logging.debug('Cell crop fields: {}'.format(', '.join(crop_field_list))) # Get crop acreages for each cell # DEADBEEF - Does this dict need to be keyed by crop then cell_id? # Could it be changed to cell_id, crop or fid, crop to make it easier to # write to the shapefile using update_cursor()? crop_acreage_dict = defaultdict(dict) field_list = [cell_id_field] + crop_field_list for fid, row in _arcpy.search_cursor(cells_path, field_list).items(): for crop_field, crop_num in zip(crop_field_list, crop_number_list): if crop_skip_list and crop_num in crop_skip_list: continue elif crop_num in crop_add_list: crop_acreage_dict[crop_num][row[cell_id_field]] = 0 elif row[crop_field]: crop_acreage_dict[crop_num][ row[cell_id_field]] = row[crop_field] else: crop_acreage_dict[crop_num][row[cell_id_field]] = 0 crop_number_list = sorted(list(set(crop_number_list) | set(crop_add_list))) # Make an empty template crop feature class logging.info('') crop_template_path = os.path.join(calibration_ws, 'crop_00_template' + ext) if overwrite_flag and _arcpy.exists(crop_template_path): logging.debug('Overwriting template crop feature class') _arcpy.delete(crop_template_path) if _arcpy.exists(crop_template_path): logging.info('Template crop feature class already exists, skipping') else: logging.info('Building template crop feature class') _arcpy.copy(cells_path, crop_template_path) # Remove unneeded et cell fields for field in _arcpy.list_fields(crop_template_path): # if (field not in keep_field_list and # field.editable and not field.required): if field not in keep_field_list: logging.debug(' Delete field: {}'.format(field)) _arcpy.delete_field(crop_template_path, field) field_list = _arcpy.list_fields(crop_template_path) # Add crop acreage field if crop_acres_field not in field_list: logging.debug(' Add field: {}'.format(crop_acres_field)) _arcpy.add_field(crop_template_path, crop_acres_field, ogr.OFTReal) _arcpy.calculate_field(crop_template_path, crop_acres_field, '0') # Add crop parameter fields if necessary for param_field, param_method, param_type in param_list: logging.debug(' Add field: {}'.format(param_field)) if param_field not in field_list: _arcpy.add_field(crop_template_path, param_field, param_type) # if dairy_cutting_field not in field_list: # logging.debug(' Add field: {}'.format(dairy_cutting_field)) # _arcpy.add_field(crop_template_path, dairy_cutting_field, # ogr.OFTInteger) # _arcpy.calculate_field(crop_template_path, dairy_cutting_field, # dairy_cuttings) # if beef_cutting_field not in field_list: # logging.debug(' Add field: {}'.format(beef_cutting_field)) # _arcpy.add_field(crop_template_path, beef_cutting_field, # ogr.OFTInteger) # _arcpy.calculate_field(crop_template_path, beef_cutting_field, # beef_cuttings) # Add an empty/zero crop field for the field mappings below # if 'CROP_EMPTY' not in _arcpy.list_fields(cells_path): # _arcpy.add_field(cells_path, 'CROP_EMPTY', ogr.OFTReal) # _arcpy.calculate_field(cells_path, 'CROP_EMPTY', '0') # Process each crop logging.info('\nBuilding crop feature classes') for crop_num in crop_number_list: try: crop_param = crop_param_dict[crop_num] except: continue logging.info('{:>2d} {}'.format(crop_num, crop_param.name)) logging.debug('{}'.format(crop_param)) # Replace other characters with spaces, then remove multiple spaces crop_name = re.sub('[-"().,/~]', ' ', str(crop_param.name).lower()) crop_name = ' '.join(crop_name.strip().split()).replace(' ', '_') crop_path = os.path.join( calibration_ws, 'crop_{0:02d}_{1}{2}'.format(crop_num, crop_name, ext)) # crop_field = 'CROP_{:02d}'.format(crop_num) # Don't check crops in add list if crop_num in crop_add_list: pass # Skip if all zone crop areas are below threshold elif all( [v < area_threshold for v in crop_acreage_dict[crop_num].values()]): logging.info('** Skipping Crop {}, All crop acreages below' ' threshold'.format(crop_num)) continue # Remove existing shapefiles if necessary if overwrite_flag and _arcpy.exists(crop_path): logging.debug(' Overwriting: {}'.format( os.path.basename(crop_path))) _arcpy.delete(crop_path) # Don't check skip list until after existing files are removed # if ((crop_test_list and crop_num not in crop_test_list) or # _skip_list and crop_num in crop_skip_list)): # .debug(' Skipping') # Copy ET cells for each crop if needed if _arcpy.exists(crop_path): logging.debug(' Shapefile already exists, skipping') continue else: # logging.debug(' {}'.format(crop_path)) _arcpy.copy(crop_template_path, crop_path) # Remove extra fields # for field in _arcpy.list_fields(crop_path): # if field not in keep_field_list: # # logging.debug(' {}'.format(field)) # _arcpy.delete_field(crop_path, field) # Add alfalfa cutting field if crop_num in [1, 2, 3, 4]: if dairy_cutting_field not in _arcpy.list_fields(crop_path): logging.debug(' Add field: {}'.format(dairy_cutting_field)) _arcpy.add_field(crop_path, dairy_cutting_field, ogr.OFTInteger) _arcpy.calculate_field(crop_path, dairy_cutting_field, str(dairy_cuttings)) if beef_cutting_field not in _arcpy.list_fields(crop_path): logging.debug(' Add field: {}'.format(beef_cutting_field)) _arcpy.add_field(crop_path, beef_cutting_field, ogr.OFTInteger) _arcpy.calculate_field(crop_path, beef_cutting_field, str(beef_cuttings)) # Write default crop parameters to file # Note: Couldn't use _arcpy.udpate_cursor directly since the # crop_acreage_dict is keyed by crop_num then by cell_id (not FID first) input_driver = _arcpy.get_ogr_driver(crop_path) input_ds = input_driver.Open(crop_path, 1) input_lyr = input_ds.GetLayer() for input_ftr in input_lyr: cell_id = input_ftr.GetField( input_ftr.GetFieldIndex(cell_id_field)) # Don't remove zero acreage crops if in add list if crop_num in crop_add_list: pass # Skip and/or remove zones without crop acreage elif crop_acreage_dict[crop_num][cell_id] < area_threshold: if remove_empty_flag: input_lyr.DeleteFeature(input_ftr.GetFID()) continue # Write parameter values for param_field, param_method, param_type in param_list: input_ftr.SetField(input_ftr.GetFieldIndex(param_field), getattr(crop_param, param_method)) # Write crop acreage if crop_num not in crop_add_list: input_ftr.SetField(input_ftr.GetFieldIndex(crop_acres_field), crop_acreage_dict[crop_num][cell_id]) input_lyr.SetFeature(input_ftr) input_ds = None