;====================================================================== ; This function is a modified version of the "shapefile_mask_data" ; routine, which additionally allows you to mask data around a ; shapefile area, using a distance in meters or kilometers from ; the shapefile outline. This enables to you to get some of the grid ; points that fall just outside your data. See "delta_kilometers" ; below. ; ; This function masks a rectilinear, curvilinear, or unstructured ; data array based on either all the outlines in a shapefile, or ; based on a string variable name in a shapefile and a list of strings ; to check for, like "Water body" or (/"Ohio","Michigan"/). ; ; You have the option to return the mask array, rather than the masked ; data array. ; ; Input paramaters ; data : numeric - 1D or 2D data to mask or base mask array on ; shp_file_name[1] : string - Name of shapefile ; opt[1] : logical - Use to set options for this function. If ; set to False, then all options will be ignored. ; ; "opt" can have the following attributes: ; ; "delta_kilometers" - data point within this distance of the ; shapefile outline will be kept. ; [default 100] ; "keep" - Whether to keep the values in the given shapefile ; areas or toss them. ; [default True] ; ; "shape_var" - Name of variable on shapefile that contains the ; string names of specific areas you want to mask. ; [default is to use the whole shapefile] ; ; "shape_names" - List of string names in "shape_name" to mask against ; [no default] ; ; "return_mask" - Whether to return a mask array (0s and 1s) ; instead of the masked data. ; [default False] ; ; "debug" - Whether to print debug information. ; [default False] ; ; - If a rectilinear grid, then "data" must have coordinate arrays ; attached. ; - If a curvilinear grid, then "data" must have the special lat2d ; and lon2d attributes attached. ; - If a unstructured grid, then "data"must have the special lat1d ; and lon1d attributes attached. ;====================================================================== undef("shapefile_mask_data_mod") function shapefile_mask_data_mod(data:numeric,shp_file_name[1]:string,\ opt[1]:logical) local mask_start_time, mask_end_time,keep_true_value, keep_false_value, \ dims, rank, grid_type, lat1d, lon1d, nlatlon1d, f, segments, geometry, \ segsDims, geomDims, geom_segIndex, geom_numSegs, segs_xyzIndex, segs_numPnts,numFeatures, lat, lon, shp_mask_names, found, nf, numFeatures, \ startSegment, numSegments, seg, startPT, endPT, lon_sub, lat_sub, \ min_lat_shp, max_lat_shp, min_lon_shp, max_lon_shp begin mask_start_time = get_cpu_time() ;---Make sure we can open shapefile if(.not.isfilepresent(shp_file_name)) then print("shapefile_mask_data_mod : Error: " + shp_file_name + \ " either doesn't exist or is not a valid shapefile.") exit end if f = addfile(shp_file_name,"r") ;---Parse options and check for errors DEBUG = get_res_value_keep(opt,"debug",False) KEEP = get_res_value_keep(opt,"keep",True) RETURN_MASK = get_res_value_keep(opt,"return_mask",False) SHP_VAR = opt.and.isatt(opt,"shape_var") DELTA_KILOMETERS = get_res_value_keep(opt,"delta_kilometers",100) if(opt.and.isatt(opt,"shape_var")) then if(.not.isatt(opt,"shape_names")) then print("shapefile_mask_data_mod : Error: if you set 'shape_var' you must also set 'shape_names'") exit end if shp_var_name = opt@shape_var usr_mask_names = opt@shape_names ;---Make sure shp_var_name is on shapefile. if(isfilevar(f,shp_var_name)) then shp_mask_names = f->$shp_var_name$ else print("shapefile_mask_data_mod : Error: The given variable name to use does not exist on the given shapefile.") exit end if ;---Make sure usr_mask_names has at least one match in shp_mask_names num_found = 0 nusr_mask_names = dimsizes(usr_mask_names) do i=0,nusr_mask_names-1 if(any(usr_mask_names(i).eq.shp_mask_names)) then num_found = num_found+1 end if end do if(num_found.eq.0) then print("shapefile_mask_data_mod : Error: none of the given mask_names match the names on the shapefile.") exit end if if(num_found.lt.nusr_mask_names) then print("shapefile_mask_data_mod : warning: Only found " + num_found + \ " of the " + nusr_mask_names) print(" given mask_names on the shapefile.") end if end if if(KEEP) then keep_true_value = 1 ; 1 ==> values inside given mask areas will be kept keep_false_value = 0 else keep_true_value = 0 ; 0 ==> values inside given mask areas will be tossed keep_false_value = 1 end if ;---Determine the grid type dims = dimsizes(data) rank = dimsizes(dims) grid_type = "" if(rank.eq.2.and.\ isdimnamed(data,0).and.iscoord(data,data!0).and.\ isdimnamed(data,1).and.iscoord(data,data!1)) then lat1d = ndtooned(conform_dims(dims,data&$data!0$,0)) lon1d = ndtooned(conform_dims(dims,data&$data!1$,1)) grid_type = "rectilinear" else if(rank.eq.2.and.all(isatt(data,(/"lat2d","lon2d"/)))) then ;---Curvilinear lat1d = ndtooned(data@lat2d) lon1d = ndtooned(data@lon2d) if(product(dims).eq.dimsizes(lat1d).and.\ product(dims).eq.dimsizes(lon1d)) then grid_type = "curvilinear" end if else if(rank.eq.1.and.all(isatt(data,(/"lat1d","lon1d"/)))) then ;---Unstructured lat1d = data@lat1d lon1d = data@lon1d if(dims.eq.dimsizes(lat1d).and.\ product(dims).eq.dimsizes(lon1d)) then grid_type = "unstructured" end if end if end if end if if(grid_type.eq."") then print("shapefile_mask_data_mod: Error: not a valid rectilinear, curvilinear, or unstructured grid") exit end if nlatlon1d = dimsizes(lat1d) ;---Read data off the shapefile segments = f->segments geometry = f->geometry segsDims = dimsizes(segments) geomDims = dimsizes(geometry) ;---Read global attributes geom_segIndex = f@geom_segIndex geom_numSegs = f@geom_numSegs segs_xyzIndex = f@segs_xyzIndex segs_numPnts = f@segs_numPnts numFeatures = geomDims(0) ;---Read shapefile lat/lon lon = f->x lat = f->y ;---Use the whole shapefile min_lat_shp = min(lat) max_lat_shp = max(lat) min_lon_shp = min(lon) max_lon_shp = max(lon) ;---min/max lat/lon to use for checking the data lat/lon min_lat_chk = get_res_value(opt,"minlat",min_lat_shp) max_lat_chk = get_res_value(opt,"maxlat",max_lat_shp) min_lon_chk = get_res_value(opt,"minlon",min_lon_shp) max_lon_chk = get_res_value(opt,"maxlon",max_lon_shp) ;--lat/lon coordinates of data array min_lat_data = min(lat1d) max_lat_data = max(lat1d) min_lon_data = min(lon1d) max_lon_data = max(lon1d) if(DEBUG) then print("==================================================") print("Shapefile: " + shp_file_name) if(SHP_VAR) then print("Areas of interest: " + str_join(usr_mask_names,",")) else print("Areas of interest: the whole shapefile") end if print("min_lat_chk: " + min_lat_chk) print("max_lat_chk: " + max_lat_chk) print("min_lon_chk: " + min_lon_chk) print("max_lon_chk: " + max_lon_chk) print("min_lat_data: " + min_lat_data) print("max_lat_data: " + max_lat_data) print("min_lon_data: " + min_lon_data) print("max_lon_data: " + max_lon_data) print(dimsizes(lat1d) + " data values originally") if(.not.LOOP_CHECK) then print(nii + " data values within given shapefile areas.") end if if(keep_true_value.eq.1) then print("Will keep data values inside given shapefile areas") else print("Will toss data values inside given shapefile areas") end if end if ;---Create array to hold new data mask data_mask_1d = new(nlatlon1d,integer) data_mask_1d = keep_false_value do n=0,nlatlon1d-1 do nf=0,numFeatures-1 if(data_mask_1d(n).ne.keep_true_value.and.\ (.not.SHP_VAR.or.(SHP_VAR.and.\ any(shp_mask_names(nf).eq.usr_mask_names)))) then startSegment = geometry(nf, geom_segIndex) numSegments = geometry(nf, geom_numSegs) do seg=startSegment, startSegment+numSegments-1 startPT = segments(seg, segs_xyzIndex) endPT = startPT + segments(seg, segs_numPnts) - 1 lat_sub := lat(startPT:endPT) lon_sub := lon(startPT:endPT) nlatlon_sub = dimsizes(lon_sub) if(gc_inout(lat1d(n),lon1d(n),lat_sub,lon_sub)) then data_mask_1d(n) = keep_true_value break else gcdist := gc_latlon(lat1d(n),lon1d(n),lat_sub,lon_sub,2,4) if(any(gcdist.le.DELTA_KILOMETERS)) then data_mask_1d(n) = keep_true_value break end if end if end do end if end do end do if(DEBUG) then print("==================================================") if(KEEP) then print(num(data_mask_1d.eq.keep_true_value) + " data values kept") else print(num(data_mask_1d.ne.keep_true_value) + " data values kept") end if end if ; ; Create a 2D data array of same size as original data, ; but with appropriate values masked. Create a missing ; value if our data doesn't have one. ; if(.not.isatt(data,"_FillValue")) then data_msg = default_fillvalue(typeof(data)) else data_msg = data@_FillValue end if ;---Keep all the locations where the mask array is 1. if(RETURN_MASK) then data_mask = onedtond(data_mask_1d,dims) else data_mask = where(onedtond(data_mask_1d,dims).eq.1,data,data_msg) copy_VarMeta(data,data_mask) ; Copy all metadata if(.not.isatt(data,"_FillValue")) then data_mask@_FillValue = data_msg end if end if if(DEBUG) then ;---Print timings mask_end_time = get_cpu_time() print("shapefile_mask_data_mod: elapsed time: " + \ (mask_end_time-mask_start_time) + " CPU seconds.") print("==================================================") end if return(data_mask) end