def coastline(resolution="110m", radius=1.0, geocentric=True): fname = shp.natural_earth(resolution=resolution, category="physical", name="coastline") reader = shp.Reader(fname) dtype = np.float32 blocks = pv.MultiBlock() for i, record in enumerate(reader.records()): for geometry in record.geometry: xy = np.array(geometry.coords[:], dtype=dtype) if geocentric: xr = np.radians(xy[:, 0]).reshape(-1, 1) yr = np.radians(90 - xy[:, 1]).reshape(-1, 1) x = radius * np.sin(yr) * np.cos(xr) y = radius * np.sin(yr) * np.sin(xr) z = radius * np.cos(yr) else: # otherwise, geodetic x = xy[:, 0].reshape(-1, 1) y = xy[:, 1].reshape(-1, 1) z = np.zeros_like(x) xyz = np.hstack((x, y, z)) poly = pv.lines_from_points(xyz, close=False) blocks.append(poly) return blocks
def test_multi_block_save_lines(tmpdir): radius = 1 xr = np.random.random(10) yr = np.random.random(10) x = radius * np.sin(yr) * np.cos(xr) y = radius * np.sin(yr) * np.sin(xr) z = radius * np.cos(yr) xyz = np.stack((x, y, z), axis=1) poly = pyvista.lines_from_points(xyz, close=False) blocks = pyvista.MultiBlock() for _ in range(2): blocks.append(poly) path = tmpdir.mkdir("tmpdir") line_filename = str(path.join('lines.vtk')) block_filename = str(path.join('blocks.vtmb')) poly.save(line_filename) blocks.save(block_filename) poly_load = pyvista.read(line_filename) assert np.allclose(poly_load.points, poly.points) blocks_load = pyvista.read(block_filename) assert np.allclose(blocks_load[0].points, blocks[0].points)
def get_coastlines(resolution="110m"): """ Modified version of https://github.com/bjlittle/poc-ngvat/blob/master/poc-3/utils.py """ fname = shp.natural_earth(resolution=resolution, category="physical", name="coastline") reader = shp.Reader(fname) dtype = np.float32 blocks = pv.MultiBlock() for i, record in enumerate(reader.records()): for geometry in record.geometry: xy = np.array(geometry.coords[:], dtype=dtype) x = xy[:, 0].reshape(-1, 1) y = xy[:, 1].reshape(-1, 1) z = np.zeros_like(x) xyz = np.hstack((x, y, z)) poly = pv.lines_from_points(xyz, close=False) blocks.append(poly) return blocks
def test_lines_from_points(): points = np.array([[0, 0, 0], [1, 0, 0], [1, 1, 0]]) poly = pyvista.lines_from_points(points) assert poly.n_cells == 2 assert poly.n_points == 3 cells = poly.lines assert np.allclose(cells[:3], [2, 0, 1]) assert np.allclose(cells[3:], [2, 1, 2])
def boundary( self, surface: Optional[pv.PolyData] = None, radius: Optional[float] = None ) -> pv.PolyData: """ The region of the bounding-box that intersects on the surface of the mesh that will be enclosed. Parameters ---------- surface : PolyData, optional The :class:`pyvista.PolyData` mesh that will be enclosed by the bounding-box boundary. radius : float, default=1.0 The radius of the mesh that will be enclosed by the bounding-box boundary. Note that, the ``radius`` is only used when the ``surface`` is not provided. Returns ------- PolyData The boundary of the bounding-box. Notes ----- .. versionadded:: 0.1.0 """ self._generate_bbox_mesh(surface=surface, radius=radius) # TODO: address "fudge-factor" z-level radius = self._surface_radius + self._surface_radius / 1e4 edge_idxs = self._bbox_face_edge_idxs() edge_lons = self._bbox_lons[edge_idxs] edge_lats = self._bbox_lats[edge_idxs] edge_xyz = to_xyz(edge_lons, edge_lats, radius=radius) edge = pv.lines_from_points(edge_xyz, close=True) return edge
def add_vector_line_in_orbit(self, center_point, vector_point): self.vtk_widget.subplot(0, 0) self.vector_line_from_sc = pv.lines_from_points(np.array([center_point, center_point + 1e7 * vector_point])) self.vtk_widget.add_mesh(self.vector_line_from_sc, color='w') return
# address = 'Holzgerlingen DE' # graph = ox.graph_from_address(address, dist=500, network_type='drive') # pickle.dump(graph, open('/tmp/tmp.p', 'wb')) # Alternatively, use the pickeled graph included in our examples. from pyvista import examples graph = examples.download_osmnx_graph() ############################################################################### # Next, convert the edges into pyvista lines using # :func:`pyvista.lines_from_points`. nodes, edges = ox.graph_to_gdfs(graph) lines = [] # convert each edge into a line for idx, row in edges.iterrows(): x_pts = row['geometry'].xy[0] y_pts = row['geometry'].xy[1] z_pts = np.zeros(len(x_pts)) pts = np.column_stack((x_pts, y_pts, z_pts)) line = pv.lines_from_points(pts) lines.append(line) ############################################################################### # Finally, merge the lines and plot combined_lines = lines[0].merge(lines[1:]) combined_lines.plot(line_width=3, cpos='xy')
def sampleDataBlockProfile(dataBlock, line_walldists, pointid=None, cutterobj=None, normal=None) -> Profile: """Sample data block over a wall-normal profile Given a dataBlock containing a 'grid' and 'wall' block, this will return a PolyData object that samples 'grid' at the wall distances specified in line_walldists. This assumes that the 'wall' block has a field named 'Normals' containing the wall-normal vectors. The location of the profile is defined by either the index of a point in the 'wall' block or by specifying a vtk implicit function (such as vtk.vtkPlane) that intersects the 'wall' object. The latter uses the vtk.vtkCutter filter to determine the intersection. Parameters ---------- dataBlock : pv.MultiBlock MultiBlock containing the 'grid' and 'wall' objects line_walldists : numpy.ndarray The locations normal to the wall that should be sampled and returned. Locations are expected to be in order. pointid : int, optional Index of the point in 'wall' where the profile should be taken. (default: None) cutterobj : vtk.vtkPlane, optional VTK object that defines the profile location via intersection with the 'wall' normal : numpy.ndarray, optional If given, use this vector as the wall normal. Returns ------- vtkpytools.Profile """ wall = dataBlock['wall'] if 'Normals' not in wall.array_names: raise RuntimeError( 'The wall object must have a "Normals" field present.') if not (isinstance(pointid, int) ^ bool(cutterobj)): #xnor raise RuntimeError('Must provide either pointid or cutterobj.') if isinstance(pointid, int): wallnormal = wall['Normals'][pointid, :] if normal is None else normal wallnormal = np.tile(wallnormal, (len(line_walldists), 1)) sample_points = line_walldists[:, None] * wallnormal sample_points += wall.points[pointid] sample_line = pv.lines_from_points(sample_points) sample_line = sample_line.sample(dataBlock['grid']) sample_line['WallDistance'] = line_walldists else: cutterout = vCutter(wall, cutterobj) if cutterout.points.shape[0] != 1: raise RuntimeError( 'vCutter resulted in {:d} points instead of 1.'.format( cutterout.points.shape[0])) wallnormal = cutterout['Normals'] if normal is None else normal sample_points = line_walldists[:, None] * wallnormal sample_points += cutterout.points sample_line = pv.lines_from_points(sample_points) sample_line = sample_line.sample(dataBlock['grid']) sample_line['WallDistance'] = line_walldists sample_line = Profile(sample_line) sample_line.setWallDataFromPolyDataPoint(cutterout) return sample_line
def line( lons: npt.ArrayLike, lats: npt.ArrayLike, surface: Optional[pv.PolyData] = None, radius: Optional[float] = None, npts: Optional[int] = GEODESIC_NPTS, ellps: Optional[str] = ELLIPSE, close: Optional[bool] = False, ) -> pv.PolyData: """ Create a geodesic line consisting of one or more connected geodesic line segments. Parameters ---------- lons : ArrayLike The longitudes (degrees) of the geodesic line segments, in the half-closed interval [-180, 180). Note that, longitudes will be wrapped to this interval. lats : ArrayLike The latitudes (degrees) of the geodesic line segments, in the closed interval [-90, 90]. surface : PolyData, optional The surface that the geodesic line will be rendered over. radius : float, default=1.0 The radius of the surface that the geodesic line will be rendered over. Note that, the ``radius`` is only used when the ``surface`` is not provided. npts : float, default=GEODESIC_NPTS The number of equally spaced geodesic points in a line segment, excluding the segment end-point, but including the segment start-point i.e., ``npts`` must be at least 2. ellps : str, default=ELLIPSE The ellipsoid for geodesic calculations. See :func:`pyproj.get_ellps_map`. close : bool, default=False Whether to close the geodesic line segments into a loop i.e., the last point is connected to the first point. Returns ------- PolyData The geodesic line. Notes ----- .. versionadded:: 0.1.0 """ if surface is not None: radius = calculate_radius(surface) radius = 1.0 if radius is None else abs(radius) # TODO: address "fudge-factor" z-level radius += radius / 1e4 if not isinstance(lons, Iterable): lons = [lons] if not isinstance(lats, Iterable): lats = [lats] lons = np.asanyarray(lons) lats = np.asanyarray(lats) n_lons, n_lats = lons.size, lats.size if n_lons != n_lats: emsg = ( f"Require the same number of longitudes ({n_lons}) and " f"latitudes ({n_lats})." ) raise ValueError(emsg) if n_lons < 2: emsg = ( "Require a line geometry containing at least 2 longitude/latitude " f"values, got '{n_lons}'." ) raise ValueError(emsg) # ensure the specified line geometry is open if np.isclose(lons[0], lons[-1]) and np.isclose(lats[0], lats[-1]): lons, lats = lons[-1], lats[-1] line_lons, line_lats = [], [] geod = pyproj.Geod(ellps=ellps) for idx in range(n_lons - 1): glons, glats = npoints_by_idx( lons, lats, idx, idx + 1, npts=npts, include_start=True, include_end=False, geod=geod, ) line_lons.extend(glons) line_lats.extend(glats) # finally, include the end-point line_lons.append(lons[-1]) line_lats.append(lats[-1]) xyz = to_xyz(line_lons, line_lats, radius=radius) lines = pv.lines_from_points(xyz, close=close) return lines