예제 #1
0
    def generate_item_3d_tree(self, item_2d):

        coords = item_2d.geom.coords[0]

        #invalid = ddd.group([self.osm.ways_2d["0"], self.osm.buildings_2d])
        #if not self.osm.osmops.placement_valid(ddd.disc(coords, r=0.4), invalid=invalid):
        #    return None

        numvariants = 5  # 7
        '''
        tree_type = item_2d.extra.get('osm:tree:type')
        if tree_type is None:
            tree_type = random.choice(['default', 'palm'])
        '''

        tree_type = item_2d.get('osm:tree:type')
        if isinstance(tree_type, dict):
            tree_type = weighted_choice(tree_type)

        key = "tree-%s-%d" % (
            tree_type, random.choice([x + 1 for x in range(numvariants)]))

        item_3d = self.osm.catalog.instance(key)
        if not item_3d:
            plant_height = random.normalvariate(8.0, 3.0)
            if plant_height < 4.0: plant_height = random.uniform(4.0, 6.5)
            if plant_height > 35.0: plant_height = random.uniform(30.0, 35.0)

            if tree_type == 'default':
                plant_height += 3
                item_3d = plants.tree_default(height=plant_height)
            elif tree_type == 'palm':
                plant_height += 6
                item_3d = plants.tree_palm(height=plant_height)
            elif tree_type == 'fir':
                item_3d = plants.tree_fir(height=plant_height)
            elif tree_type == 'bush':
                item_3d = plants.tree_bush(height=plant_height)
            elif tree_type == 'reed':
                item_3d = plants.reed()
            else:
                raise DDDException("Unknown tree type %r for object %s" %
                                   (tree_type, item_2d))

            item_3d = self.osm.catalog.add(key, item_3d)

        item_3d = item_3d.rotate([0.0, 0.0, random.uniform(0, math.pi * 2)])
        item_3d = item_3d.translate([coords[0], coords[1], 0.0])
        item_3d.name = 'Tree: %s' % item_2d.name
        return item_3d
예제 #2
0
파일: s55_plants.py 프로젝트: c3ypt1c/ddd
def generate_area_2d_park(area, tree_density_m2=0.0025, tree_types=None):

    max_trees = None

    if tree_types is None:
        tree_types = {'default': 1}  #, 'palm': 0.001}

    #area = ddd.shape(feature["geometry"], name="Park: %s" % feature['properties'].get('name', None))
    feature = area.extra['osm:feature']
    area = area.material(ddd.mats.park)
    area.name = "Park: %s" % feature['properties'].get('name', None)
    area.extra['ddd:area:type'] = 'park'

    # Add trees if necesary
    # FIXME: should not check for None in intersects, filter shall not return None (empty group)

    trees = ddd.group2(name="Trees (Aug): %s" % area.name)

    if area.geom:

        tree_area = area  # area.intersection(ddd.shape(osm.area_crop)).union()
        if tree_area.geom:
            # Decimation would affect after
            num_trees = int((tree_area.geom.area * tree_density_m2))
            #if num_trees == 0 and random.uniform(0, 1) < 0.5: num_trees = 1  # alone trees
            if max_trees:
                num_trees = min(num_trees, max_trees)

            def filter_func_noise(coords):
                val = noise.pnoise2(coords[0] * 0.1, coords[1] * 0.1, octaves=2, persistence=0.5, lacunarity=2, repeatx=1024, repeaty=1024, base=0)
                return (val > random.uniform(-0.5, 0.5))

            for p in tree_area.random_points(num_points=num_trees):
                tree_type = weighted_choice(tree_types)
                tree = ddd.point(p, name="Tree")
                tree.extra['ddd:aug:status'] = 'added'
                tree.extra['osm:natural'] = 'tree'  # TODO: Change to DDD
                tree.extra['osm:tree:type'] = tree_type  # TODO: Change to DDD
                trees.append(tree)

    return trees
예제 #3
0
    def generate_building_3d_generic(self, building_2d):
        """
        Buildings 2D may contain references to building parts.

        This section also sets ddd:building:building_2d, for parent buildings and building parts, referencing
        the respective 2D geometry (eg. to be used for snapping or footprints).

        TODO: Do a lot more in tags in 2D and here, and generalize tasks to pipelines and tags.
        """

        # Calculate floor 0 height from actual terrain elevation difference on building footprint
        # TODO: sample more points, as sometimes footprint
        elevation_min = building_2d.get('ddd:building:elevation:min')
        elevation_max = building_2d.get('ddd:building:elevation:max')
        elevation_diff = elevation_max - elevation_min
        floor_0_height = 3 + elevation_diff # TODO: temporary for test, should be floor info

        floors = building_2d.extra.get('ddd:building:levels', building_2d.extra.get('osm:building:levels', None))
        floors_min = building_2d.extra.get('ddd:building:min_level', building_2d.extra.get('osm:building:min_level', 0))
        building_height = parse_meters(building_2d.extra.get('osm:height', 0))

        # TODO: Do this in the pipeline or fail here if ddd:building:levels not set
        if not floors:
            if building_height:
                floors = int((building_height + 3 - floor_0_height) / 3.0)
            else:
                floors = random.randint(2, 8)

        floors = int(float(floors))
        floors_min = int(float(floors_min))
        base_floors = floors
        base_floors_min = floors_min

        random.seed(hash(building_2d.name))
        building_material = random.choice(["building_1", "building_2", "building_3", "building_4", "building_5"])
        material_name = building_2d.get('ddd:building:material', building_2d.get('osm:building:material', building_material))

        # Roof defaults
        roof_shape = weighted_choice({'none': 2, 'flat': 1, 'pyramidal': 0.5, 'attic': 0.5, 'terrace': 1})
        if floors < 2:
            roof_shape = 'none'
        #if floors < base_floors:
        #    pbuffered = False
        #    if (random.uniform(0, 1) < 0.5): roof_shape = random.choice(['terrace', 'none'])
        #    if (floors <= 2):
        #        if (random.uniform(0, 1) < 0.8): roof_shape = random.choice(['terrace', 'terrace', 'terrace', 'none'])
        #if 'osm:building:part' in part.extra:
        #    roof_shape = 'none'
        #    pbuffered = 0
        roof_shape = building_2d.get('ddd:roof:shape', building_2d.get('osm:roof:shape', roof_shape))
        roof_height = parse_meters(building_2d.get('ddd:roof:height', building_2d.extra.get('osm:roof:height', 0)))

        entire_building_3d = None
        parts = building_2d.extra.get('ddd:building:parts', None)
        if parts is None or len(building_2d.children) == 0:
            buildparts = [building_2d]
        else:
            entire_building_3d = building_2d.copy3(name="Building (mp): %s" % (building_2d.name))
            buildparts = parts

        for part in buildparts:

            if part.is_empty():
                continue

            # Propagate values
            part.set('ddd:building:elevation:min', default=elevation_min)
            part.set('ddd:building:elevation:max', default=elevation_max)
            part.set('ddd:building:min_level', default=part.extra.get('osm:building:min_level', base_floors_min))
            part.set('ddd:building:levels', default=int(float(part.extra.get('osm:building:levels', base_floors))))
            part.set('ddd:building:material', default=part.extra.get('osm:building:material', material_name))
            part.set('ddd:building:level:0:height', floor_0_height)
            part.set('ddd:roof:shape', default=part.extra.get('osm:roof:shape', roof_shape))
            part.set('ddd:roof:height', default=part.extra.get('osm:roof:height', roof_height))
            if material_name: part.set('ddd:building:material', default=material_name)

            subbuilding = self.generate_building_3d_generic_part(part)

            if not subbuilding:
                logger.error("No building part generated for: %s (part: %s)", building_2d, part)
                continue

            if len(buildparts) > 1:
                entire_building_3d.append(subbuilding)
            else:
                entire_building_3d = subbuilding

        if entire_building_3d:
            entire_building_3d.extra['ddd:building:building_2d'] = building_2d
        else:
            entire_building_3d = None

        return entire_building_3d
예제 #4
0
    def generate_building_3d_generic(self, building_2d):
        """
        Buildings 2D may contain references to building parts.

        TODO: Do a lot more in tags in 2D and here, and generalize tasks to pipelines and tags.
        Support buildings recursively earlier.
        """

        floors = building_2d.extra.get('osm:building:levels', None)
        floors_min = building_2d.extra.get('osm:building:min_level', 0)
        if not floors:
            floors = random.randint(2, 8)

        floors = int(float(floors))
        floors_min = int(float(floors_min))
        base_floors = floors
        base_floors_min = floors_min

        random.seed(hash(building_2d.name))
        building_material = random.choice([ddd.mats.building_1, ddd.mats.building_2, ddd.mats.building_3])

        if building_2d.extra.get('osm:building:material', None):
            material_name = building_2d.extra.get('osm:building:material')
            if hasattr(ddd.mats, material_name):
                building_material = getattr(ddd.mats, material_name)

        entire_building_2d = ddd.group2()
        entire_building_3d = building_2d.copy3(name="Building: %s" % (building_2d.name))

        roof_type = weighted_choice({'none': 2,
                                     'flat': 1,
                                     'pyramidal': 0.5,
                                     'attic': 0.5,
                                     'terrace': 1})
        roof_buffered = weighted_choice({True: 1, False: 5})
        roof_buffer = random.uniform(0.5, 1.2)
        roof_wall_material = weighted_choice({"stone": 3, "bricks": 1})

        for part in (building_2d.extra.get('ddd:building:parts', []) + [building_2d]):

            # Process subbuildings recursively (non standard, but improves support and compatibility with other renderers)
            if part != building_2d and part.extra.get('osm:building', None) is not None:
                subbuilding = self.generate_building_3d_generic(part)
                entire_building_2d.append(part)
                entire_building_3d.append(subbuilding)
                continue

            building_3d = None
            try:

                floors = int(float(part.extra.get('osm:building:levels', base_floors)))
                if floors == 0:
                    logger.warn("Building part with 0 floors (setting to 1): %s", floors)
                    floors = 1
                floors_min = int(float(part.extra.get('osm:building:min_level', base_floors_min)))

                # Remove the rest of the building
                if part == building_2d:
                    part = part.subtract(entire_building_2d)
                if part.geom.is_empty:
                    continue

                material = building_material
                if part.extra.get('osm:building:material', None):
                    material_name = part.extra.get('osm:building:material')
                    if hasattr(ddd.mats, material_name):
                        material = getattr(ddd.mats, material_name)
                if part.extra.get('osm:building:facade:material', None):
                    material_name = part.extra.get('osm:building:facade:material')
                    if hasattr(ddd.mats, material_name):
                        material = getattr(ddd.mats, material_name)

                # Roof: default
                pbuffered = roof_buffered
                roof_shape = roof_type
                if floors < 2:
                    roof_shape = 'none'
                if floors < base_floors:
                    pbuffered = False
                    if (random.uniform(0, 1) < 0.5): roof_shape = random.choice(['terrace', 'none'])
                    if (floors <= 2):
                        if (random.uniform(0, 1) < 0.8): roof_shape = random.choice(['terrace', 'terrace', 'terrace', 'none'])
                if 'osm:building:part' in part.extra:
                    roof_shape = 'none'
                    pbuffered = 0

                # Roof: info
                roof_shape = part.extra.get('osm:roof:shape', roof_shape)
                roof_height = float(part.extra.get('osm:roof:height', 0))

                roof_material = ddd.mats.roof_tiles
                if part.extra.get('osm:roof:material', None):
                    material_name = part.extra.get('osm:roof:material')
                    if hasattr(ddd.mats, material_name):
                        roof_material = getattr(ddd.mats, material_name)

                floors_height = floors * 3.00
                floors_min_height = floors_min * 3.00
                min_height = float(part.extra.get('osm:min_height', floors_min_height))
                #max_height = parse_meters(part.extra.get('osm:height', floors_height + min_height)) - roof_height
                max_height = parse_meters(part.extra.get('osm:height', floors_height)) - roof_height
                dif_height = max_height - min_height

                # Generate building procedurally (use library)
                try:
                    building_3d = part.extrude(dif_height)
                except ValueError as e:
                    logger.error("Could not generate building (%s): %s", part, e)
                    continue
                except DDDException as e:
                    logger.error("Could not generate building (%s): %s", part, e)
                    continue

                if min_height == 0:
                    building_3d = ddd.meshops.remove_faces_pointing(building_3d, ddd.VECTOR_DOWN)

                if min_height: building_3d = building_3d.translate([0, 0, min_height])
                building_3d = building_3d.material(material)

                # Building solid post processing
                if part.extra.get('osm:tower:type', None) == 'bell_tower':  # and dif_height > 6:
                    # Cut
                    center_pos = part.centroid().geom.coords[0]
                    (axis_major, axis_minor, axis_rot) = ddd.geomops.oriented_axis(part)
                    cut1 = ddd.rect([-axis_major.length(), -axis_minor.length() * 0.20, +axis_major.length(), +axis_minor.length() * 0.20])
                    cut2 = ddd.rect([-axis_major.length() * 0.20, -axis_minor.length(), +axis_major.length() * 0.20, +axis_minor.length()])
                    cuts = ddd.group2([cut1, cut2]).union().rotate(axis_rot).extrude(-6.0).translate([center_pos[0], center_pos[1], max_height - 2])
                    #ddd.group3([building_3d, cuts]).show()
                    building_3d = building_3d.subtract(cuts)
                    #building_3d.show()

                    # TODO: Create 1D items
                    (axis_major, axis_minor, axis_rot) = ddd.geomops.oriented_axis(part.buffer(-0.80))
                    for coords in (axis_major.geom.coords[0], axis_major.geom.coords[1], axis_minor.geom.coords[0], axis_minor.geom.coords[1]):
                        bell = urban.bell().translate([coords[0], coords[1], max_height - 3.0])
                        entire_building_3d.append(bell)



                # Base
                if 'osm:building:part' not in part.extra:
                    if random.uniform(0, 1) < 0.2:
                        base = part.buffer(0.3, cap_style=2, join_style=2).extrude(1.00)
                        base = base.material(random.choice([ddd.mats.building_1, ddd.mats.building_2, ddd.mats.building_3, ddd.mats.stone, ddd.mats.cement]))
                        building_3d.children.append(base)

                # Roof
                try:
                    roof = None

                    if roof_shape == 'flat':
                        # Flat
                        default_height = 0.75
                        roof_height = roof_height if roof_height else default_height
                        roof = part.buffer(roof_buffer if pbuffered else 0, cap_style=2, join_style=2).extrude(roof_height).translate([0, 0, max_height]).material(roof_material)

                    elif roof_shape == 'terrace':
                        # Flat
                        usefence = random.uniform(0, 1) < 0.8
                        if usefence:
                            terrace = part.subtract(part.buffer(-0.4)).extrude(0.6).translate([0, 0, max_height]).material(getattr(ddd.mats, roof_wall_material))
                            fence = part.buffer(-0.2).outline().extrude(0.7).twosided().translate([0, 0, max_height + 0.6]).material(ddd.mats.railing)
                            roof = ddd.group3([terrace, fence], name="Roof")
                        else:
                            terrace = part.subtract(part.buffer(-0.4)).extrude(random.uniform(0.40, 1.20)).translate([0, 0, max_height]).material(ddd.mats.stone)
                            roof = ddd.group3([terrace], name="Roof")

                    elif roof_shape == 'pyramidal':
                        # Pointy
                        default_height = floors * 0.2 + random.uniform(2.0, 5.0)
                        roof_height = roof_height if roof_height else default_height
                        roof = part.buffer(roof_buffer if pbuffered else 0, cap_style=2, join_style=2).extrude_step(part.centroid(), roof_height)
                        roof = roof.translate([0, 0, max_height]).material(roof_material)

                    elif roof_shape == 'attic':
                        # Attic
                        height = random.uniform(3.0, 4.0)
                        roof = part.buffer(roof_buffer if pbuffered else 0, cap_style=2, join_style=2).extrude_step(part.buffer(-2), height, method=ddd.EXTRUSION_METHOD_SUBTRACT).translate([0, 0, max_height]).material(roof_material)

                    elif roof_shape == 'gabled':
                        # Attic
                        base = part.buffer(roof_buffer if pbuffered else 0)
                        orientation = "major"
                        if part.extra.get("osm:roof:orientation", "along") == "across": orientation = "minor"
                        (axis_major, axis_minor, axis_rot) = ddd.geomops.oriented_axis(base)
                        axis_line = axis_major if orientation == "major" else axis_minor
                        default_height = random.uniform(3.0, 4.0)
                        roof_height = roof_height if roof_height else default_height
                        roof = base.extrude_step(axis_line, roof_height).translate([0, 0, max_height]).material(roof_material)

                        '''
                        #elif roof_shape == 'round':
                        # Attic
                        base = part.buffer(roof_buffer if pbuffered else 0)
                        orientation = "major"
                        if part.extra.get("osm:roof:orientation", "along") == "across": orientation = "minor"
                        (axis_major, axis_minor, axis_rot) = ddd.geomops.oriented_axis(base)
                        axis_line = axis_major if orientation == "major" else axis_minor

                        major_seg_plus = ((axis_major.coords[0][0] + (axis_minor.coords[0][0] - axis_minor.coords[1][0]) * 0.5, axis_major.coords[0][1] + (axis_minor.coords[0][1] - axis_minor.coords[1][1]) * 0.5),
                                          (axis_major.coords[1][0] + (axis_minor.coords[0][0] - axis_minor.coords[1][0]) * 0.5, axis_major.coords[1][1] + (axis_minor.coords[0][1] - axis_minor.coords[1][1]) * 0.5))
                        minor_seg_plus = ((axis_minor.coords[0][0] + (axis_major.coords[0][0] - axis_major.coords[1][0]) * 0.5, axis_minor.coords[0][1] + (axis_major.coords[0][1] - axis_major.coords[1][1]) * 0.5),
                                          (axis_minor.coords[1][0] + (axis_major.coords[0][0] - axis_major.coords[1][0]) * 0.5, axis_minor.coords[1][1] + (axis_major.coords[0][1] - axis_major.coords[1][1]) * 0.5))



                        default_height = random.uniform(3.0, 4.0)
                        roof_height = roof_height if roof_height else default_height
                        roof = base.extrude_step(axis_line, roof_height).translate([0, 0, max_height]).material(roof_material)
                        '''

                    elif roof_shape == 'skillion':
                        # Attic
                        base = part.buffer(roof_buffer if pbuffered else 0)
                        orientation = "major"
                        if part.extra.get("osm:roof:orientation", "along") == "across": orientation = "minor"
                        (axis_major, axis_minor, axis_rot) = ddd.geomops.oriented_axis(base)

                        axis_major = axis_major.geom
                        axis_minor = axis_minor.geom

                        major_seg_plus = ((axis_major.coords[0][0] + (axis_minor.coords[0][0] - axis_minor.coords[1][0]) * 0.5, axis_major.coords[0][1] + (axis_minor.coords[0][1] - axis_minor.coords[1][1]) * 0.5),
                                          (axis_major.coords[1][0] + (axis_minor.coords[0][0] - axis_minor.coords[1][0]) * 0.5, axis_major.coords[1][1] + (axis_minor.coords[0][1] - axis_minor.coords[1][1]) * 0.5))
                        minor_seg_plus = ((axis_minor.coords[0][0] + (axis_major.coords[0][0] - axis_major.coords[1][0]) * 0.5, axis_minor.coords[0][1] + (axis_major.coords[0][1] - axis_major.coords[1][1]) * 0.5),
                                          (axis_minor.coords[1][0] + (axis_major.coords[0][0] - axis_major.coords[1][0]) * 0.5, axis_minor.coords[1][1] + (axis_major.coords[0][1] - axis_major.coords[1][1]) * 0.5))

                        skillion_line = major_seg_plus if orientation == "major" else minor_seg_plus
                        default_height = random.uniform(1.0, 2.0)
                        roof_height = roof_height if roof_height else default_height
                        roof = base.extrude_step(ddd.line(skillion_line), roof_height).translate([0, 0, max_height]).material(roof_material)

                    elif roof_shape == 'hipped':
                        # Attic
                        base = part.buffer(roof_buffer if pbuffered else 0)
                        orientation = "major"
                        if part.extra.get("osm:roof:orientation", "along") == "across": orientation = "minor"
                        (axis_major, axis_minor, axis_rot) = ddd.geomops.oriented_axis(base)
                        axis_line = axis_major if orientation == "major" else axis_minor
                        #other_axis_line = axis_minor if orientation == "major" else axis_major

                        axis_line = axis_line.intersection(axis_line.centroid().buffer(axis_minor.geom.length / 2, cap_style=ddd.CAP_ROUND, resolution=8))

                        default_height = random.uniform(1.0, 2.0)
                        roof_height = roof_height if roof_height else default_height
                        roof = base.extrude_step(axis_line, roof_height).translate([0, 0, max_height]).material(roof_material)

                    elif roof_shape == 'dome':
                        default_height = random.uniform(2.0, 4.0)
                        roof_height = roof_height if roof_height else default_height

                        roofbase = part.buffer(roof_buffer if pbuffered else 0, cap_style=2, join_style=2)
                        roof = roofbase.copy()

                        steps = 6
                        stepheight = 1.0 / steps
                        for i in range(steps):
                            stepy = (i + 1) * stepheight
                            stepx = math.sqrt(1 - (stepy ** 2))
                            stepbuffer = -(1 - stepx)
                            roof = roof.extrude_step(roofbase.buffer(stepbuffer * roof_height), stepheight * roof_height)
                        roof = roof.translate([0, 0, max_height]).material(roof_material)

                    elif roof_shape == 'none':
                        pass

                    else:
                        logger.warning("Unknown roof shape: %s", roof_shape)

                    if roof: building_3d.children.append(roof)

                except Exception as e:
                    logger.warning("Cannot generate roof: %s (geom: %s)" % (e, part.geom))

                # UV Mapping
                building_3d = ddd.uv.map_cubic(building_3d)

                entire_building_2d.append(part)
                entire_building_3d.append(building_3d)

            except ValueError as e:
                logger.error("Cannot generate building part %s: %s (geom: %s)" % (part, e, part.geom))
                raise
                #return None
            except IndexError as e:
                logger.error("Cannot generate building part %s: %s (geom: %s)" % (part, e, part.geom))
                raise
                #return None
            except Exception as e:
                logger.error("Cannot generate building part %s: %s (geom: %s)" % (part, e, part.geom))
                raise

        entire_building_3d.extra['building_2d'] = building_2d
        entire_building_3d.extra['ddd:building:feature'] = building_2d

        return entire_building_3d
예제 #5
0
def generate_area_2d_park(area, tree_density_m2=0.0025, tree_types=None):

    max_trees = None

    if tree_types is None:
        tree_types = {'default': 1}  #, 'palm': 0.001}

    #area = ddd.shape(feature["geometry"], name="Park: %s" % feature['properties'].get('name', None))
    feature = area.extra['osm:feature']
    area = area.material(ddd.mats.park)
    area.name = "Park: %s" % feature['properties'].get('name', None)
    area.extra['ddd:area:type'] = 'park'

    # Add trees if necesary
    # FIXME: should not check for None in intersects, filter shall not return None (empty group)

    trees = ddd.group2(name="Trees (Aug): %s" % area.name)

    align = area.get('ddd:aug:itemfill:align', 'noise')

    if area.geom:

        tree_area = area  # area.intersection(ddd.shape(osm.area_crop)).union()
        if tree_area.geom:

            if align == 'noise':

                # Decimation would affect after
                num_trees = int((tree_area.geom.area * tree_density_m2))
                #if num_trees == 0 and random.uniform(0, 1) < 0.5: num_trees = 1  # alone trees
                if max_trees:
                    num_trees = min(num_trees, max_trees)

                def filter_func_noise(coords):
                    val = noise.pnoise2(coords[0] * 0.1,
                                        coords[1] * 0.1,
                                        octaves=2,
                                        persistence=0.5,
                                        lacunarity=2,
                                        repeatx=1024,
                                        repeaty=1024,
                                        base=0)
                    return (val > random.uniform(-0.5, 0.5))

                for p in tree_area.random_points(num_points=num_trees):
                    tree_type = weighted_choice(tree_types)
                    tree = ddd.point(p, name="Tree")
                    tree.extra['ddd:aug:status'] = 'added'
                    tree.extra['osm:natural'] = 'tree'  # TODO: Change to DDD
                    tree.extra[
                        'osm:tree:type'] = tree_type  # TODO: Change to DDD
                    trees.append(tree)

            elif align == 'grid':

                # Give some margin for grid, and check the area is still a polygon
                tree_area = tree_area.buffer(-1.0)
                if tree_area.is_empty() or tree_area.geom.type != "Polygon":
                    return trees

                (major_seg, minor_seg,
                 angle) = ddd.geomops.oriented_axis(tree_area)

                # Decimation would affect this afterwards
                num_trees = int(
                    (tree_area.geom.minimum_rotated_rectangle.area *
                     tree_density_m2))
                major_minor_ratio = major_seg.geom.length / minor_seg.geom.length
                trees_major = int(
                    max(1,
                        math.sqrt(num_trees) * major_minor_ratio))
                trees_minor = int(
                    max(1,
                        math.sqrt(num_trees) * (1 / major_minor_ratio)))

                minor_seg_centered = minor_seg.recenter()
                for i in range(trees_major):
                    p_major = major_seg.geom.interpolate(
                        i * major_seg.geom.length /
                        (trees_major - 1 if trees_major > 1 else 1))
                    for j in range(trees_minor):
                        p_minor_offset = minor_seg_centered.geom.interpolate(
                            j * minor_seg_centered.geom.length /
                            (trees_minor - 1 if trees_minor > 1 else 1))
                        p = (p_major.coords[0][0] +
                             p_minor_offset.coords[0][0],
                             p_major.coords[0][1] +
                             p_minor_offset.coords[0][1])

                        if not tree_area.contains(ddd.point(p)):
                            continue

                        tree_type = weighted_choice(tree_types)
                        tree = ddd.point(p, name="Tree")
                        tree.extra['ddd:aug:status'] = 'added'
                        tree.extra[
                            'osm:natural'] = 'tree'  # TODO: Change to DDD
                        tree.extra[
                            'osm:tree:type'] = tree_type  # TODO: Change to DDD
                        trees.append(tree)

            else:
                raise DDDException("Invalid item align type: %s", align)

    return trees
예제 #6
0
    def generate_building_3d_part_roof(self, part):

        roof = None

        roof_shape = part.get('ddd:roof:shape')
        roof_height = parse_meters(part.get('ddd:roof:height'))

        floors = int(float(part.get('ddd:building:levels')))
        floors_min = int(float(part.get('ddd:building:min_level')))
        floor_0_height = part.get('ddd:building:level:0:height')

        roof_buffered = weighted_choice({True: 1, False: 5})
        roof_buffer = random.uniform(0.5, 1.2)
        roof_wall_material = weighted_choice({"stone": 3, "bricks": 1})
        pbuffered = roof_buffered

        floors_height = floor_0_height + (floors - 1) * 3.00
        floors_min_height = floors_min * 3.00
        min_height = float(part.extra.get('osm:min_height', floors_min_height))
        #max_height = parse_meters(part.extra.get('osm:height', floors_height + min_height)) - roof_height
        max_height = parse_meters(part.extra.get('osm:height',
                                                 floors_height)) - roof_height
        dif_height = max_height - min_height

        roof_material = ddd.mats.roof_tiles
        material_name = part.get('ddd:roof:material',
                                 part.get('osm:roof:material', None))
        if material_name:
            if hasattr(ddd.mats, material_name):
                roof_material = getattr(ddd.mats, material_name)

        if roof_shape == 'flat':
            # Flat
            default_height = 0.75
            roof_height = roof_height if roof_height else default_height
            roof = part.buffer(roof_buffer if pbuffered else 0,
                               cap_style=2,
                               join_style=2).extrude(roof_height).translate(
                                   [0, 0, max_height]).material(roof_material)

        elif roof_shape == 'terrace':
            # Flat
            usefence = random.uniform(0, 1) < 0.8
            if usefence:
                terrace = part.subtract(
                    part.buffer(-0.4)).extrude(0.6).translate([
                        0, 0, max_height
                    ]).material(getattr(ddd.mats, roof_wall_material))
                fence = part.buffer(-0.2).outline().extrude(
                    0.7).twosided().translate([0, 0, max_height + 0.6
                                               ]).material(ddd.mats.railing)
                roof = ddd.group3([terrace, fence], name="Roof")
            else:
                terrace = part.subtract(part.buffer(-0.4)).extrude(
                    random.uniform(0.40, 1.20)).translate(
                        [0, 0, max_height]).material(ddd.mats.stone)
                roof = ddd.group3([terrace], name="Roof")

        elif roof_shape == 'pyramidal':
            # Pointy
            default_height = floors * 0.2 + random.uniform(2.0, 5.0)
            roof_height = roof_height if roof_height else default_height
            roof = part.buffer(roof_buffer if pbuffered else 0,
                               cap_style=2,
                               join_style=2).extrude_step(
                                   part.centroid(), roof_height)
            roof = roof.translate([0, 0, max_height]).material(roof_material)

        elif roof_shape == 'attic':
            # Attic
            height = random.uniform(3.0, 4.0)
            roof = part.buffer(
                roof_buffer if pbuffered else 0, cap_style=2,
                join_style=2).extrude_step(
                    part.buffer(-2),
                    height,
                    method=ddd.EXTRUSION_METHOD_SUBTRACT).translate(
                        [0, 0, max_height]).material(roof_material)

        elif roof_shape == 'gabled':
            # Attic
            base = part.buffer(roof_buffer if pbuffered else 0)
            orientation = "major"
            if part.extra.get("osm:roof:orientation", "along") == "across":
                orientation = "minor"
            (axis_major, axis_minor,
             axis_rot) = ddd.geomops.oriented_axis(base)
            axis_line = axis_major if orientation == "major" else axis_minor
            default_height = random.uniform(3.0, 4.0)
            roof_height = roof_height if roof_height else default_height
            roof = base.extrude_step(axis_line, roof_height).translate(
                [0, 0, max_height]).material(roof_material)
            '''
            #elif roof_shape == 'round':
            # Attic
            base = part.buffer(roof_buffer if pbuffered else 0)
            orientation = "major"
            if part.extra.get("osm:roof:orientation", "along") == "across": orientation = "minor"
            (axis_major, axis_minor, axis_rot) = ddd.geomops.oriented_axis(base)
            axis_line = axis_major if orientation == "major" else axis_minor

            major_seg_plus = ((axis_major.coords[0][0] + (axis_minor.coords[0][0] - axis_minor.coords[1][0]) * 0.5, axis_major.coords[0][1] + (axis_minor.coords[0][1] - axis_minor.coords[1][1]) * 0.5),
                              (axis_major.coords[1][0] + (axis_minor.coords[0][0] - axis_minor.coords[1][0]) * 0.5, axis_major.coords[1][1] + (axis_minor.coords[0][1] - axis_minor.coords[1][1]) * 0.5))
            minor_seg_plus = ((axis_minor.coords[0][0] + (axis_major.coords[0][0] - axis_major.coords[1][0]) * 0.5, axis_minor.coords[0][1] + (axis_major.coords[0][1] - axis_major.coords[1][1]) * 0.5),
                              (axis_minor.coords[1][0] + (axis_major.coords[0][0] - axis_major.coords[1][0]) * 0.5, axis_minor.coords[1][1] + (axis_major.coords[0][1] - axis_major.coords[1][1]) * 0.5))



            default_height = random.uniform(3.0, 4.0)
            roof_height = roof_height if roof_height else default_height
            roof = base.extrude_step(axis_line, roof_height).translate([0, 0, max_height]).material(roof_material)
            '''

        elif roof_shape == 'skillion':
            # Attic
            base = part.buffer(roof_buffer if pbuffered else 0)
            orientation = "major"
            if part.extra.get("osm:roof:orientation", "along") == "across":
                orientation = "minor"
            (axis_major, axis_minor,
             axis_rot) = ddd.geomops.oriented_axis(base)

            axis_major = axis_major.geom
            axis_minor = axis_minor.geom

            major_seg_plus = (
                (axis_major.coords[0][0] +
                 (axis_minor.coords[0][0] - axis_minor.coords[1][0]) * 0.5,
                 axis_major.coords[0][1] +
                 (axis_minor.coords[0][1] - axis_minor.coords[1][1]) * 0.5),
                (axis_major.coords[1][0] +
                 (axis_minor.coords[0][0] - axis_minor.coords[1][0]) * 0.5,
                 axis_major.coords[1][1] +
                 (axis_minor.coords[0][1] - axis_minor.coords[1][1]) * 0.5))
            minor_seg_plus = (
                (axis_minor.coords[0][0] +
                 (axis_major.coords[0][0] - axis_major.coords[1][0]) * 0.5,
                 axis_minor.coords[0][1] +
                 (axis_major.coords[0][1] - axis_major.coords[1][1]) * 0.5),
                (axis_minor.coords[1][0] +
                 (axis_major.coords[0][0] - axis_major.coords[1][0]) * 0.5,
                 axis_minor.coords[1][1] +
                 (axis_major.coords[0][1] - axis_major.coords[1][1]) * 0.5))

            skillion_line = major_seg_plus if orientation == "major" else minor_seg_plus

            # Create a 1 unit height flat roof and then calculate height along the skillion direction line for the top half vertices
            roof = base.extrude(1.0)
            #skillion_line = ddd.line(skillion_line)
            #roof = base.extrude_step(skillion_line, roof_height).translate([0, 0, max_height]).material(roof_material)
            default_height = random.uniform(1.0, 2.0)
            roof_height = roof_height if roof_height else default_height
            roof = roof.translate([0, 0, max_height]).material(roof_material)

        elif roof_shape == 'hipped':

            # TODO:
            #  https://gis.stackexchange.com/questions/136143/how-to-compute-straight-skeletons-using-python
            #  https://scikit-geometry.github.io/scikit-geometry/skeleton.html

            # Attic
            base = part.buffer(roof_buffer if pbuffered else 0)
            orientation = "major"
            if part.extra.get("osm:roof:orientation", "along") == "across":
                orientation = "minor"
            (axis_major, axis_minor,
             axis_rot) = ddd.geomops.oriented_axis(base)
            axis_line = axis_major if orientation == "major" else axis_minor
            #other_axis_line = axis_minor if orientation == "major" else axis_major

            axis_line = axis_line.intersection(axis_line.centroid().buffer(
                axis_minor.geom.length / 2,
                cap_style=ddd.CAP_ROUND,
                resolution=8))

            default_height = random.uniform(1.0, 2.0)
            roof_height = roof_height if roof_height else default_height
            roof = base.extrude_step(axis_line, roof_height).translate(
                [0, 0, max_height]).material(roof_material)

        elif roof_shape == 'dome':
            default_height = random.uniform(2.0, 4.0)
            roof_height = roof_height if roof_height else default_height

            roofbase = part.buffer(roof_buffer if pbuffered else 0,
                                   cap_style=2,
                                   join_style=2)
            roof = roofbase.copy()

            steps = 6
            stepheight = 1.0 / steps
            for i in range(steps):
                stepy = (i + 1) * stepheight
                stepx = math.sqrt(1 - (stepy**2))
                stepbuffer = -(1 - stepx)
                roof = roof.extrude_step(
                    roofbase.buffer(stepbuffer * roof_height),
                    stepheight * roof_height)
            roof = roof.translate([0, 0, max_height]).material(roof_material)

        #elif
        # Reminder: https://scikit-geometry.github.io/scikit-geometry/skeleton.html
        #  https://gis.stackexchange.com/questions/136143/how-to-compute-straight-skeletons-using-python

        elif roof_shape == 'none':
            pass

        else:
            logger.warning("Unknown roof shape: %s", roof_shape)

        if roof:
            roof = roof.smooth(math.pi / 12)
            roof = ddd.uv.map_cubic(roof, split=True)

        return roof