def __init__(self): # _members stores member regions in a dictionary keyed by RID self._members = {} # These IDGenerator instances generate unique ID numbers that are never # reused, and always unique (once used an ID number cannot be # reassigned to another agent). All instances of the Person class, for # example, will have a unique ID number generated by the PIDGen # IDGenerator instance. self._PIDGen = IDGenerator() self._RIDGen = IDGenerator()
class World(Agent_set): """ The world class generates new agents, while tracking ID numbers to ensure that they are always unique across each agent type. It also contains a dictionary with all the regions in the model. """ def __init__(self): # _members stores member regions in a dictionary keyed by RID self._members = {} # These IDGenerator instances generate unique ID numbers that are never # reused, and always unique (once used an ID number cannot be # reassigned to another agent). All instances of the Person class, for # example, will have a unique ID number generated by the PIDGen # IDGenerator instance. self._PIDGen = IDGenerator() self._RIDGen = IDGenerator() def set_DEM_data(DEM, gt, prj): self._DEM_array = DEM self._DEM_gt = gt self._DEM_prj = prj return 0 def get_DEM(self): return self._DEM_array def get_DEM_data(self): return self._DEM_array, self._DEM_gt, self._DEM_prj def set_world_mask_data(self, world_mask, gt, prj): self._world_mask_array = world_mask self._world_mask_gt = gt self._world_mask_prj = prj return 0 def get_world_mask(self): return self._world_mask_array def get_world_mask_data(self): return self._world_mask_array, self._world_mask_gt, self._world_mask_prj def set_lulc_data(self, lulc, gt, prj): self._lulc_array = lulc self._lulc_gt = gt self._lulc_prj = prj return 0 def get_lulc(self): return self._lulc_array def set_lulc(self, new_lulc): self._lulc_array = new_lulc def get_lulc_data(self): return self._lulc_array, self._lulc_gt, self._lulc_prj def set_lulc_markov_matrix(self, markov_file): """ Stores a markov transition matrix in dictionary form to be used for a simple model of land cover change. The transitions are stored in a dictionary keyed as: class_t1:class[t_2]:probability """ file = open(markov_file, 'r') lines = file.readlines() file.close() # Delete the first line since it is a header if not (lines.pop(0) == '"first","second","lower","upper"\n'): raise ValueError("Error loading transition matrix (%s doesn't look like a markov transition file)"%markov_file) markov_dict = {} for line in lines: line = line.strip('\n') t1, t2, lower, upper = line.split(',') t1 = int(t1.strip('\'"')) t2 = int(t2.strip('\'"')) lower = float(lower.strip('\'"')) upper = float(upper.strip('\'"')) if not markov_dict.has_key(t1): markov_dict[t1] = {} if markov_dict[t1].has_key(t2): raise ValueError("Error in transition matrix in %s"%markov_file) markov_dict[t1][t2] = (lower, upper) self._markov_dict = markov_dict def lulc_markov_transition(self): # TODO: Right now this only works if there are only two classes. Rewrite # this to work for a wider range of scenarios. current_lu = self.get_lulc() new_lu = current_lu random_values = np.random.random(np.shape(current_lu)) for t1 in self._markov_dict.keys(): for t2 in self._markov_dict[t1].keys(): lower_prob_bound, upper_prob_bound = self._markov_dict[t1][t2] new_lu[(current_lu == t1) & (random_values >= lower_prob_bound) & (random_values < upper_prob_bound)] = t2 self.set_lulc(new_lu) def new_person(self, birthdate, PID=None, age=0, sex=None, initial_agent=False, ethnicity=None, education=None, charcoal=None, own_toilet=None, yrs_in_house_cat=None, x=None, y=None, health=None): "Returns a new person agent." if PID == None: PID = self._PIDGen.next() else: # Update the generator so the PID will not be reused self._PIDGen.use_ID(PID) return Person(self, birthdate, PID, age, sex, initial_agent, ethnicity, education, charcoal, own_toilet, yrs_in_house_cat, x, y, health) def new_region(self, RID=None, initial_agent=False): "Returns a new region agent, and adds it to the world member list." if RID == None: RID = self._RIDGen.next() else: # Update the generator so the RID will not be reused self._RIDGen.use_ID(RID) region = Region(self, RID, initial_agent) self.add_agent(region) return region def get_regions(self): return self._members.values() def iter_regions(self): "Convenience function for iteration over all regions in the world." for region in self._members.values(): yield region def iter_persons(self): "Convenience function used for things like incrementing agent ages." for region in self.iter_regions(): for person in region.iter_persons(): yield person def num_persons(self): "Convenience function used for getting total populationsize." pop = 0 for region in self.iter_regions(): pop += region.num_persons() return pop def write_persons_to_csv(self, timestep, results_path): """ Writes a list of persons, with a header row, to CSV. """ psn_csv_file = os.path.join(results_path, "psns_time_%s.csv"%timestep) out_file = open(psn_csv_file, "w") csv_writer = csv.writer(out_file) csv_writer.writerow(["pid", "x_utm30", "y_utm30", "rid", "gender", "age", "education", "charcoal", "own_toilet", "yrs_in_house_cat", "health", "ethnicity", "veg_fraction"]) for region in self.iter_regions(): for person in region.iter_persons(): new_row = [] new_row.append(person.get_ID()) new_row.append(person._x) new_row.append(person._y) new_row.append(person.get_parent_agent().get_ID()) new_row.append(person.get_sex()) new_row.append(person.get_age_years()) new_row.append(person._education) new_row.append(person._charcoal) new_row.append(person._own_toilet) new_row.append(person._yrs_in_house_cat) new_row.append(person._health) new_row.append(person._ethnicity) new_row.append(person._veg_fraction) csv_writer.writerow(new_row) out_file.close() def extract_egocentric_neighborhoods(self, lulc_data, buffer): """ Extracts a buffer from the given dataset around each person in the world. Note that the 'lulc_data' parameter should be a tuple as is returned from the get_**_data functions (get_lulc_data, get_DEM_data, etc.), and that the 'buffer' parameter should be specified in meters. """ lulc_array, gt, prj = lulc_data rows, cols = np.shape(lulc_array) min_x = gt[0] max_y = gt[3] pixel_width = gt[1] pixel_height = gt[5] # note pixel_height is negative max_x = min_x + cols * pixel_width min_y = max_y + rows * pixel_height buffer_pixels_x = int(np.round(buffer / pixel_width, 0)) buffer_pixels_y = int(np.round(buffer / np.abs(pixel_height), 0)) def convert_to_img_coords(x, y): img_x = int((x - min_x)/pixel_width) img_y = int((y - max_y)/pixel_height) return img_x, img_y data = np.zeros((2*buffer_pixels_x, 2*buffer_pixels_y, self.num_persons()), dtype='int8') person_IDs = [] n = -1 for person in self.iter_persons(): n += 1 person_IDs.append(person.get_ID()) x = person.get_x() y = person.get_y() assert ((x - buffer) > min_x) & ((x + buffer) < max_x), "Neighborhood must be within raster image" assert ((y - buffer) > min_y) & ((y + buffer) < max_y), "Neighborhood must be within raster image" # Round off coordinates to the nearest center of a cell x = round((x - min_x) / pixel_width, 0)*pixel_width + min_x + pixel_width/2 y = round((y - min_y) / np.abs(pixel_height), 0)*np.abs(pixel_height) + min_y + np.abs(pixel_height)/2 center_x, center_y = convert_to_img_coords(x, y) # In the below lines ul means "upper left", lr means "lower right" ul_x, ul_y = center_x-buffer_pixels_x+1, center_y-buffer_pixels_y+1 # here buffer_pixels_y is positive but remember y increases going down in the image lr_x, lr_y = center_x+buffer_pixels_x+1, center_y+buffer_pixels_y+1 box = lulc_array[ul_y:lr_y, ul_x:lr_x] # TODO: The commented out line below should eventually replace the # one following it for storing the box in the data array. The # current line is used to ensure NANs get filled in areas where the # neighborhood boundary for a person is outside the image. #data[:,:,n] = box assert np.shape(box) == np.shape(data)[0:2], "NBH box must always cover a full NBH" data[0:np.shape(box)[0], 0:np.shape(box)[1], n] = box return person_IDs, data def calculate_veg_fractions(self): # Vegetation is coded as: # 0: NA # 1: NONVEG # 2: VEG veg_value = rcParams['lulc.veg_value'] NA_value = rcParams['lulc.NA_value'] if rcParams['NBH_effects_type'] == "egocentric": buffer = rcParams['lulc.buffer'] person_IDs, neighborhoods = self.extract_egocentric_neighborhoods(self.get_lulc_data(), buffer) veg_fractions_dict = calculate_cover_fraction_NBH(person_IDs, neighborhoods, veg_value, NA_value) veg_fractions = 0. for person in self.iter_persons(): person._veg_fraction = veg_fractions_dict[person.get_ID()] veg_fractions += person._veg_fraction # Return mean veg fraction for use in progress tracking printout # while running the model. return veg_fractions / self.num_persons() elif rcParams['NBH_effects_type'] == "vernacular": # Otherwise use vernacular neighborhood (veg_fraction calculated # over the entire world - this works since currently the model is # only run with a single vernacular neighborhood at a time). veg_fraction = calculate_cover_fraction_world(self.get_lulc(), veg_value, NA_value) for person in self.iter_persons(): person._veg_fraction = veg_fraction # Return FMV NBH veg fraction for use in progress tracking printout # while running the model. return veg_fraction elif rcParams['NBH_effects_type'] == "none": return 0 else: raise ValueError('Invalid neighborhoods effects type "%s"'%rcParams['NBH_effects_type'])