def prune_neighbors(self, arr: numpy.ndarray, distance=2):
     comp_arr = numpy.full(arr.shape, True, dtype=int)
     print(_("pruning values..."))
     for (x, y), item in numpy.ndenumerate(arr):
         summed = 0
         if item > 0:
             for xd in range(x - distance, x + distance + 1):
                 if summed > 1:
                     continue
                 for yd in range(y - distance, y + distance + 1):
                     with suppress(IndexError):
                         summed += (1 if arr[xd][yd] > 0 else 0)
         comp_arr[x][y] = int(summed)
     mask = numpy.ma.greater(comp_arr, 1)
     print(_("pruned {}").format(mask.count()))
     return arr * mask
 def __init__(self,
              i3d_fn: str,
              tree_source: str,
              raster_source: Path,
              shape: str = None,
              xml_raster_metadata: str = None):
     """
     takes a i3d file and creates forests automatically based on a combination of either shp or xml data with a
     raster layer input to specify locations of the forests
     :param i3d_fn: path to an i3d file (not the data itself)
     :param tree_source: the source TransformGroup name to pull the trees from
     :param raster_source: a png or tiff that matches the DEM file in the i3d file
     :param shape: a shapefile that the raster_source that was exported from a GIS app with corresponding metadata
     :param xml_raster_metadata: a xml file that corresponds with the infoLayer png (raster_source)
     """
     super().__init__()
     self.i3d_data = i3d.TransformGroup(file=i3d_fn)
     self.terrain = i3d.Terrain(i3d=self.i3d_data.data)
     self.tree_source = tree_source
     self.shp = shape
     self.xml_raster_metadata = xml_raster_metadata
     self.raster = simple_rasters.read_img(raster_source)
     self.global_density_factor = 0.3  # 0.6 is good for a realistic 'feel' but it's not great for gameplay
     self.dem_files = self.terrain.get_dem_files()
     assert self.dem_files, _("DEM not found in i3d")
     self.__shp_records = None
     self.__shp_fields = None
     self.__dem = None
    def generateMask(self,
                     record: shapefile.ShapeRecord,
                     rand_array: numpy.ndarray,
                     species: list = [],
                     weighting: list = []):
        transform = TransformGroup()
        mask = numpy.ma.equal(self.raster, record.record.id)
        aoi = rand_array * mask
        masked = aoi * self.prune_neighbors(aoi)
        log_choice = numpy.flip(numpy.logspace(0, 1, len(species), base=10))
        _weighting = list(weighting.values()) if isinstance(
            weighting, dict) else weighting
        if _weighting:
            probabilities = _weighting
        else:
            probabilities = log_choice / log_choice.sum()
        print(_("Probabilities:"))
        for i in range(len(probabilities)):
            print(
                f"{species[i][transform.name]}: {round(probabilities[i] * 100, 2)}%"
            )
        tree_species = numpy.random.choice(list(range(0, len(species))),
                                           mask.count(),
                                           p=probabilities)

        y_loc = 0
        locations = []
        print(_("masking grid..."))
        i = 0
        for (x, y), val in numpy.ndenumerate(masked):
            if val > 0:
                locations.append({
                    'x': x,
                    'y': y,
                    'z': species[tree_species[i]]
                })
                i += 1
        return locations
Example #4
0
        target = f"{output}0{str(index)}_weight.png"
        if os.path.exists(target):
            bak = target + ".bak"
            if os.path.exists(bak) and overwrite is True:
                os.remove(bak)
            shutil.move(target, bak)
        imageio.imwrite(target, sample)


def main(output: str,
         input_file: str = "blank.png",
         overwrite: bool = False,
         layers: int = 4):
    for i in range(1, layers, 1):
        weight_target = f"{output}0{str(i)}_weight.png"
        if not os.path.exists(weight_target) or overwrite is True:
            shutil.copyfile(input_file, weight_target)


if __name__ == "__main__":
    while True:
        try:
            target = input(_("output path (`C:\\exmaple\\animalMud`): ")) \
                     or r"D:\Games\MyMods\Sussex\maps\mapNB1\animalMud"
            source = input(
                _("sourceImage (blank for blank.png): ")) or "blank.png"
            generate_weights(target, source, overwrite=False, layers=4)
        except KeyboardInterrupt:
            print("Goodbye!")
            raise
 def generate(self,
              record: shapefile.ShapeRecord,
              rand_array: numpy.ndarray,
              forest_number: int = 1,
              id_start: int = 1000000,
              gitter=1.25,
              z_offset=-0.05):  # , tree_types:list =[]):
     print(_("processing forest #" + str(forest_number)))
     transform = TransformGroup()
     terrain_pixel_scale = float(self.terrain.get_transform_group()[0][
         transform.prefix('unitsPerPixel')])
     prefix = 'base'
     forest = transform.new_transform_group(name=f"forest{forest_number}",
                                            identifier=id_start,
                                            children=[])
     id_start += 1
     trees = self.trees[0][0][transform.default_label]
     tree_names = list(
         map(lambda x: x[transform.name].lstrip(prefix), trees))
     tree_weights = list(map("wgt{}".format, tree_names))
     weights = [0.37, 0.53, 0.07, 0.03]
     shp_weights = {}
     for tree_weight in tree_weights:
         try:
             _weight = getattr(record.record, tree_weight)
         except AttributeError as exc:
             print(
                 _("missing tree weights for shp with name:") + tree_weight)
         else:
             if _weight is not None:
                 shp_weights[tree_weight] = _weight
     if shp_weights:
         if sum(shp_weights.values()) == 1:
             weights = shp_weights
         else:
             print(
                 _("weights do not sum to 1.0 for {}").format(
                     record.record.id))
     tree_count = {}
     for loc in self.generateMask(record=record,
                                  rand_array=rand_array,
                                  species=trees,
                                  weighting=weights):
         clone_tree = loc['z'].copy()
         tree_tg = clone_tree[transform.default_label]
         no_prefix = clone_tree[transform.name].lstrip(prefix).lower()
         tree_count.setdefault(no_prefix, {})
         try:
             available_ages = [
                 int(x[transform.name].lstrip(f"{no_prefix}_stage"))
                 for x in tree_tg
             ]
         except TypeError as exc:
             clone_tree_list = [[
                 tree_tg
             ]]  # a single tree in the group causes this (maple)
             rand_choice = tree_tg[transform.name].lstrip(
                 f"{no_prefix}_stage")
         else:
             available_ages = [
                 x for x in available_ages
                 if record.record.minSize <= x <= record.record.maxSize
             ]
             if not available_ages:
                 print(
                     _("found invalid min/max size for record (or no tree of permitted ages)#"
                       ) + str(record.record.id))
                 continue
             rand_choice = random.choice(available_ages)
             target_name = f"{no_prefix}_stage{rand_choice}"
             clone_tree_list = get_for_key(transform.name, target_name,
                                           dict(root=tree_tg))
         tree_count[no_prefix].setdefault(str(rand_choice), 0)
         tree_count[no_prefix][str(rand_choice)] += 1
         x_gitter = abs(random.random()) % (gitter - gitter * 2)
         y_gitter = abs(random.random()) % (gitter - gitter * 2)
         dem_values = []
         for _x in range(loc['x'] - 1, loc['x'] + 2):
             for _y in range(loc['y'] - 1, loc['y'] + 2):
                 dem_values.append(self.dem[_x, _y])
         dem_height = sum(sorted(dem_values[:2])) / 2
         z_val = dem_height / (pow(2, 16) / pow(2, 8)) + z_offset
         loc_str = "{x} {z} {y}".format(
             **{
                 "x": ((float(loc['y']) - self.raster.shape[0] / 2) *
                       terrain_pixel_scale) + x_gitter,
                 "y": ((float(loc['x']) - self.raster.shape[1] / 2) *
                       terrain_pixel_scale) + y_gitter,
                 'z':
                 z_val
             })
         if clone_tree_list:
             tree = clone_tree_list[0][0].copy()
             tree[transform.translation] = loc_str
             rotate = map(
                 str, [0, round(abs(random.random() % 360), 2) - 180, 0])
             tree[transform.rotation] = "{} {} {}".format(*rotate)
             tree[transform.id] = str(id_start)
             id_start += 1
             forest[transform.default_label].append(tree)
     print(
         _("forest #{} has {} trees").format(
             forest_number, len(forest[transform.default_label])))
     print(json.dumps(tree_count, indent=2, sort_keys=True))
     return forest