def test_resampling_to_numpy(self): region = Region() region.ewres = 0.1 region.nsres = 0.1 region.adjust() region.set_raster_region() a = raster2numpy(self.name) self.assertEqual(len(a), 400) region.ewres = 1 region.nsres = 1 region.adjust() region.set_raster_region() a = raster2numpy(self.name) self.assertEqual(len(a), 40) region.ewres = 5 region.nsres = 5 region.adjust() region.set_raster_region() a = raster2numpy(self.name) self.assertEqual(len(a), 8)
def setUpClass(cls): """Create test raster map and region""" cls.use_temp_region() cls.runModule("g.region", n=40, s=0, e=60, w=0, res=1) cls.runModule("r.mapcalc", expression="%s = float(row() + (10.0 * col()))"%(cls.name), overwrite=True) cls.numpy_obj = raster2numpy(cls.name)
def setUpClass(cls): """Create test raster map and region""" cls.use_temp_region() cls.runModule("g.region", n=40, s=0, e=60, w=0, res=1) cls.runModule("r.mapcalc", expression="%s = float(row() + (10.0 * col()))" % (cls.name), overwrite=True) cls.numpy_obj = raster2numpy(cls.name)
def _rst2np(self, raster): """ Convert raster data into numpy array :param raster: raster name :return: numpy array """ # raster is read from current computation region # g.region cannot be called here, # see https://github.com/storm-fsv-cvut/smoderp2d/issues/42 Region().from_rast(raster) return raster2numpy(raster)
def calcPhotoPolRast(myraster, max_distance, reg, elevation, lights, photopollution): map2d_1 = garray.array() map2d_1.fill(np.nan) elev = raster.raster2numpy(elevation) elev = elev <> None xcoords, ycoords = elev.nonzero() counter = 1 for pixel in zip(list(xcoords), list(ycoords)): i = pixel[0] j = pixel[1] indexForPixel = calcPhotoPol(i, j, reg=reg, elevation=elevation, lights=lights, max_distance=max_distance, obs_elevation=OBS_ELEVATION) map2d_1[i, j] = indexForPixel perc = counter * 100 / len(xcoords) print 'pixel:{}/{} - {}%'.format(counter, len(xcoords), perc) counter += 1 #write array object to grass raster map map2d_1.write( mapname=photopollution, overwrite=True) #SOS: region should be the same as generated map2d_1 return ('map2d_1')
def _get_mat_stream_seg(self, stream): """Get numpy array of integers detecting whether there is a stream on corresponding pixel of raster (number equal or greater than 1000 in return numpy array) or not (number 0 in return numpy array). :param stream: Polyline with stream in the area. :return mat_stream_seg: Numpy array """ Module('g.region', vector=stream, res=self.spix) Module('v.to.rast', input=stream, type='line', output='stream_rst', use='cat') # TODO: probably not needed, see relevant code in arcgis provider Module( 'r.mapcalc', # expression='{o} = if(isnull({i}), 1000, {i})'.format( expression='{o} = {i}'.format(o='stream_seg', i='stream_rst')) # TODO: probably not needed # Module('g.region', # w=self.ll_corner[0], # s=self.ll_corner[1], # cols=self.cols, # rows=self.rows # ) mat_stream_seg = raster2numpy('stream_seg') mat_stream_seg = mat_stream_seg.astype('int16') # TODO: ? no_of_streams = mat_stream_seg.max() self._get_mat_stream_seg_(mat_stream_seg, no_of_streams) return mat_stream_seg
def main(): try: import pysptools.eea as eea except ImportError: gs.fatal(_("Cannot import pysptools \ (https://pypi.python.org/pypi/pysptools) library." " Please install it (pip install pysptools)" " or ensure that it is on path" " (use PYTHONPATH variable).")) try: # sklearn is a dependency of used pysptools functionality import sklearn except ImportError: gs.fatal(_("Cannot import sklearn \ (https://pypi.python.org/pypi/scikit-learn) library." " Please install it (pip install scikit-learn)" " or ensure that it is on path" " (use PYTHONPATH variable).")) try: from cvxopt import solvers, matrix except ImportError: gs.fatal(_("Cannot import cvxopt \ (https://pypi.python.org/pypi/cvxopt) library." " Please install it (pip install cvxopt)" " or ensure that it is on path" " (use PYTHONPATH variable).")) # Parse input options input = options['input'] output = options['output'] prefix = options['prefix'] endmember_n = int(options['endmember_n']) endmembers = options['endmembers'] if options['maxit']: maxit = options['maxit'] else: maxit = 0 extraction_method = options['extraction_method'] unmixing_method = options['unmixing_method'] atgp_init = True if not flags['n'] else False # List maps in imagery group try: maps = gs.read_command('i.group', flags='g', group=input, quiet=True).rstrip('\n').split('\n') except: pass # Validate input # q and maxit can be None according to manual, but does not work in current pysptools version if endmember_n <= 0: gs.fatal('Number of endmembers has to be > 0') """if (extraction_method == 'PPI' or extraction_method == 'NFINDR'): gs.fatal('Extraction methods PPI and NFINDR require endmember_n >= 2') endmember_n = None""" if maxit <= 0: maxit = 3 * len(maps) if endmember_n > len(maps) + 1: gs.warning('More endmembers ({}) requested than bands in \ input imagery group ({})'.format(endmember_n, len(maps))) if extraction_method != 'PPI': gs.fatal('Only PPI method can extract more endmembers than number \ of bands in the imagery group') if not atgp_init and extraction_method != 'NFINDR': gs.verbose('ATGP is only taken into account in \ NFINDR extraction method...') # Get metainformation from input bands band_types = {} img = None n = 0 gs.verbose('Reading imagery group...') for m in maps: map = m.split('@') # Build numpy stack from imagery group raster = r.raster2numpy(map[0], mapset=map[1]) if raster == np.float64: raster = float32(raster) gs.warning('{} is of type Float64.\ Float64 is currently not supported.\ Reducing precision to Float32'.format(raster)) # Determine map type band_types[map[0]] = get_rastertype(raster) # Create cube and mask from GRASS internal NoData value if n == 0: img = raster # Create mask from GRASS internal NoData value mask = mask_rasternd(raster) else: img = np.dstack((img, raster)) mask = np.logical_and((mask_rasternd(raster)), mask) n = n + 1 # Read a mask if present and give waringing if not # Note that otherwise NoData is read as values gs.verbose('Checking for MASK...') try: MASK = r.raster2numpy('MASK', mapset=getenv('MAPSET')) == 1 mask = np.logical_and(MASK, mask) MASK = None except: pass if extraction_method == 'NFINDR': # Extract endmembers from valid pixels using NFINDR function from pysptools gs.verbose('Extracting endmembers using NFINDR...') nfindr = eea.NFINDR() E = nfindr.extract(img, endmember_n, maxit=maxit, normalize=False, ATGP_init=atgp_init, mask=mask) elif extraction_method == 'PPI': # Extract endmembers from valid pixels using PPI function from pysptools gs.verbose('Extracting endmembers using PPI...') ppi = eea.PPI() E = ppi.extract(img, endmember_n, numSkewers=10000, normalize=False, mask=mask) elif extraction_method == 'FIPPI': # Extract endmembers from valid pixels using FIPPI function from pysptools gs.verbose('Extracting endmembers using FIPPI...') fippi = eea.FIPPI() # q and maxit can be None according to manual, but does not work """if not maxit and not endmember_n: E = fippi.extract(img, q=None, normalize=False, mask=mask) if not maxit: E = fippi.extract(img, q=endmember_n, normalize=False, mask=mask) if not endmember_n: E = fippi.extract(img, q=int(), maxit=maxit, normalize=False, mask=mask) else: E = fippi.extract(img, q=endmember_n, maxit=maxit, normalize=False, mask=mask)""" E = fippi.extract(img, q=endmember_n, maxit=maxit, normalize=False, mask=mask) # Write output file in format required for i.spec.unmix addon if output: gs.verbose('Writing spectra file...') n = 0 with open(output, 'w') as o: o.write('# Channels: {}\n'.format('\t'.join(band_types.keys()))) o.write('# Wrote {} spectra line wise.\n#\n'.format(endmember_n)) o.write('Matrix: {0} by {1}\n'.format(endmember_n, len(maps))) for e in E: o.write('row{0}: {1}\n'.format(n, '\t'.join([str(i) for i in e]))) n = n + 1 # Write vector map with endmember information if requested if endmembers: gs.verbose('Writing vector map with endmembers...') from grass.pygrass import utils as u from grass.pygrass.gis.region import Region from grass.pygrass.vector import Vector from grass.pygrass.vector import VectorTopo from grass.pygrass.vector.geometry import Point # Build attribute table # Deinfe columns for attribute table cols = [(u'cat', 'INTEGER PRIMARY KEY')] for b in band_types.keys(): cols.append((b.replace('.','_'), band_types[b])) # Get region information reg = Region() # Create vector map new = Vector(endmembers) new.open('w', tab_name=endmembers, tab_cols=cols) cat = 1 for e in E: # Get indices idx = np.where((img[:,:]==e).all(-1)) # Numpy array is ordered rows, columns (y,x) if len(idx[0]) == 0 or len(idx[1]) == 0: gs.warning('Could not compute coordinated for endmember {}. \ Please consider rescaling your data to integer'.format(cat)) cat = cat + 1 continue coords = u.pixel2coor((idx[1][0], idx[0][0]), reg) point = Point(coords[1] + reg.ewres / 2.0, coords[0] - reg.nsres / 2.0) # Get attributes n = 0 attr = [] for b in band_types.keys(): if band_types[b] == u'INTEGER': attr.append(int(e[n])) else: attr.append(float(e[n])) n = n + 1 # Write geometry with attributes new.write(point, cat=cat, attrs=tuple(attr)) cat = cat + 1 # Close vector map new.table.conn.commit() new.close(build=True) if prefix: # Run spectral unmixing import pysptools.abundance_maps as amaps if unmixing_method == 'FCLS': fcls = amaps.FCLS() result = fcls.map(img, E, normalize=False, mask=mask) elif unmixing_method == 'NNLS': nnls = amaps.NNLS() result = nnls.map(img, E, normalize=False, mask=mask) elif unmixing_method == 'UCLS': ucls = amaps.UCLS() result = ucls.map(img, E, normalize=False, mask=mask) # Write results for l in range(endmember_n): rastname = '{0}_{1}'.format(prefix, l + 1) r.numpy2raster(result[:,:,l], 'FCELL', rastname)
def visual_magnitude_reverse(lreg_shape, t_loc, np_viewshed, reg, exp_range, r_dsm, dsm_type, v_elevation, b_1): """Calculate visual magnitude from viewpoints to target based on Chamberlain (2011) and Chamberlain & Meither (2013) and use it to parametrise binary viewshed :param lreg_shape: Dimensions of local computational region :type lreg_shape: list :param t_loc: Array of target point coordinates in local coordinate system :type t_loc: ndarray :param np_viewshed: 2D array of binary viewshed :type np_viewshed: ndarray :param reg: computational region :type reg: Region() :param exp_range: exposure range :type reg: float :param r_dsm: Name of digital surface model raster :type r_dsm: string :param dsm_type: Raster map precision type :type dsm_type: string :param v_elevation: Observer height :type v_elevation: float :param b_1: radius in fuzzy viewshed parametrisation :type b_1: float :return: 2D array of weighted parametrised viewshed :rtype: ndarray """ # 1. Convert DSM to numpy np_dsm = raster2numpy(r_dsm) # Ensure that values are represented as float (in case of CELL # data type) and replace integer NaN with numpy NaN dsm_type = grass.parse_command("r.info", map=r_dsm, flags="g")["datatype"] if dsm_type == "CELL": np_dsm = np_dsm.astype(np.float32) np_dsm[np_dsm == -2147483648] = np.nan # 2. local row, col coordinates and global Z coordinate of observer points V # 3D array (lreg_shape[0] x lreg_shape[1] x 3) v_loc = np.array([ np.tile( np.arange(0.5, lreg_shape[0] + 0.5).reshape(-1, 1), (1, lreg_shape[1])), np.tile( np.arange(0.5, lreg_shape[1] + 0.5).reshape(-1, 1).transpose(), (lreg_shape[0], 1), ), np_dsm + v_elevation, ]) # 3. vector VT, adjusted for cell size # 3D array (lreg_shape[0] x lreg_shape[1] x 3) v_vect = np.array([ (v_loc[0] - t_loc[0]) * reg.nsres, (v_loc[1] - t_loc[1]) * reg.ewres, (v_loc[2] - t_loc[2]), ]) # 4. projection of vector VT to XZ and YZ plane, adjusted for cell size v_vect_ns = np.array([(v_loc[0] - t_loc[0]) * reg.nsres, v_loc[2] - t_loc[2]]) v_vect_ew = np.array([(v_loc[1] - t_loc[1]) * reg.ewres, v_loc[2] - t_loc[2]]) v_vect_ns_unit = v_vect_ns / np.linalg.norm(v_vect_ns, axis=0) v_vect_ew_unit = v_vect_ew / np.linalg.norm(v_vect_ew, axis=0) # 5. size of vector VT # 2D array (lreg_shape[0] x lreg_shape[1]) v_scal = np.sqrt(v_vect[0]**2 + v_vect[1]**2 + v_vect[2]**2) # replace 0 distance for central pixel by resolution to avoid division by 0 # v_scal = np.where(abs(v_scal) < 1e-6, (reg.nsres + reg.ewres) / 2, v_scal) # grass.verbose(v_scal) # 6. vector n, its projection to XZ, YZ plane # 1D array [X, Z], [Y, Z] # already unit vector n_vect_ns_unit = [ np.sin(np.radians(t_loc[4])), np.cos(np.radians(t_loc[4])) ] n_vect_ew_unit = [ np.sin(np.radians(t_loc[3])), np.cos(np.radians(t_loc[3])) ] # 7. angles beta (ns), theta (ew) (0-90 degrees) # 2D array (lreg_shape[0] x lreg_shape[1]) beta = np.arccos(n_vect_ns_unit[0] * v_vect_ns_unit[:][0] + n_vect_ns_unit[1] * v_vect_ns_unit[:][1]) beta = np.where(beta > math.pi / 2, beta - math.pi / 2, beta) theta = np.arccos(n_vect_ew_unit[0] * v_vect_ew_unit[:][0] + n_vect_ew_unit[1] * v_vect_ew_unit[:][1]) theta = np.where(theta > math.pi / 2, theta - math.pi / 2, theta) # 8. visual magnitude adjusted for distance weight visual_magnitude = (np.cos(beta) * np.cos(theta) * ((reg.nsres * reg.ewres) / (v_scal**2))) # 9. Multiply visual magnitude by binary viewshed and weight return visual_magnitude * np_viewshed * t_loc[-1]
def solid_angle_reverse(lreg_shape, t_loc, np_viewshed, reg, exp_range, r_dsm, dsm_type, v_elevation, b_1): """Calculate solid angle from viewpoints to target based on Domingo-Santos et al. (2011) and use it to parametrise binary viewshed :param lreg_shape: Dimensions of local computational region :type lreg_shape: list :param t_loc: Array of target point coordinates in local coordinate system :type t_loc: ndarray :param np_viewshed: 2D array of binary viewshed :type np_viewshed: ndarray :param reg: computational region :type reg: Region() :param exp_range: exposure range :type reg: float :param r_dsm: Name of digital surface model raster :type r_dsm: string :param dsm_type: Raster map precision type :type dsm_type: string :param v_elevation: Observer height :type v_elevation: float :param b_1: radius in fuzzy viewshed parametrisation :type b_1: float :return: 2D array of weighted parametrised viewshed :rtype: ndarray """ # 1. Convert DSM to numpy np_dsm = raster2numpy(r_dsm) # Ensure that values are represented as float (in case of CELL # data type) and replace integer NaN with numpy NaN if dsm_type == "CELL": np_dsm = np_dsm.astype(np.float32) np_dsm[np_dsm == -2147483648] = np.nan # 2. local row, col coordinates and global Z coordinate of observer points V # 3D array (lreg_shape[0] x lreg_shape[1] x 3) v_loc = np.array([ np.tile( np.arange(0.5, lreg_shape[0] + 0.5).reshape(-1, 1), (1, lreg_shape[1])), np.tile( np.arange(0.5, lreg_shape[1] + 0.5).reshape(-1, 1).transpose(), (lreg_shape[0], 1), ), np_dsm + v_elevation, ]) # 3. local row, col coordinates and global Z coordinate of points A, B, C, D # 1D array [row, col, Z] a_loc = np.array([t_loc[0] + 0.5, t_loc[1] - 0.5, t_loc[3]]) b_loc = np.array([t_loc[0] - 0.5, t_loc[1] - 0.5, t_loc[4]]) c_loc = np.array([t_loc[0] - 0.5, t_loc[1] + 0.5, t_loc[5]]) d_loc = np.array([t_loc[0] + 0.5, t_loc[1] + 0.5, t_loc[6]]) # 4. vectors a, b, c, d, adjusted for cell size # 3D array (lreg_shape[0] x lreg_shape[1] x 3) a_vect = np.array([ (v_loc[0] - a_loc[0]) * reg.nsres, (v_loc[1] - a_loc[1]) * reg.ewres, (v_loc[2] - a_loc[2]), ]) b_vect = np.array([ (v_loc[0] - b_loc[0]) * reg.nsres, (v_loc[1] - b_loc[1]) * reg.ewres, (v_loc[2] - b_loc[2]), ]) c_vect = np.array([ (v_loc[0] - c_loc[0]) * reg.nsres, (v_loc[1] - c_loc[1]) * reg.ewres, (v_loc[2] - c_loc[2]), ]) d_vect = np.array([ (v_loc[0] - d_loc[0]) * reg.nsres, (v_loc[1] - d_loc[1]) * reg.ewres, (v_loc[2] - d_loc[2]), ]) # 5. sizes of vectors a, b, c, d # 2D array (lreg_shape[0] x lreg_shape[1]) a_scal = np.sqrt(a_vect[0]**2 + a_vect[1]**2 + a_vect[2]**2) b_scal = np.sqrt(b_vect[0]**2 + b_vect[1]**2 + b_vect[2]**2) c_scal = np.sqrt(c_vect[0]**2 + c_vect[1]**2 + c_vect[2]**2) d_scal = np.sqrt(d_vect[0]**2 + d_vect[1]**2 + d_vect[2]**2) # 6. scalar products ab, ac, bc, ad, dc # 2D arrays (lreg_shape[0] x lreg_shape[1]) ab_scal = sum(a_vect * b_vect) ac_scal = sum(a_vect * c_vect) bc_scal = sum(b_vect * c_vect) ad_scal = sum(a_vect * d_vect) dc_scal = sum(d_vect * c_vect) # 7. determinants of matrix abc, adc # 2D arrays (lreg_shape[0] x lreg_shape[1]) det_abc = (a_vect[0] * (b_vect[1] * c_vect[2] - b_vect[2] * c_vect[1]) - b_vect[0] * (a_vect[1] * c_vect[2] - a_vect[2] * c_vect[1]) + c_vect[0] * (a_vect[1] * b_vect[2] - a_vect[2] * b_vect[1])) det_adc = (a_vect[0] * (d_vect[1] * c_vect[2] - d_vect[2] * c_vect[1]) - d_vect[0] * (a_vect[1] * c_vect[2] - a_vect[2] * c_vect[1]) + c_vect[0] * (a_vect[1] * d_vect[2] - a_vect[2] * d_vect[1])) # 8. solid angle solid_angle_1 = np.arctan2( det_abc, a_scal * b_scal * c_scal + ab_scal * c_scal + ac_scal * b_scal + bc_scal * a_scal, ) solid_angle_2 = np.arctan2( det_adc, a_scal * d_scal * c_scal + ad_scal * c_scal + ac_scal * d_scal + dc_scal * a_scal, ) solid_angle = np.absolute(solid_angle_1) + np.absolute(solid_angle_2) # 9. Multiply solid angle by binary viewshed and weight return solid_angle * np_viewshed * t_loc[-1]
def do_it_all(global_vars, target_pts_np): """Conduct weighted and parametrised partial viewshed and cummulate it with the previous partial viewsheds :param target_pts_np: Array of target points in global coordinate system :type target_pts_np: ndarray :return: 2D array of weighted parametrised cummulative viewshed :rtype: ndarray """ # Set counter counter = 1 # Get variables out of global_vars dictionary reg = global_vars["region"] exp_range = global_vars["range"] flagstring = global_vars["flagstring"] r_dsm = global_vars["r_dsm"] v_elevation = global_vars["observer_elevation"] refr_coeff = global_vars["refr_coeff"] memory = global_vars["memory"] parametrise_viewshed = global_vars["param_viewshed"] dsm_type = global_vars["dsm_type"] b_1 = global_vars["b_1"] cores = global_vars["cores"] tempname = global_vars["tempname"] # Create empty viewshed np_cum = np.empty((reg.rows, reg.cols), dtype=np.single) np_cum[:] = np.nan tmp_vs = "{}_{}".format(tempname, os.getpid()) for target_pnt in target_pts_np: # Display a progress info message grass.percent(counter, len(target_pts_np), 1) grass.verbose("Processing point {i} ({p:.1%})".format( i=int(target_pnt[0]), p=counter / len(target_pts_np))) # Global coordinates and attributes of target point T t_glob = target_pnt[1:] # ====================================================================== # 1. Set local computational region: +/- exp_range from target point # ====================================================================== # compute position of target point within a pixel delta_n = math.ceil( (t_glob[1] - reg.south) / reg.nsres) * reg.nsres - (t_glob[1] - reg.south) delta_s = (t_glob[1] - reg.south) - math.floor( (t_glob[1] - reg.south) / reg.nsres) * reg.nsres delta_e = math.ceil( (t_glob[0] - reg.west) / reg.ewres) * reg.ewres - (t_glob[0] - reg.west) delta_w = (t_glob[0] - reg.west) - math.floor( (t_glob[0] - reg.west) / reg.ewres) * reg.ewres # ensure that local region doesn't exceed global region loc_reg_n = min(t_glob[1] + exp_range + delta_n, reg.north) loc_reg_s = max(t_glob[1] - exp_range - delta_s, reg.south) loc_reg_e = min(t_glob[0] + exp_range + delta_e, reg.east) loc_reg_w = max(t_glob[0] - exp_range - delta_w, reg.west) # pygrass sets region for pygrass tasks lreg = deepcopy(reg) lreg.set_bbox(Bbox(loc_reg_n, loc_reg_s, loc_reg_e, loc_reg_w)) lreg.set_raster_region() # Create processing environment with region information c_env = os.environ.copy() c_env["GRASS_REGION"] = grass.region_env(n=loc_reg_n, s=loc_reg_s, e=loc_reg_e, w=loc_reg_w) lreg_shape = [lreg.rows, lreg.cols] # ====================================================================== # 2. Calculate binary viewshed and convert to numpy # ====================================================================== vs = grass.pipe_command( "r.viewshed", flags="b" + flagstring, input=r_dsm, output=tmp_vs, coordinates="{},{}".format(t_glob[0], t_glob[1]), observer_elevation=0.0, target_elevation=v_elevation, max_distance=exp_range, refraction_coeff=refr_coeff, memory=int(round(memory / cores)), quiet=True, overwrite=True, env=c_env, ) vs.communicate() # Workaround for https://github.com/OSGeo/grass/issues/1436 clean_temp(vs.pid) # Read viewshed into numpy with single precision and replace NoData np_viewshed = raster2numpy(tmp_vs).astype(np.single) np_viewshed[np_viewshed == -2147483648] = np.nan # ====================================================================== # 3. Prepare local coordinates and attributes of target point T # ====================================================================== # Calculate how much of rows/cols of local region lies # outside global region o_1 = [ max(t_glob[1] + exp_range + reg.nsres / 2 - reg.north, 0), max(reg.west - (t_glob[0] - exp_range - reg.ewres / 2), 0), ] t_loc = np.append( np.array([ exp_range / reg.nsres + 0.5 - o_1[0] / reg.nsres, exp_range / reg.ewres + 0.5 - o_1[1] / reg.ewres, ]), t_glob[2:], ) # ====================================================================== # 4. Parametrise viewshed # ====================================================================== np_viewshed = parametrise_viewshed( lreg_shape, t_loc, np_viewshed, reg, exp_range, r_dsm, dsm_type, v_elevation, b_1, ).astype(np.single) # ====================================================================== # 5. Cummulate viewsheds # ====================================================================== # Determine position of local parametrised viewshed within # global cummulative viewshed o_2 = [ int(round((reg.north - loc_reg_n) / reg.nsres)), # NS (rows) int(round((loc_reg_w - reg.west) / reg.ewres)), # EW (cols) ] # Add local parametrised viewshed to global cummulative viewshed # replace nans with 0 in processed regions, keep nan where both are nan all_nan = np.all( np.isnan([ np_cum[o_2[0]:o_2[0] + lreg_shape[0], o_2[1]:o_2[1] + lreg_shape[1]], np_viewshed, ]), axis=0, ) np_cum[o_2[0]:o_2[0] + lreg_shape[0], o_2[1]:o_2[1] + lreg_shape[1]] = np.nansum( [ np_cum[o_2[0]:o_2[0] + lreg_shape[0], o_2[1]:o_2[1] + lreg_shape[1]], np_viewshed, ], axis=0, ) np_cum[o_2[0]:o_2[0] + lreg_shape[0], o_2[1]:o_2[1] + lreg_shape[1]][all_nan] = np.nan counter += 1 return np_cum