Example #1
0
def osm_structured_export_2d_tile(root, osm, pipeline):
    """Save a cropped tileable 2D image of the scene."""

    tile = ddd.group2([
        ddd.shape(osm.area_crop).material(ddd.material(color='#ffffff')),  # White background (?)
        #self.ground_2d,
        root.select(path="/Water", recurse=False),
        root.select(path="/Areas", recurse=False),
        root.select(path="/Ways", recurse=False),  #, select="")  self.ways_2d['-1a'], self.ways_2d['0'], self.ways_2d['0a'], self.ways_2d['1'],
        root.select(path="/Roadlines2", recurse=False),
        root.select(path="/Buildings", recurse=False),
        #self.areas_2d_objects, self.buildings_2d.material(ddd.material(color='#8a857f')),
        root.select(path="/ItemsAreas", recurse=False),  #self.items_2d,
        root.select(path="/ItemsWays", recurse=False),  #self.items_2d,
        root.select(path="/ItemsNodes", recurse=False).buffer(0.5).material(ddd.mats.red),

    ]).flatten().select(func=lambda o: o.extra.get('ddd:area:type') != 'underwater')

    tile = tile.intersection(ddd.shape(osm.area_crop))
    tile = tile.clean()
    tile.prop_set('svg:stroke-width', 0.01, children=True)

    path = pipeline.data['filenamebase'] + ".png"
    tile.save(path)

    tile.save("/tmp/osm-structured.png")
Example #2
0
    def __init__(self,
                 features=None,
                 area_filter=None,
                 area_crop=None,
                 osm_proj=None,
                 ddd_proj=None):

        self.catalog = PrefabCatalog()

        self.items = ItemsOSMBuilder(self)
        self.items2 = AreaItemsOSMBuilder(self)
        self.ways1 = Ways1DOSMBuilder(self)
        self.ways2 = Ways2DOSMBuilder(self)
        self.ways3 = Ways3DOSMBuilder(self)
        self.areas2 = Areas2DOSMBuilder(self)
        self.areas3 = Areas3DOSMBuilder(self)
        self.buildings = BuildingOSMBuilder(self)
        self.customs = CustomsOSMBuilder(self)

        self.osmops = OSMBuilderOps(self)

        self.area_filter = area_filter
        self.area_crop = area_crop

        self.area_filter2 = ddd.shape(self.area_filter)
        self.area_crop2 = ddd.shape(self.area_crop)

        #self.simplify_tolerance = 0.01

        self.layer_indexes = ('-2', '-1', '0', '1', '2', '3', '-2a', '-1a',
                              '0a', '1a')

        self.layer_heights = {
            '-2': -12.0,
            '-1': -6.0,
            '0': 0.0,
            '1': 6.0,
            '2': 12.0,
            '3': 18.0,
            #'-2a': -9.0, '-1a': -2.5, '0a': 3.0, '1a': 9.0}
            #'-2a': -12.0, '-1a': -5.0, '0a': 0.0, '1a': 6.0}
            '-2a': 0.0,
            '-1a': 0.0,
            '0a': 0.0,
            '1a': 0.0
        }

        self.webmercator_proj = pyproj.Proj(init='epsg:3857')
        self.osm_proj = osm_proj  # 4326
        self.ddd_proj = ddd_proj

        self.features = features if features else []
        self.features_2d = ddd.group2(name="Features")
Example #3
0
    def preprocess_features(self):
        """
        Corrects inconsistencies and adapts OSM data for generation.
        """

        # Correct layers
        for f in self.features:
            f.properties['id'] = f.properties['id'].replace("/", "-")
            if f.properties.get(
                    'tunnel',
                    None) == 'yes' and f.properties.get('layer', None) is None:
                f.properties['layer'] = "-1"
            if f.properties.get(
                    'brige',
                    None) == 'yes' and f.properties.get('layer', None) is None:
                f.properties['layer'] = "1"
            if f.properties.get('layer', None) is None:
                f.properties['layer'] = "0"

            # Create feature objects
            defaultname = f.geometry.type  # "Feature"
            name = f.properties.get('name', defaultname)
            osmid = f.properties.get('id', None)
            if osmid is not None:
                name = "%s_(%s)" % (name, osmid)

            feature_2d = ddd.shape(f.geometry, name=name)
            feature_2d.extra['osm:feature'] = f
            feature_2d.extra['osm:feature_2d'] = feature_2d
            feature_2d.extra['osm:element'] = f.properties['id'].split("-")[0]
            for k, v in f.properties.items():
                feature_2d.extra['osm:' + k] = v

            # We do not validate or clean here as linestrings may crose themselves. Simply remove empty geometries.

            # Crop to area filter
            try:
                feature_2d.validate()
            except Exception as e:
                logger.debug("Invalid feature (1/2) '%s': %s", name, e)
                try:
                    feature_2d = feature_2d.intersection(self.area_filter2)
                    #feature_2d.clean(eps=0.01)
                    feature_2d.validate()
                except Exception as e:
                    logger.warn("Invalid feature (2/2) '%s' %s: %s", name,
                                feature_2d.metadata("", ""), e)
                    continue

            # Separate GeometryCollection geometries
            if feature_2d.geom.type == "GeometryCollection":
                logger.info("Splitting GeometryCollection: %s", feature_2d)
                for f in feature_2d.individualize().children:
                    f.extra['osm:feature_2d'] = f
                    f = f.intersection(self.area_filter2)
                    #f = f.clean(eps=0.0)
                    f.validate()
                    self.features_2d.append(f)
            else:
                self.features_2d.append(feature_2d)
Example #4
0
    def generate_custom_1d(self, feature):

        otype = 'checkpoint'
        layer = feature.properties.get('ddd:layer')

        if otype == 'checkpoint':
            race = layer
            idx = int(feature.properties.get('fid'))
            item = ddd.shape(feature['geometry'],
                             name="Checkpoint %d (race): %s" % (idx, race))
            item.extra['checkpoint_idx'] = idx
            item.extra['type'] = otype
        else:
            logger.warn("Unknown custom feature: %s", feature)
            return

        return item
Example #5
0
    def preprocess_buildings_features(self, features_2d):

        logger.info("Preprocessing buildings and bulding parts (2D)")
        # TODO: create nested buildings them separately, consider them part of the bigger building for subtraction)

        # Assign each building part to a building, or transform it into a building if needed
        #features = sorted(features_2d.children, key=lambda f: f.geom.area)
        features_2d_original = list(features_2d.children)
        for feature in list(features_2d.children):
            if feature.geom.type == 'Point': continue

            if feature.extra.get('osm:building:part', None) is None and feature.extra.get('osm:building', None) is None: continue

            # Find building
            #buildings = features_2d.select(func=lambda o: o.extra.get('osm:building', None) and ddd.polygon(o.geom.exterior.coords).contains(feature))
            buildings = features_2d.select(func=lambda o: o.extra.get('osm:building', None) and o != feature and o.contains(feature) and o in features_2d_original)

            if len(buildings.children) == 0:
                if feature.extra.get('osm:building', None) is None:
                    logger.warn("Building part with no building: %s", feature)
                    building = ddd.shape(feature.geom, name="Building (added): %s" % feature.name)
                    building.extra['osm:building'] = feature.extra.get('osm:building:part', 'yes')
                    building.extra['ddd:building:parts'] = [feature]
                    feature.extra['ddd:building:parent'] = building
                    features_2d.append(building)

            elif len(buildings.children) > 1:
                # Sort by area and get the smaller one
                buildings.children.sort(key=lambda b: b.geom.area, reverse=False)
                logger.warn("Building part with multiple buildings: %s -> %s", feature, buildings.children)

                feature.extra['ddd:building:parent'] = buildings.children[0]
                if 'ddd:building:parts' not in buildings.children[0].extra:
                    buildings.children[0].extra['ddd:building:parts'] = []
                buildings.children[0].extra['ddd:building:parts'].append(feature)

            else:
                logger.debug("Associating building part to building: %s -> %s", feature, buildings.children[0])
                feature.extra['ddd:building:parent'] = buildings.children[0]
                if 'ddd:building:parts' not in buildings.children[0].extra:
                    buildings.children[0].extra['ddd:building:parts'] = []
                buildings.children[0].extra['ddd:building:parts'].append(feature)
Example #6
0
    def generate_ways_3d_subways(self):
        """
        Generates boxing for sub ways.
        """
        logger.info("Generating subways.")
        logger.warn("IMPLEMENT 2D/3D separation for this, as it needs to be cropped, and it's being already cropped earlier")

        # Take roads
        ways = [w for w in self.osm.ways_2d["-1a"].children] + [w for w in self.osm.ways_2d["-1"].children]

        union = self.osm.ways_2d["-1"].union()
        union_with_transitions = ddd.group(ways, empty="2").union()
        union_sidewalks = union_with_transitions.buffer(0.6, cap_style=2, join_style=2)

        sidewalks_2d = union_sidewalks.subtract(union_with_transitions)  # we include transitions
        walls_2d = sidewalks_2d.buffer(0.5, cap_style=2, join_style=2).subtract(union_sidewalks)
        floors_2d = union_sidewalks.copy()
        ceilings_2d = union.buffer(0.6, cap_style=2, join_style=2).subtract(self.osm.ways_2d["-1a"])

        # FIXME: Move cropping to generic site, use interintermediatemediate osm.something for storage
        crop = ddd.shape(self.osm.area_crop)
        sidewalks_2d = sidewalks_2d.intersection(crop)
        walls_2d = walls_2d.intersection(crop)
        floors_2d = floors_2d.intersection(crop)
        ceilings_2d = ceilings_2d.intersection(crop)

        sidewalks_3d = sidewalks_2d.extrude(0.3).translate([0, 0, -5]).material(ddd.mats.sidewalk)
        walls_3d = walls_2d.extrude(5).translate([0, 0, -5]).material(ddd.mats.cement)
        #floors_3d = floors_2d.extrude(-0.3).translate([0, 0, -5]).material(ddd.mats.sidewalk)
        floors_3d = floors_2d.triangulate().translate([0, 0, -5]).material(ddd.mats.sidewalk)
        ceilings_3d = ceilings_2d.extrude(0.5).translate([0, 0, -1.0]).material(ddd.mats.cement)

        sidewalks_3d = terrain.terrain_geotiff_elevation_apply(sidewalks_3d, self.osm.ddd_proj)
        sidewalks_3d = ddd.uv.map_cubic(sidewalks_3d)
        walls_3d = terrain.terrain_geotiff_elevation_apply(walls_3d, self.osm.ddd_proj)
        walls_3d = ddd.uv.map_cubic(walls_3d)
        floors_3d = terrain.terrain_geotiff_elevation_apply(floors_3d, self.osm.ddd_proj)
        ceilings_3d = terrain.terrain_geotiff_elevation_apply(ceilings_3d, self.osm.ddd_proj)
        ceilings_3d = ddd.uv.map_cubic(ceilings_3d)

        subway = ddd.group([sidewalks_3d, walls_3d, floors_3d, ceilings_3d], empty=3).translate([0, 0, -0.2])
        self.osm.other_3d.children.append(subway)
Example #7
0
def osm_groups_areas(root, osm, obj, logger):

    item = obj.copy(name="Area: %s" % obj.name)

    try:
        area = item.individualize().flatten()
        area.validate()
    except DDDException as e:
        logger.warn("Invalid geometry (cropping area) for area %s (%s): %s", area, area.extra, e)
        try:
            area = area.clean(eps=0.001).intersection(ddd.shape(osm.area_crop))
            area = area.individualize().flatten()
            area.validate()
        except DDDException as e:
            logger.warn("Invalid geometry (ignoring area) for area %s (%s): %s", area, area.extra, e)
            return

    for a in area.children:
        if a.geom:
            a.extra['ddd:area:area'] = a.geom.area
            root.find("/Areas").append(a)
Example #8
0
    def generate_coastline_2d(self, area_crop):
        logger.info(
            "Generating water and land areas according to coastline: %s",
            (area_crop.bounds))

        #self.water_3d = terrain.terrain_grid(self.area_crop.bounds, height=0.1, detail=200.0).translate([0, 0, 1]).material(mat_sea)

        water = ddd.rect(area_crop.bounds, name="Coastline Water")

        coastlines = []
        coastlines_1d = []

        for way in self.osm.features_2d.children:
            if way.extra.get('osm:natural') == 'coastline':
                original = way.extra[
                    'osm:original']  # Original has not been cropped (cropped verison is not valid for this)
                coastlines_1d.append(original)
                coastlines.append(original.buffer(0.01))

        #for way in self.osm.features.children:
        #    if way.properties.get('natural') == 'coastline':
        #        coastlines_1d.append(ddd.shape(way.geometry))
        #        coastlines.append(ddd.shape(way.geometry).buffer(0.1))

        if not coastlines:
            logger.info("No coastlines in the feature set.")
            return

        coastlines_1d = ddd.group(coastlines_1d)  #.individualize().flatten()
        coastlines = ddd.group(coastlines)  # .individualize().flatten()
        coastline_areas = water.subtract(coastlines)

        logger.info("Coastlines: %s", (coastlines_1d, ))
        logger.info("Coastline areas: %s", (coastline_areas, ))

        # Generate coastline edge
        if coastlines_1d.children:
            coastlines_2d = coastlines_1d.intersection(water)
            coastlines_2d = coastlines_2d.individualize()
            #coastlines_3d = coastlines_2d.extrude(10.0).translate([0, 0, -10.0])
            #coastlines_3d = terrain.terrain_geotiff_elevation_apply(coastlines_3d, self.osm.ddd_proj)
            #coastlines_3d = ddd.uv.map_cubic(coastlines_3d)
            #coastlines_3d.name = 'Coastline: %s' % coastlines_3d.name
            #self.osm.other_3d.append(coastlines_3d)

        areas_2d = []
        #geoms = coastline_areas.geom.geoms if coastline_areas.geom.type == 'MultiPolygon' else [coastline_areas.geom]
        for water_area_geom in coastline_areas.individualize().flatten().clean(
        ).children:
            # Find closest point, closest segment, and angle to closest segment
            #water_area_geom = ddd.shape(water_area_geom).clean(eps=0.01).geom

            #if not water_area_geom.is_simple:
            #    logger.error("Invalid water area geometry (not simple): %s", water_area_geom)
            #    continue

            if not water_area_geom.geom: continue

            #water_area_geom.dump()
            coastlines_1d = coastlines_1d.outline().clean()

            water_area_point = water_area_geom.geom.representative_point()
            p, segment_idx, segment_coords_a, segment_coords_b, closest_obj, closest_d = coastlines_1d.closest_segment(
                ddd.shape(water_area_point))
            pol = LinearRing([
                segment_coords_a, segment_coords_b,
                (water_area_point.coords[0][0], water_area_point.coords[0][1],
                 0)
            ])

            if not pol.is_ccw:
                #area_3d = area_2d.extrude(-0.2)
                area_2d = ddd.shape(
                    water_area_geom.geom).buffer(0.10).clean(eps=0.01)
                area_2d.validate()
                area_2d = area_2d.material(ddd.mats.sea)
                area_2d.extra['ddd:area:type'] = 'sea'  # not treated as sea
                area_2d.extra['ddd:area:elevation'] = 'none'
                area_2d.extra['ddd:height'] = 0
                area_2d.extra['ddd:collider'] = False
                area_2d.extra['ddd:shadows'] = False
                area_2d.extra['ddd:occluder'] = False

                #area_3d = area_2d.triangulate().translate([0, 0, -0.5])
                areas_2d.append(area_2d)
                #areas.append(area_3d)

        return ddd.group2(areas_2d, name="Water")
Example #9
0
    def generate_ways_3d_elevated(self):

        logger.info("Generating elevated ways.")
        logger.warn(
            "IMPLEMENT 2D/3D separation for this, as it needs to be cropped")

        elevated = []

        # Walk roads
        ways = ([w for w in self.osm.ways_2d["1"].children] +
                [w for w in self.osm.ways_2d["0a"].children] +
                [w for w in self.osm.ways_2d["-1a"].children])
        # ways_union = ddd.group(ways).union()

        sidewalk_width = 0.4

        elevated_union = DDDObject2()
        for way in ways:
            # way_longer = way.buffer(0.3, cap_style=1, join_style=2)

            if 'intersection' in way.extra: continue

            way_with_sidewalk_2d = way.buffer(sidewalk_width,
                                              cap_style=2,
                                              join_style=2)
            #way_with_sidewalk_2d_extended = osmops.extend_way(way).buffer(sidewalk_width, cap_style=2, join_style=2)
            sidewalk_2d = way_with_sidewalk_2d.subtract(way).material(
                ddd.mats.sidewalk)
            wall_2d = way_with_sidewalk_2d.buffer(
                0.25, cap_style=2,
                join_style=2).subtract(way_with_sidewalk_2d).buffer(
                    0.001, cap_style=2, join_style=2).material(ddd.mats.cement)
            floor_2d = way_with_sidewalk_2d.buffer(
                0.3, cap_style=2,
                join_style=2).buffer(0.001, cap_style=2,
                                     join_style=2).material(ddd.mats.cement)

            sidewalk_2d.extra['way_2d'] = way
            wall_2d.extra['way_2d'] = way
            floor_2d.extra['way_2d'] = way

            # Get connected ways
            connected = self.osm.ways1.follow_way(way.extra['way_1d'], 1)
            connected_2d = ddd.group(
                [self.osm.ways2.get_way_2d(c) for c in connected])
            if 'intersection_start_2d' in way.extra['way_1d'].extra:
                connected_2d.append(
                    way.extra['way_1d'].extra['intersection_start_2d'])
            if 'intersection_end_2d' in way.extra['way_1d'].extra:
                connected_2d.append(
                    way.extra['way_1d'].extra['intersection_end_2d'])
            # print(connected)

            sidewalk_2d = sidewalk_2d.subtract(connected_2d).buffer(0.001)
            wall_2d = wall_2d.subtract(connected_2d.buffer(sidewalk_width))
            # TODO: Subtract floors from connected or resolve intersections
            wall_2d = wall_2d.subtract(elevated_union)

            # FIXME: Move cropping to generic site, use itermediate osm.something for storage
            crop = ddd.shape(self.osm.area_crop)
            sidewalk_2d = sidewalk_2d.intersection(
                crop.buffer(-0.003)).clean(eps=0.01)
            wall_2d = wall_2d.intersection(crop.buffer(-0.003)).clean(eps=0.01)
            floor_2d = floor_2d.intersection(
                crop.buffer(-0.003)).clean(eps=0.01)

            #FIXME: TODO: this shal be done earlier, before generating the path
            #if way.extra.get('ddd:way:elevated:material', None):
            #    way.extra['way_2d'].material(way.extra.get('ddd:way:elevated:material'))

            # ddd.group((sidewalk_2d, wall_2d)).show()
            if way.extra.get('ddd:way:elevated:border', None) == 'fence':
                fence_2d = wall_2d.outline().clean()
                fence_2d = fence_2d.material(ddd.mats.fence)
                #fence_2d.dump()
                #wall_2d.show()
                fence_2d.extra['ddd:item'] = True
                fence_2d.extra['ddd:item:height'] = 1.2
                fence_2d.extra['ddd:height'] = 1.2
                fence_2d.extra['barrier'] = "fence"
                fence_2d.extra[
                    '_height_mapping'] = "terrain_geotiff_and_path_apply"
                fence_2d.extra['way_1d'] = way.extra['way_1d']

                self.osm.items_1d.append(fence_2d)
                elevated.append((sidewalk_2d, None, floor_2d))
            else:
                elevated.append((sidewalk_2d, wall_2d, floor_2d))

            elevated_union = elevated_union.union(
                ddd.group([sidewalk_2d, wall_2d, floor_2d]))

            # Bridge piers
            path = way.extra['way_1d']
            if path.geom.length > 15.0:  # and path.extra['ddd:bridge:posts']:
                # Generate posts
                interval = 35.0
                length = path.geom.length
                numposts = int(length / interval)
                idx = 0

                logger.debug("Piers for bridge (length=%s, num=%d, way=%s)",
                             length, numposts, way)
                for d in numpy.linspace(0.0, length, numposts, endpoint=False):
                    if d == 0.0: continue

                    # Calculate left and right perpendicular intersections with sidewalk, park, land...
                    p, segment_idx, segment_coords_a, segment_coords_b = path.interpolate_segment(
                        d)

                    # FIXME: Use items and crop in a generic way (same for subways) (so ignore below in common etc)
                    if not self.osm.area_crop.contains(ddd.point(p).geom):
                        continue

                    dir_vec = (segment_coords_b[0] - segment_coords_a[0],
                               segment_coords_b[1] - segment_coords_a[1])
                    dir_vec_length = math.sqrt(dir_vec[0]**2 + dir_vec[1]**2)
                    dir_vec = (dir_vec[0] / dir_vec_length,
                               dir_vec[1] / dir_vec_length)
                    angle = math.atan2(dir_vec[1], dir_vec[0])

                    idx = idx + 1

                    if len(p) < 3:
                        logger.error(
                            "Bridge path with less than 3 components when building bridge piers."
                        )
                        continue

                    if p[2] > 1.0:  # If no height, no pilar, but should be a margin and also corrected by base_height
                        item = ddd.rect([
                            -way.extra['ddd:way:width'] * 0.3, -0.5,
                            way.extra['ddd:way:width'] * 0.3, 0.5
                        ],
                                        name="Bridge Post %s" % way.name)
                        item = item.extrude(-(math.fabs(p[2]) - 0.5)).material(
                            ddd.mats.cement)
                        item = ddd.uv.map_cubic(item)
                        item = item.rotate([0, 0, angle - math.pi / 2
                                            ]).translate([p[0], p[1], 0])
                        vertex_func = self.get_height_apply_func(path)
                        item = item.vertex_func(vertex_func)
                        item = terrain.terrain_geotiff_elevation_apply(
                            item, self.osm.ddd_proj)
                        item = item.translate([0, 0, -0.8])
                        item.extra['way_2d'] = way
                        item.extra['ddd:bridge:post'] = True
                        self.osm.other_3d.children.append(item)

        elevated_3d = []
        for item in elevated:
            sidewalk_2d, wall_2d, floor_2d = item
            sidewalk_3d = sidewalk_2d.extrude(0.2).translate([0, 0, -0.2])
            wall_3d = wall_2d.extrude(0.6) if wall_2d else None
            floor_3d = floor_2d.extrude(-0.5).translate([0, 0, -0.2])
            # extra_height = way_2d.extra['extra_height']
            # way_3d = way_2d.extrude(-0.2 - extra_height).translate([0, 0, extra_height])  # + layer_height

            sidewalk_3d = ddd.uv.map_cubic(sidewalk_3d)
            wall_3d = ddd.uv.map_cubic(wall_3d) if wall_3d else None
            floor_3d = ddd.uv.map_cubic(floor_3d)

            elevated_3d.append(sidewalk_3d)
            elevated_3d.append(floor_3d)
            if wall_3d:
                elevated_3d.append(wall_3d)

        # Raise items to their way height position
        nitems = []
        for item in elevated_3d:
            # print(item.extra)
            path = item.extra['way_1d']
            vertex_func = self.osm.ways1.get_height_apply_func(path)
            nitem = item.vertex_func(vertex_func)
            nitems.append(nitem)

        result = ddd.group(nitems, empty=3)
        result = terrain.terrain_geotiff_elevation_apply(
            result, self.osm.ddd_proj)
        self.osm.other_3d.children.append(result)
Example #10
0
        #ddd.shape(osm.area_crop).material(ddd.material(color='#ffffff')),  # White background (?)
        #self.ground_2d,
        root.select(path="/Water", recurse=False),
        root.select(path="/Areas", recurse=False),
        root.select(path="/Ways", recurse=False),  #, select="")  self.ways_2d['-1a'], self.ways_2d['0'], self.ways_2d['0a'], self.ways_2d['1'],
        root.select(path="/Buildings", recurse=False),
        #root.select(path="/Roadlines2", recurse=False),
        #root.select(path="/ItemsAreas", recurse=False),  #self.items_2d,
        #root.select(path="/ItemsWays", recurse=False),  #self.items_2d,
        #root.select(path="/ItemsNodes", recurse=False).buffer(0.5).material(ddd.mats.red),

    ]).flatten().select(func=lambda o: o.extra.get('ddd:area:type') != 'underwater')

    '''
    # Save a cropped tileable idmap image of the scene.
    tile = tile.intersection(ddd.shape(osm.area_crop))
    tile = tile.clean()
    tile.prop_set('svg:stroke-width', 0, children=True)  # 0.01,

    path = pipeline.data['filenamebase'] + ".idmap.png"
    tile.save("/tmp/osm-idmap.png")
    #tile.save(path)
    '''

    SPLAT_NONE =        0x00
    SPLAT_UNKNOWN =     0x08

    # Spatial index

    rtree = STRtree(tile.geom_recursive())