Example #1
0
    def __init__(self, pbf_path):
        osmium.SimpleHandler.__init__(self)
        self.wkbfab = osmium.geom.WKBFactory()
        self.df = []
        self.road_types = [
            'motorway', 'trunk', 'primary', 'secondary', 'tertiary', 'road',
            'residential', 'service', 'motorway_link', 'trunk_link',
            'primary_link', 'secondary_link', 'tertiary_link'
        ]

        print(f'loading {pbf_path}...')
        self.apply_file(pbf_path, locations=True)
        cols = ['way_id', 'nodes', 'line', 'line_length', 'name', 'maxspeed']
        self.df = pd.DataFrame(self.df, columns=cols).set_index('way_id')
        not_numeric_flag = ~self.df['maxspeed'].astype(str).str.isnumeric()
        self.df.loc[not_numeric_flag, 'maxspeed'] = '0'
        self.df['maxspeed'] = self.df['maxspeed'].astype(int)
        print('creating spatial index...')

        # Populate R-tree index with bounds of grid cells
        self.r_tree = index.Index()
        pols = []
        for way_id, row in self.df.iterrows():
            p = Polygon(row['line'].buffer(.00005).exterior.coords)
            p.maxspeed = row['maxspeed']
            p.way_id = way_id
            p.name = row['name']
            pols.append(p)

            self.r_tree.insert(way_id, p.bounds)

        self.df = gpd.GeoDataFrame(self.df, geometry=pols)
        print(f'finished')
Example #2
0
    def _rect_grid_to_shape_list(self):
        """
        internal method, convert structured grid to list of shapely polygons

        Returns
        -------
        list
            list of shapely Polygons

        """
        if shapely is None:
            msg = 'GridIntersect()._rect_grid_to_shape_list(): error ' + \
                  'importing shapely - try "pip install shapely"'
            raise ImportError(msg)
        else:
            from shapely.geometry import Polygon

        shplist = []
        for i in range(self.mfgrid.nrow):
            for j in range(self.mfgrid.ncol):
                xy = self.mfgrid.get_cell_vertices(i, j)
                p = Polygon(xy)
                p.name = (i, j)
                shplist.append(p)
        return shplist
Example #3
0
 def parse_polygon(self, name, description):
     _points = description['corners']
     for points in itertools.permutations(_points):
         polygon = Polygon(points)
         polygon.name = name
         if polygon.is_valid:
             return polygon
Example #4
0
    def _vtx_grid_to_shape_list(self):
        """
        internal method, convert vertex grid to list of shapely polygons

        Returns
        -------
        list
            list of shapely Polygons

        """
        if shapely is None:
            msg = 'GridIntersect()._vtx_grid_to_shape_list(): error ' + \
                  'importing shapely - try "pip install shapely"'
            raise ImportError(msg)
        else:
            from shapely.geometry import Polygon

        shplist = []
        if isinstance(self.mfgrid._cell2d, np.recarray):
            for icell in self.mfgrid._cell2d.icell2d:
                points = []
                for iv in self.mfgrid._cell2d[[
                        "icvert_0", "icvert_1", "icvert_2"
                ]][icell]:
                    points.append((self.mfgrid._vertices.xv[iv],
                                   self.mfgrid._vertices.yv[iv]))
                # close the polygon, if necessary
                if points[0] != points[-1]:
                    points.append(points[0])
                p = Polygon(points)
                p.name = icell
                shplist.append(p)
        elif isinstance(self.mfgrid._cell2d, list):
            for icell in range(len(self.mfgrid._cell2d)):
                points = []
                for iv in self.mfgrid._cell2d[icell][-3:]:
                    points.append((self.mfgrid._vertices[iv][1],
                                   self.mfgrid._vertices[iv][2]))
                # close the polygon, if necessary
                if points[0] != points[-1]:
                    points.append(points[0])
                p = Polygon(points)
                p.name = icell
                shplist.append(p)
        return shplist
 def addShapeAsHole(self, name='undefined'):
     """Adds a hole in the shape of the current shape with the current position"""
     newhole = Polygon(self.getShapeOriented())
     if self.convex_hull:
         newhole = newhole.convex_hull
     newhole.shape_nfps = defaultdict()
     newhole.name = name
     newhole.position = self.position
     newhole.angle = self.angle
     newhole.origin = affinity.rotate(Point(-self.circle_center[0], -self.circle_center[1]), self.angle, origin=(0,0))
     newhole.origin = affinity.translate(newhole.origin, self.position[0], self.position[1])
     self.hole_shapes.append(newhole)
Example #6
0
    def _vtx_grid_to_shape_list(self):
        """
        internal method, convert vertex grid to list of shapely polygons

        Returns
        -------
        list
            list of shapely Polygons

        """

        shplist = []
        if isinstance(self.mfgrid._cell2d, np.recarray):
            for icell in self.mfgrid._cell2d.icell2d:
                points = []
                icverts = ["icvert_{}".format(i) for i in
                           range(self.mfgrid._cell2d["ncvert"][icell])]
                for iv in self.mfgrid._cell2d[icverts][icell]:
                    points.append((self.mfgrid._vertices.xv[iv],
                                   self.mfgrid._vertices.yv[iv]))
                # close the polygon, if necessary
                if points[0] != points[-1]:
                    points.append(points[0])
                p = Polygon(points)
                p.name = icell
                shplist.append(p)
        elif isinstance(self.mfgrid._cell2d, list):
            for icell in range(len(self.mfgrid._cell2d)):
                points = []
                for iv in self.mfgrid._cell2d[icell][-3:]:
                    points.append((self.mfgrid._vertices[iv][1],
                                   self.mfgrid._vertices[iv][2]))
                # close the polygon, if necessary
                if points[0] != points[-1]:
                    points.append(points[0])
                p = Polygon(points)
                p.name = icell
                shplist.append(p)
        return shplist
Example #7
0
    def _rect_grid_to_shape_list(self):
        """internal method, convert structured grid to list of
        shapely polygons

        Returns
        -------
        list
            list of shapely Polygons

        """
        shplist = []
        for i in range(self.mfgrid.nrow):
            for j in range(self.mfgrid.ncol):
                xy = self.mfgrid.get_cell_vertices(i, j)
                p = Polygon(xy)
                p.name = (i, j)
                shplist.append(p)
        return shplist
Example #8
0
    def from_shape(cls,
                   shape,
                   height=0.,
                   name="area",
                   properties=None,
                   unit='um',
                   min_x=None,
                   max_x=None):
        '''
        Create an :class:`Area` from a :class:`Shape` object.

        Parameters
        ----------
        shape : :class:`Shape`
            Shape that should be converted to an Area.

        Returns
        -------
        :class:`Area` object.
        '''
        if _unit_support:
            from .units import Q_
            if isinstance(height, Q_):
                height = height.m_as(unit)
            if isinstance(min_x, Q_):
                min_x = min_x.m_as(unit)
            if isinstance(max_x, Q_):
                max_x = max_x.m_as(unit)

        obj = None
        g_type = None

        if isinstance(shape, MultiPolygon):
            g_type = "MultiPolygon"
        elif isinstance(shape, (Polygon, Shape, Area)):
            g_type = "Polygon"
        else:
            raise TypeError("Expected a Polygon or MultiPolygon object.")

        # find the scaling factor
        scaling = 1.
        if None not in (min_x, max_x):
            ext = np.array(shape.exterior.coords)
            leftmost = np.min(ext[:, 0])
            rightmost = np.max(ext[:, 0])
            scaling = (max_x - min_x) / (rightmost - leftmost)
            obj = scale(shape, scaling, scaling)
        else:
            if g_type == "Polygon":
                obj = Polygon(shape)
            else:
                obj = MultiPolygon(shape)

        obj.__class__ = cls
        obj._parent = None
        obj._unit = unit
        obj._geom_type = g_type
        obj.__class__ = Area
        obj._areas = None
        obj.height = height
        obj.name = name
        obj._prop = _PDict({} if properties is None else deepcopy(properties))
        obj._return_quantity = False

        return obj
Example #9
0
    def xsec_to_2d(self,
                   xsec_name: str,
                   lunit: Optional[str] = None) -> Geo2DData:
        """
        Generates a Geo2DData from a cross section
        :param xsec_name: Name of the cross section
        :return: Geo2DData object
        """
        # Get our new coordinates
        # This construction ensures that y_new (the first axis in our new 2d coodinate
        # system) is always aligned (by projection) to one of the old axes, prefering
        # x, then y, then z
        x_new = np.array(self.xsecs[xsec_name]["axis"])
        y_new = np.array([0.0, 0, 0])
        axis_ind = -1
        while not np.any(y_new):
            axis_ind += 1
            y_new[axis_ind] = 1
            y_new -= y_new.dot(x_new) * x_new
        y_new /= np.linalg.norm(y_new)
        z_new = np.cross(x_new, y_new)

        def _project(vec):
            """Projects a 3D vector into our 2D cross section plane"""
            vec = vec - x_new * self.xsecs[xsec_name]["distance"]
            return [vec.dot(y_new), vec.dot(z_new)]

        def _inverse_project(vec):
            """Inverse of _project"""
            return (x_new * self.xsecs[xsec_name]["distance"] +
                    vec[0] * y_new + vec[1] * z_new)

        part_polygons = {}
        virtual_part_polygons = {}
        # part_polygons is a dictionary of part_name to polygons. virtual_part_polygons
        # is the same for virtual parts
        for part_name in self.build_order:
            polygons = []
            for name, points in self.xsecs[xsec_name]["polygons"].items():
                if name.startswith(f"{part_name}_"):
                    poly = Polygon(map(_project, points))
                    # we add a name to the polygon so we can reference it easier later
                    poly.name = name
                    polygons.append(poly)
            if polygons:
                if self.parts[part_name].domain_type == "virtual":
                    virtual_part_polygons[part_name] = polygons
                else:
                    part_polygons[part_name] = polygons

        def _build_containment_graph(poly_list):
            """
            Given a list of polygons, build a directed graph where edge A -> B means
            A contains B. This intentionally does not include nested containment. Which
            means if A contains B and B contains C, we will only get the edges A -> B
            and B -> C, not A -> C
            """
            poly_by_area = sorted(poly_list, key=lambda p: p.area)

            # graph["poly_name"] is a list of polygons that poly_name contains
            graph = {poly.name: [] for poly in poly_list}

            for i, poly in enumerate(poly_by_area):
                # Find the smallest polygon that contains poly (if any), and add it to
                # the graph
                for bigger_poly in poly_by_area[i + 1:]:
                    if bigger_poly.contains(poly):
                        graph[bigger_poly.name].append(poly)
                        break
            return graph

        def _is_inside(poly, part):
            """
            Given a polygon, find a point inside of it, and then check if that point is
            in the (3D) part
            """
            # Find the midpoint in x, then find the intersections with the polygon on
            # that vertical line. Then find the midpoint in y along the first
            # intersection line (if there're multiple)
            min_x, min_y, max_x, max_y = poly.bounds
            x = (min_x + max_x) / 2
            x_line = LineString([(x, min_y), (x, max_y)])
            intersec = x_line.intersection(poly)
            if type(intersec) == MultiLineString:
                intersec = intersec[0]
            x_line_intercept_min, x_line_intercept_max = intersec.xy[1].tolist(
            )
            y = (x_line_intercept_min + x_line_intercept_max) / 2

            # Get 3D coordinates and check if it's in the freecad shape
            x, y, z = _inverse_project([x, y])
            freecad_solid = Part.Solid(
                FreeCAD.ActiveDocument.getObject(part.built_fc_name).Shape)
            return freecad_solid.isInside(Base.Vector(x, y, z), 1e-5, True)

        # Let's deal with the physical domains first, which can have cavities
        geo_2d = Geo2DData()
        self.get_data("fcdoc")  # The document name is "instance"
        for name, poly_list in part_polygons.items():
            cont_graph = _build_containment_graph(poly_list)
            polys_to_add = []
            # For each polygon (in each part), we subtract from it all interior polygons
            # And then check if what remains is inside the part or not (it could be a
            # cavity). We add it if it's not a cavity
            for poly in poly_list:
                for interior_poly in cont_graph[poly.name]:
                    poly = poly.difference(interior_poly)
                if _is_inside(poly, self.parts[name]):
                    polys_to_add.append(poly)
            if not polys_to_add:
                continue
            if len(polys_to_add) == 1:
                geo_2d.add_part(name, polys_to_add[0])
                continue
            for i, poly in enumerate(polys_to_add):
                geo_2d.add_part(f"{name}_{i}", poly)

        # Now we deal with the virtual parts, which are just added as is
        for name, poly_list in virtual_part_polygons.items():
            if len(poly_list) == 1:
                geo_2d.add_part(name, poly_list[0])
                continue
            for i, poly in enumerate(poly_list):
                geo_2d.add_part(f"{name}_{i}", poly)

        geo_2d.lunit = lunit
        # Clean up freecad document
        FreeCAD.closeDocument("instance")
        return geo_2d
Example #10
0
    def xsec_to_2d(self,
                   xsec_name: str,
                   lunit: Optional[str] = None) -> Geo2DData:
        """Generates a Geo2DData from a cross section

        Parameters
        ----------
        xsec_name : str
            Name of the cross section
        lunit : Optional[str] :
            (Default value = None)
        Returns
        -------
        None

        """

        # Get our new coordinates
        # This constructions tries to align the new coordinates to our old coordinates
        # In particular, the map from projection axis -> new axes is
        # [1,0,0] -> [0,1,0] [0,0,1]
        # [0,1,0] -> [1,0,0] [0,0,1]
        # [0,0,1] -> [1,0,0] [0,1,0]

        # Find out which axis the projection axis is most closely aligned to
        x_new = np.array(self.xsecs[xsec_name]["axis"])
        ind = np.argmax(np.abs(x_new))
        y_new = np.array([0, 1.0, 0]) if ind == 0 else np.array([1.0, 0, 0])
        y_new -= y_new.dot(x_new) * x_new
        z_new = np.cross(x_new, y_new)
        # Adjust our second axis so that the "height axis" is correctly aligned
        if z_new[2] < 0:
            z_new = -z_new

        def _project(vec):
            """Projects a 3D vector into our 2D cross section plane
            
            Parameters
            ----------
            vec :
            Returns
            -------
            List of projection.

            """
            vec = vec - x_new * self.xsecs[xsec_name]["distance"]
            return [vec.dot(y_new), vec.dot(z_new)]

        def _inverse_project(vec):
            """Inverse of _project
            
            Parameters
            ----------
            vec :
            Returns
            -------
            Float, inverse of _project.

            """
            return (x_new * self.xsecs[xsec_name]["distance"] +
                    vec[0] * y_new + vec[1] * z_new)

        part_polygons = {}
        virtual_part_polygons = {}
        # part_polygons is a dictionary of part_name to polygons. virtual_part_polygons
        # is the same for virtual parts
        for part_name in self.build_order:
            polygons = []
            for name, points in self.xsecs[xsec_name]["polygons"].items():
                if name.startswith(f"{part_name}_"):
                    poly = Polygon(map(_project, points))
                    # we add a name to the polygon so we can reference it easier later
                    poly.name = name
                    polygons.append(poly)
            if polygons:
                if self.parts[part_name].virtual:
                    virtual_part_polygons[part_name] = polygons
                else:
                    part_polygons[part_name] = polygons

        def _build_containment_graph(poly_list):
            """Given a list of polygons, build a directed graph where edge A -> B means
            A contains B. This intentionally does not include nested containment. Which
            means if A contains B and B contains C, we will only get the edges A -> B
            and B -> C, not A -> C.

            Parameters
            ----------
            poly_list :
            Returns
            -------
            graph

            """
            poly_by_area = sorted(poly_list, key=lambda p: p.area)

            # graph["poly_name"] is a list of polygons that poly_name contains
            graph = {poly.name: [] for poly in poly_list}

            for i, poly in enumerate(poly_by_area):
                # Find the smallest polygon that contains poly (if any), and add it to
                # the graph
                for bigger_poly in poly_by_area[i + 1:]:
                    if bigger_poly.contains(poly):
                        graph[bigger_poly.name].append(poly)
                        break
            return graph

        def _is_inside(poly, part):
            """Given a polygon, find a point inside of it, and then check if that point is
            in the (3D) part

            Parameters
            ----------
            poly :
            part :
            Returns
            -------
            Boolean

            """
            # Find the midpoint in x, then find the intersections with the polygon on
            # that vertical line. Then find the midpoint in y along the first
            # intersection line (if there're multiple)
            min_x, min_y, max_x, max_y = poly.bounds
            x = (min_x + max_x) / 2
            x_line = LineString([(x, min_y), (x, max_y)])
            intersec = x_line.intersection(poly)
            if type(intersec) == MultiLineString:
                intersec = intersec[0]
            x_line_intercept_min, x_line_intercept_max = intersec.xy[1].tolist(
            )
            y = (x_line_intercept_min + x_line_intercept_max) / 2

            # Get 3D coordinates and check if it's in the freecad shape
            x, y, z = _inverse_project([x, y])
            freecad_solid = Part.Solid(
                FreeCAD.ActiveDocument.getObject(part.built_fc_name).Shape)
            return freecad_solid.isInside(Base.Vector(x, y, z), 1e-5, True)

        # Let's deal with the physical domains first, which can have cavities
        geo_2d = Geo2DData()
        self.get_data("fcdoc")  # The document name is "instance"
        for name, poly_list in part_polygons.items():
            cont_graph = _build_containment_graph(poly_list)
            polys_to_add = []
            # For each polygon (in each part), we subtract from it all interior polygons
            # And then check if what remains is inside the part or not (it could be a
            # cavity). We add it if it's not a cavity
            for poly in poly_list:
                for interior_poly in cont_graph[poly.name]:
                    poly = poly.difference(interior_poly)
                if _is_inside(poly, self.parts[name]):
                    polys_to_add.append(poly)
            if not polys_to_add:
                continue
            if len(polys_to_add) == 1:
                geo_2d.add_part(name, polys_to_add[0])
                continue
            for i, poly in enumerate(polys_to_add):
                geo_2d.add_part(f"{name}:{i}", poly)

        # Now we deal with the virtual parts, which are just added as is
        for name, poly_list in virtual_part_polygons.items():
            if len(poly_list) == 1:
                geo_2d.add_part(name, poly_list[0])
                continue
            for i, poly in enumerate(poly_list):
                geo_2d.add_part(f"{name}:{i}", poly)

        geo_2d.lunit = self.lunit if lunit is None else None
        # Clean up freecad document
        FreeCAD.closeDocument("instance")
        return geo_2d