示例#1
0
    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()
示例#2
0
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'])