def test_geodesic_input(self, n, radius, center): U = Rotation.random(random_state=0).as_matrix() thetas = np.linspace(0, 2 * np.pi, n, endpoint=False) points = np.vstack([np.sin(thetas), np.cos(thetas), np.zeros(n)]).T points = radius * points @ U sv = SphericalVoronoi(points + center, radius=radius, center=center) # each region must have 4 vertices region_sizes = np.array([len(region) for region in sv.regions]) assert (region_sizes == 4).all() regions = np.array(sv.regions) # vertices are those between each pair of input points + north and # south poles vertices = sv.vertices - center assert len(vertices) == n + 2 # verify that north and south poles are orthogonal to geodesic on which # input points lie poles = vertices[n:] assert np.abs(np.dot(points, poles.T)).max() < 1E-10 for point, region in zip(points, sv.regions): cosine = np.dot(vertices[region], point) sine = np.linalg.norm(np.cross(vertices[region], point), axis=1) arclengths = radius * np.arctan2(sine, cosine) # test arc lengths to poles assert_almost_equal(arclengths[[1, 3]], radius * np.pi / 2) # test arc lengths to forward and backward neighbors assert_almost_equal(arclengths[[0, 2]], radius * np.pi / n) regions = sv.regions.copy() sv.sort_vertices_of_regions() assert regions == sv.regions
def __init__(self, refinement=2, vecs=None): """ Setup a triangular grid on the 2-sphere. Args: vecs : array of shape (3, l_labels) refinement : if vecs is None, the triangulation is generated using trisphere(refinement), defined below """ if vecs is None: vecs, tris = trisphere(refinement) vecs = normalize(vecs) else: assert (vecs.shape[0] == 3) vecs = normalize(vecs) sv = SphericalVoronoi(vecs.T) sv.sort_vertices_of_regions() tris = sv._tri.simplices.T assert (tris.shape[0] == 3) s_manifold = 2 m_gradients = tris.shape[1] r_points = 3 l_labels = vecs.shape[1] logging.info("Set up for 2-sphere with {l} labels and {m} gradients." \ .format(s=s_manifold, l=l_labels,m=m_gradients)) self.v = vecs self.faces = tris self.mdims = { 's_manifold': s_manifold, 'm_gradients': m_gradients, 'r_points': r_points, 'l_labels': l_labels } self.v_grad = np.zeros((3, m_gradients), order='C') for j in range(m_gradients): # v : columns correspond to the vertices of triangle j v = self.v[:, self.faces[:, j]] # v0 : centroid of triangle j # taking the mean is a bottle neck here! self.v_grad[:, j] = self.mean(v).ravel() # b { l_labels } # P { m_gradients, r_points } # b : vol. elements, s.t. sum(self.b) converges to 4*PI as n to inf self.b = np.zeros((l_labels, ), order='C') self.b[:] = sphere_areas(vecs) self.b_precond = 1.0 / np.sqrt(np.einsum('i,i->', self.b, self.b)) self.P = np.zeros((m_gradients, r_points), order='C', dtype=np.int64) self.P[:] = tris.T # A { m_gradients, s_manifold, s_manifold } # B { m_gradients, s_manifold, r_points } self.A = np.zeros((m_gradients, s_manifold, s_manifold), order='C') self.B = np.zeros((m_gradients, s_manifold, r_points), order='C') self.setup_taylor_grad()
def get_dual2(g, points): sv = SphericalVoronoi(points) sv.sort_vertices_of_regions() faces = [] for region in sv.regions: face = sv.vertices[region] faces.append(face) return np.array(faces)
def iteration(pts): diagram = SphericalVoronoi(pts, radius=radius, center=np.array(center)) diagram.sort_vertices_of_regions() centers = [] for region in diagram.regions: verts = np.array([diagram.vertices[i] for i in region]) new_vert = weighted_center(verts, weight_field) centers.append(tuple(new_vert)) return centers
def test_sort_vertices_of_regions_flattened(self): expected = sorted([[0, 6, 5, 2, 3], [2, 3, 10, 11, 8, 7], [0, 6, 4, 1], [4, 8, 7, 5, 6], [9, 11, 10], [2, 7, 5], [1, 4, 8, 11, 9], [0, 3, 10, 9, 1]]) expected = list(itertools.chain(*sorted(expected))) sv = SphericalVoronoi(self.points) sv.sort_vertices_of_regions() actual = list(itertools.chain(*sorted(sv.regions))) assert_array_equal(actual, expected)
def test_region_types(self): # Tests that region integer type does not change # See Issue #13412 sv = SphericalVoronoi(self.points) dtype = type(sv.regions[0][0]) sv.sort_vertices_of_regions() assert type(sv.regions[0][0]) == dtype sv.sort_vertices_of_regions() assert type(sv.regions[0][0]) == dtype
def test_sort_vertices_of_regions_dimensionality(self): points = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [0.5, 0.5, 0.5, 0.5]]) with pytest.raises(TypeError, match="three-dimensional"): sv = SphericalVoronoi(points) sv.sort_vertices_of_regions()
def test_sort_vertices_of_regions_flattened(self): expected = sorted([[0, 6, 5, 2, 3], [2, 3, 10, 11, 8, 7], [0, 6, 4, 1], [4, 8, 7, 5, 6], [9, 11, 10], [2, 7, 5], [1, 4, 8, 11, 9], [0, 3, 10, 9, 1]]) expected = list(itertools.chain(*sorted(expected))) sv = SphericalVoronoi(self.points) sv.sort_vertices_of_regions() actual = list(itertools.chain(*sorted(sv.regions))) assert_array_equal(actual, expected)
def plot_old(self, plot_points=False, mark_peaks=0): ''' Plot the points on the sphere with their values ''' from scipy import rand import matplotlib.colors as colors #from mpl_toolkits.mplot3d import Axes3D import mpl_toolkits.mplot3d as a3 import matplotlib.pyplot as plt fig = plt.figure() ax = fig.add_subplot(111, projection='3d') if plot_points: ax.scatter(self.x, self.y, self.z, c='b', marker='o') if mark_peaks > 0: id = self.find_peaks(k=mark_peaks) s = 1.05 ax.scatter(s*self.x[id], s*self.y[id], s*self.z[id], c='k', marker='o') voronoi = SphericalVoronoi(self.cartesian.T) voronoi.sort_vertices_of_regions() if self.values is not None: col_max = self.values.max() col_min = self.values.min() if col_min != col_max: col_map = (self.values - col_min) / (col_max - col_min) else: col_map = self.values / col_max else: col_map = np.zeros(self.n_points) cmap = plt.get_cmap('coolwarm') # plot the walls for v_ind, col in zip(voronoi.regions, col_map): triangle = a3.art3d.Poly3DCollection([voronoi.vertices[v_ind]], alpha=1., linewidth=0.0) triangle.set_color(cmap(col)) triangle.set_edgecolor('k') ax.add_collection3d(triangle) ax.set_xlabel('X') ax.set_ylabel('Y') ax.set_zlabel('Z') ax.set_xlim([-1,1]) ax.set_ylim([-1,1]) ax.set_zlim([-1,1])
def test_circular_input(self, n, radius, fraction, center): # generate a random circle rng = np.random.RandomState(0) U = Rotation.random(random_state=rng).as_matrix() h = radius * fraction circle_radius = np.sqrt(radius**2 - h**2) thetas = np.linspace(0, 2 * np.pi, n, endpoint=False) points = np.vstack([ circle_radius * np.sin(thetas), circle_radius * np.cos(thetas), h * np.ones(n) ]).T points = points @ U # calculate Spherical Voronoi diagram sv = SphericalVoronoi(points + center, radius=radius, center=center) # each region must have 4 vertices region_sizes = np.array([len(region) for region in sv.regions]) assert (region_sizes == 4).all() regions = np.array(sv.regions) # vertices are those between each pair of input points + north and # south poles vertices = sv.vertices - center assert len(vertices) == n + 2 # verify that north and south poles are orthogonal to circle on which # input points lie poles = vertices[n:] midpoint = np.array([0, 0, h]) @ U assert np.abs(np.dot(points - midpoint, poles.T)).max() < 1E-10 # test arc lengths to forward and backward neighbors for point, region in zip(points, sv.regions): cosine = np.dot(vertices[region] - midpoint, point - midpoint) sine = np.linalg.norm(np.cross(vertices[region] - midpoint, point - midpoint), axis=1) arclengths = circle_radius * np.arctan2(sine, cosine) assert_almost_equal(arclengths[[0, 2]], circle_radius * np.pi / n) # test arc lengths to poles for point, region in zip(points, sv.regions): cosine = np.dot(vertices[region], point) sine = np.linalg.norm(np.cross(vertices[region], point), axis=1) arclengths = np.arctan2(sine, cosine) angle = np.arcsin(h / radius) assert_almost_equal(arclengths[1], np.pi / 2 - angle) assert_almost_equal(arclengths[3], np.pi / 2 + angle) regions = sv.regions.copy() sv.sort_vertices_of_regions() assert regions == sv.regions
def voro3D(points=np.array([0, 0, 0]), center=np.array([0, 0, 0])): #~ center = np.array([np.mean(points), np.mean(points), np.mean(points)]) radius = np.amax(points) #~ print "Center = %25.20f\t%25.20f\t%25.20f" % (center[0],center[1], center[2]) #~ print "Radius = %25.20f" % radius #~ print points.shape # calculate spherical Voronoi diagram sv = SphericalVoronoi(points, radius, center) # sort vertices (optional, helpful for plotting) sv.sort_vertices_of_regions() return sv
class SphericalVorSort(Benchmark): params = [10, 100, 1000, 5000, 10000] param_names = ['num_points'] def setup(self, num_points): self.points = generate_spherical_points(num_points) self.sv = SphericalVoronoi(self.points, radius=1, center=np.zeros(3)) def time_spherical_polygon_vertex_sorting(self, num_points): """Time the vertex sorting operation in the Spherical Voronoi code. """ self.sv.sort_vertices_of_regions()
class SphericalVorSort(Benchmark): params = [10, 100, 1000, 5000, 10000] param_names = ['num_points'] def setup(self, num_points): self.points = generate_spherical_points(num_points) self.sv = SphericalVoronoi(self.points, radius=1, center=np.zeros(3)) def time_spherical_polygon_vertex_sorting(self, num_points): """Time the vertex sorting operation in the Spherical Voronoi code. """ self.sv.sort_vertices_of_regions()
def addVoronoi(points): """add voronoi on the surface. Note that before using this function, modify the parameters(radius, center) first""" radius = RADIUS points = points/radius center = np.array([0, 0, 0]) sv = SphericalVoronoi(points, 1, center) sv.sort_vertices_of_regions() t_vals = np.linspace(0, 1, 2000)#set the num of points on the line result = np.random.rand(0, 2000, 3) for region in sv.regions: n = len(region) for i in range(n): start = sv.vertices[region][i] end = sv.vertices[region][(i + 1) % n] if not (start == end).all(): temp=radius*geometric_slerp(start, end, t_vals) result = np.concatenate((result, temp[None]), axis=0) return result
def process(self): if not any(socket.is_linked for socket in self.outputs): return vertices_s = self.inputs['Vertices'].sv_get() radius_s = self.inputs['Radius'].sv_get() center_s = self.inputs['Center'].sv_get() verts_out = [] edges_out = [] faces_out = [] for sites, center, radius in zip_long_repeat( vertices_s, center_s, radius_s): if isinstance(radius, (list, tuple)): radius = radius[0] center = center[0] sites = np.array( [project_to_sphere(center, radius, v) for v in sites]) vor = SphericalVoronoi(sites, radius=radius, center=np.array(center)) vor.sort_vertices_of_regions() new_verts = vor.vertices.tolist() new_faces = vor.regions #new_edges = polygons_to_edges([new_faces], True)[0] bm2 = bmesh_from_pydata(new_verts, [], new_faces, normal_update=True) bmesh.ops.recalc_face_normals(bm2, faces=bm2.faces) new_verts, new_edges, new_faces = pydata_from_bmesh(bm2) bm2.free() verts_out.append(new_verts) edges_out.append(new_edges) faces_out.append(new_faces) self.outputs['Vertices'].sv_set(verts_out) self.outputs['Edges'].sv_set(edges_out) self.outputs['Faces'].sv_set(faces_out)
def backbone_network(vstations, points, stns, ties): if isinstance(ties, list): # make a numpy array with the points if list passed ties = np.array(ties) flt = np.ones(len(ties), dtype=np.bool) # get xyz of the stations pc = ll2sphere_xyz(ties) print(pc) while len(pc[flt]) - BACKBONE_NET > 0: # calculate the spherical voronoi sv = SphericalVoronoi(pc[flt], radius=6371000, threshold=1e-9) #vor = Voronoi(lla) #fig = voronoi_plot_2d(vor, show_vertices=False, line_colors='orange', # line_width = 2, line_alpha = 0.6, point_size = 2) #plt.show() sv.sort_vertices_of_regions() area = np.zeros(len(sv.regions)) weight = np.zeros(len(sv.regions)) for i, region in enumerate(sv.regions): area[i] = calculate_surface_area_of_a_spherical_Voronoi_polygon( sv.vertices[region], 6371) weight[i] = 0.3 * area[i] # also needs the stations weight (to do) # rank stations by weight minw = np.argsort(weight) #for m in minw: # print stns[m][0], area[m] # remove the first one on the list flt[np.where(flt)[0][minw[0]]] = False print('iterating %i' % len(pc[flt])) plot_v(pc[flt], sv) return flt
def sphere_areas(v): """ Calculates areas of Voronoi regions corresponding to given points. Args: v : numpy array, each column corresponds to a point on the sphere Returns: 1d numpy array, areas of the Voronoi regions corresponding to each of the points """ areas = np.zeros(v.shape[1]) sv = SphericalVoronoi(v.T) # sort the vertices for poly_area() sv.sort_vertices_of_regions() """ # (optional) for visualization of the voronoi regions: from matplotlib import colors from mpl_toolkits.mplot3d.art3d import Poly3DCollection import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import proj3d fig = plt.figure() ax = fig.add_subplot(111, projection='3d') # plot generator points ax.scatter(v[0], v[1], v[2], c='b') # plot Voronoi vertices ax.scatter(sv.vertices[:, 0], sv.vertices[:, 1], sv.vertices[:, 2], c='g') # indicate Voronoi regions (as Euclidean polygons) for region in sv.regions: random_color = colors.rgb2hex(np.random.rand(3)) polygon = Poly3DCollection([sv.vertices[region]], alpha=1.0) polygon.set_color(random_color) ax.add_collection3d(polygon) plt.show() """ for j, region in enumerate(sv.regions): areas[j] = spherical_poly_area(sv.vertices[region]) return areas
def to_radius(r, v, c): x, y, z = v x0, y0, z0 = c v = x - x0, y - y0, z - z0 rho, phi, theta = to_spherical(v, "radians") x, y, z = from_spherical(r, phi, theta, "radians") return x + x0, y + y0, z + z0 out_verts = [] out_edges = [] out_faces = [] for points, center, radius in zip_long_repeat(in_points, in_center, in_radius): if isinstance(radius, (list, tuple)): radius = radius[0] center = center[0] #info("Center: %s, radius: %s", center, radius) points = np.array([to_radius(radius, v, center) for v in points]) vor = SphericalVoronoi(points, radius=radius, center=np.array(center)) vor.sort_vertices_of_regions() new_verts = vor.vertices.tolist() new_faces = vor.regions out_verts.append(new_verts) out_faces.append(new_faces)
def test_sort_vertices_of_regions(self): sv = SphericalVoronoi(self.points) unsorted_regions = sv.regions sv.sort_vertices_of_regions() assert_array_equal(sorted(sv.regions), sorted(unsorted_regions))
def createGraph(planet, planetGenerator): #the sphere is spanned into regions of closer points to the seeds with a spherical Voronoi mapping. sv = SphericalVoronoi(planet.meshPoints) sv.sort_vertices_of_regions() #ensure that the polygon is clockwise oriented and correct it if not. #For plotting, all the polygons must have the same orientation. for i, region in enumerate(sv.regions): if (np.dot(np.cross(sv.points[i], sv.vertices[region[0]]), sv.vertices[region[1]]) > 0): sv.regions[i].reverse() #the graph of regions centers is created. This is useful for checking travel between regions. #At this point it is not yet possible to set the connections between regions. centers = Graph(directed=False) centers.add_vertex(len(sv.regions)) #for each cell, register the corners points index delimiting it. centers.vertex_properties["corners"] = centers.new_vertex_property( "vector<int>") for v, corners in zip(centers.vertices(), sv.regions): centers.vp.corners[v] = corners #for each cell, set its center point. centers.vertex_properties["center"] = centers.new_vertex_property( "vector<float>") for v, center in zip(centers.vertices(), sv.points): centers.vp.center[v] = center #for each cell, set its area in km^2. This is often used as a weight for the cell. centers.vertex_properties["area"] = centers.new_vertex_property("float") for v, area in zip(centers.vertices(), sv.calculate_areas()): centers.vp.area[v] = area * planet.parameters.radius**2 #create the graph of corners points. It is the dual of the graph centers. #this graph is useful to plot the borders and check touching regions. corners = Graph(directed=False) corners.add_vertex(len(sv.vertices)) #for each corner, register its position. corners.vertex_properties["position"] = corners.new_vertex_property( "vector<float>") for v, position in zip(corners.vertices(), sv.vertices): corners.vp.position[v] = position #set the edges between corners cornerLinks = [] for region in sv.regions: cornerLinks += [(min(i, j), max(i, j)) for (i, j) in pairwiseRoll(region)] #the edges are converted to dictionnary and back to a list to remove the doublons corners.add_edge_list(list(dict.fromkeys(cornerLinks))) #set the polygons that each corner touches corners.vertex_properties["touches"] = corners.new_vertex_property( "vector<int>") for i, cornerList in enumerate(centers.vp.corners): for corner in cornerList: corners.vp.touches[corner].append(i) #compute length of each border in km corners.edge_properties["length"] = corners.new_edge_property("float") edges = corners.get_edges() positions = np.array(list(corners.vp.position))[edges] lengths = np.arccos(np.sum(positions[:, 0] * positions[:, 1], axis=-1)) * planet.parameters.radius for i, e in enumerate(corners.edges()): corners.ep.length[e] = lengths[i] corners.edge_properties["separates"] = corners.new_edge_property( "vector<int>") for e in corners.edges(): corners.ep.separates[e] = list( set(corners.vp.touches[e.source()]).intersection( corners.vp.touches[e.target()])) #add links between regions centerLinks = [] for c in corners.vertices(): l = corners.vp.touches[c] centerLinks += [(min(i, j), max(i, j)) for (i, j) in pairwiseRoll(l)] centers.add_edge_list(list(dict.fromkeys(centerLinks))) #compute distance between regions in km centers.edge_properties["length"] = centers.new_edge_property("float") edges = centers.get_edges() positions = np.array(list(centers.vp.center))[edges] lengths = np.arccos(np.sum(positions[:, 0] * positions[:, 1], axis=-1)) * planet.parameters.radius for i, e in enumerate(centers.edges()): centers.ep.length[e] = lengths[i] planet.meshCenters = centers planet.meshCorners = corners def plotTilesBorders(self, ax, *args, **kwargs): edges = self.meshCorners.get_edges() segments = np.array(list(self.meshCorners.vp.position))[edges] (x, y, z) = np.shape(segments) segments = np.reshape(segments, (x * y, z)) lat, long = Projection().toLatLong(segments) lat = np.reshape(lat, (x, y)) long = np.reshape(long, (x, y)) lines = [[(slong[0], slat[0]), (slong[1], slat[1])] for slat, slong in zip(lat, long)] ax.add_collection( mc.LineCollection(lines, *args, transform=ccrs.Geodetic(), **kwargs)) planet.plotTilesBorders = types.MethodType(plotTilesBorders, planet) def plotTilesJunctions(self, ax, *args, **kwargs): edges = self.meshCenters.get_edges() segments = np.array(list(self.meshCenters.vp.center))[edges] (x, y, z) = np.shape(segments) segments = np.reshape(segments, (x * y, z)) lat, long = Projection().toLatLong(segments) lat = np.reshape(lat, (x, y)) long = np.reshape(long, (x, y)) lines = [[(slong[0], slat[0]), (slong[1], slat[1])] for slat, slong in zip(lat, long)] ax.add_collection( mc.LineCollection(lines, *args, transform=ccrs.Geodetic(), **kwargs)) planet.plotTilesJunctions = types.MethodType(plotTilesJunctions, planet)
def main(use_random_set_of_input_points, number_of_random_points, input_samples, input_samples_point_shapefile_output_path, voronoi_polygons_shapefile_output_path, geogr_sr_text): voronoi_polygon_areas = [] """ We use +- 179.999999 rather than +- 180 deg lon to indicate a date line since -180 deg lon is sometimes automatically converted to +180 deg lon by GDAL/OGR. """ if use_random_set_of_input_points == True: """ Generate random Points """ randomness_generate_random_points(number_of_random_points) input_samples = random_points if len(input_samples) < 20: print "Need minimum of 20 points for the construction of Voronoi polygons." exit() input_craters_scipy = numpy.array([]) voronoi_out_polygons_geogr_sr = osr.SpatialReference() voronoi_out_polygons_geogr_sr.ImportFromWkt(geogr_sr_text) """ Generate Shapefiles """ # Craters driver = ogr.GetDriverByName('Esri Shapefile') points_data_source = driver.CreateDataSource(input_samples_point_shapefile_output_path) points_layer = points_data_source.CreateLayer('Voronoi_Points', voronoi_out_polygons_geogr_sr, geom_type = ogr.wkbPoint) # Voronoi Polygons driver = ogr.GetDriverByName('Esri Shapefile') voronoi_polygon_data_source = driver.CreateDataSource(voronoi_polygons_shapefile_output_path) voronoi_polygon_layer = voronoi_polygon_data_source.CreateLayer('Voronoi_Polygons', voronoi_out_polygons_geogr_sr, geom_type = ogr.wkbPolygon) area_field_defn = ogr.FieldDefn("Area", ogr.OFTReal) voronoi_polygon_layer.CreateField(area_field_defn) """ Project geographic coordinates to coordinates in 3D space [-1,1] """ point = ogr.Geometry(ogr.wkbPoint) point.AssignSpatialReference(voronoi_out_polygons_geogr_sr) for input_crater in input_samples: input_crater_X = input_crater[0] input_crater_Y = input_crater[1] point.AddPoint(input_crater_X, input_crater_Y) input_crater_X = math.pi*input_crater_X/180 # Convert to radians input_crater_Y = math.pi*input_crater_Y/180 input_crater_Y -= 1.570795765134 # subtract 90 degrees (in radians) input_crater_X_3D = math.sin(input_crater_Y) * math.cos(input_crater_X) input_crater_Z_3D = math.cos(input_crater_Y) input_crater_Y_3D = math.sin(input_crater_Y) * math.sin(input_crater_X) input_craters_scipy = numpy.append(input_craters_scipy, [[input_crater_X_3D, input_crater_Y_3D, input_crater_Z_3D]]) points_featureDefn = points_layer.GetLayerDefn() points_feature = ogr.Feature(points_featureDefn) points_feature.SetGeometry(point) points_layer.CreateFeature(points_feature) input_craters_scipy.resize((len(input_samples), 3)) # reshape numpy array """ Calculate Voronoi diagrams on a sphere in 3D space. This only generates vertices on the sphere. Polygon edges cut the sphere's interior. """ sphere_center_coordinates = numpy.array([0, 0, 0]) sphere_radius = 1 spherical_voronoi = SphericalVoronoi(input_craters_scipy, sphere_radius, sphere_center_coordinates) spherical_voronoi.sort_vertices_of_regions() """ Main part of geodesic Voronoi polygon generation: Calculate spherical coordinates of each Voronoi Polygon and save geometries. """ voronoi_count = 0 for spherical_voronoi_region_3D in spherical_voronoi.regions: voronoi_vertex_count = 1 voronoi_polygon_area = 0 voronoi_out_polygons = ogr.Geometry(ogr.wkbPolygon) voronoi_out_polygons.AssignSpatialReference(voronoi_out_polygons_geogr_sr) """ Calculate geographic coordinates of polygon vertices """ lons = [] lats = [] for spherical_voronoi_vertex_3D in spherical_voronoi.vertices[spherical_voronoi_region_3D]: spherical_voronoi_vertex_3D_X = spherical_voronoi_vertex_3D[0] spherical_voronoi_vertex_3D_Y = spherical_voronoi_vertex_3D[1] spherical_voronoi_vertex_3D_Z = spherical_voronoi_vertex_3D[2] spherical_voronoi_vertex_lon = math.degrees(math.atan(spherical_voronoi_vertex_3D_Y/spherical_voronoi_vertex_3D_X)) """ Account for atan ambiguity. Otherwise, polygon vertices would all be on the nearside. """ if spherical_voronoi_vertex_3D_X > 0: spherical_voronoi_vertex_lon -= 180 if spherical_voronoi_vertex_lon < -180: spherical_voronoi_vertex_lon += 360 spherical_voronoi_vertex_lat = math.degrees(math.asin(spherical_voronoi_vertex_3D_Z)) """ Add vertex to lons[] and lats[] """ lons.append(spherical_voronoi_vertex_lon) lats.append(spherical_voronoi_vertex_lat) if voronoi_vertex_count == 1: spherical_voronoi_end_vertex_lon = spherical_voronoi_vertex_lon spherical_voronoi_end_vertex_lat = spherical_voronoi_vertex_lat voronoi_vertex_count += 1 """ Close ring """ lons.append(spherical_voronoi_end_vertex_lon) lats.append(spherical_voronoi_end_vertex_lat) """ define reprojections from input craters (center of voronoi polygon) """ center_voronoi_polygon_X = input_samples[voronoi_count][0] center_voronoi_polygon_Y = input_samples[voronoi_count][1] voronoi_LAEA_text = 'PROJCS["PROJECTED_LAMBERT_AEA",'+str(voronoi_out_polygons_geogr_sr)+',PROJECTION["Lambert_Azimuthal_Equal_Area"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["central_meridian",'+str(center_voronoi_polygon_X)+'],PARAMETER["latitude_of_origin",'+str(center_voronoi_polygon_Y)+'],UNIT["Meter",1.0]]' voronoi_LAEA_sr = osr.SpatialReference() voronoi_LAEA_sr.ImportFromWkt(voronoi_LAEA_text) voronoi_geogr_text = re.sub('(PRIMEM)(.*)(,)', r'\1["Reference_Meridian",' + str(center_voronoi_polygon_X) + '],', str(voronoi_out_polygons_geogr_sr)) voronoi_geogr_sr = osr.SpatialReference() voronoi_geogr_sr.ImportFromWkt(voronoi_geogr_text) """ define reprojections """ geogr_to_proj_voronoi = osr.CoordinateTransformation(voronoi_out_polygons_geogr_sr, voronoi_LAEA_sr) proj_voronoi_to_geogr = osr.CoordinateTransformation(voronoi_LAEA_sr, voronoi_out_polygons_geogr_sr) proj_voronoi_to_geogr_voronoi = osr.CoordinateTransformation(voronoi_LAEA_sr, voronoi_geogr_sr) geogr_voronoi_to_proj_voronoi = osr.CoordinateTransformation(voronoi_geogr_sr, voronoi_LAEA_sr) """ define polygons that eventually become a voronoi polygon """ voronoi_ring = ogr.Geometry(ogr.wkbLinearRing) voronoi_ring.AssignSpatialReference(voronoi_out_polygons_geogr_sr) voronoi_ring_dateline_intersection = ogr.Geometry(ogr.wkbLinearRing) voronoi_ring_dateline_intersection.AssignSpatialReference(voronoi_out_polygons_geogr_sr) major_axis = voronoi_out_polygons_geogr_sr.GetSemiMajor() minor_axis = voronoi_out_polygons_geogr_sr.GetSemiMinor() flattening = (major_axis-minor_axis) / major_axis """ Use vertices of voronoi polygons to calculate geodesic polygon edges. """ voronoi_vertex_lat_lon_count = 0 voronoi_date_line_intersection_count = 0 for voronoi_vertex_lon in lons: voronoi_vertex_lat = lats[voronoi_vertex_lat_lon_count] if voronoi_vertex_lat_lon_count+1 < len(lons): voronoi_next_vertex_lat = lats[voronoi_vertex_lat_lon_count+1] voronoi_next_vertex_lon = lons[voronoi_vertex_lat_lon_count+1] if voronoi_vertex_lat_lon_count+1 >= len(lons): if voronoi_date_line_intersection_count % 2 == 0: voronoi_ring.AddPoint(lons[0], lats[0]) if voronoi_date_line_intersection_count % 2 == 1: voronoi_ring_dateline_intersection.AddPoint(lons[0], lats[0]) continue if voronoi_date_line_intersection_count % 2 == 0: voronoi_ring.AddPoint(voronoi_vertex_lon, voronoi_vertex_lat) if voronoi_date_line_intersection_count % 2 == 1: voronoi_ring_dateline_intersection.AddPoint(voronoi_vertex_lon, voronoi_vertex_lat) """ Get distance and azimuth between voronoi vertices. """ inverse_vincenty(flattening, major_axis, voronoi_vertex_lat, voronoi_vertex_lon, voronoi_next_vertex_lat, voronoi_next_vertex_lon) geodesic_distance_between_voronoi_vertices = geodesic_distance_crater_area azimuth_between_voronoi_vertices = direction12 distance_geodesic_polygon_vertices = 0 geodesic_voronoi_vertices_count = 0 """ Mark scipy voronoi vertex as 'previous vertex' in geodesic edge calculation. This is done to consider Date Line intersections that occur between the voronoi vertex and the first geodesic vertex (less than 15 km away). """ if geodesic_voronoi_vertices_count == 0: previous_voronoi_geodesic_vertex_X = voronoi_vertex_lon previous_voronoi_geodesic_vertex_Y = voronoi_vertex_lat """ Divide distance between vertices into 15 km segments and get coordinates of vertices. Vertices from geodesic distances are added to voronoi_ring. """ geodesic_distance_voronoi_vertices = 15000 """ When vertices of a polygon are closer than the segment length of the geodesic vertices, the distance between geodesic vertices is reduced to one fifth of the distance between the vertices (to get additional vertex in between). When start and end vertex of a polygon are close to the date line on either hemisphere, this would cause the program to believe that there is a polar intersection (because there is only one date line intersection present due to the missing additional vertices over the second date line intersection) resulting in wrong voronoi polygons. """ if geodesic_distance_crater_area < geodesic_distance_voronoi_vertices: geodesic_distance_voronoi_vertices = geodesic_distance_crater_area/5 """ When the current or the next vertex is very close to the date line, make sure that the distance between the geodesic vertices in between is lower than that. If not considered, a date line intersection would be recognized as a polar intersection because the second date line overlap would not occur as planned. If either vertex is <-170 deg lon or >170 deg lon (10 deg lon can be very short close to the poles), check distance to date line and if either distance is lower than the geodesic distance to calculate the vertices in between, modify the geodesic distance. """ voronoi_date_line_correction_lon_1 = 170 voronoi_date_line_correction_lon_2 = -170 """ Close to the poles, a vertex is considered very close to the date line when it is closer than +- 150 deg lon. """ if voronoi_vertex_lat > 89 or voronoi_vertex_lat < -89: voronoi_date_line_correction_lon_1 = 150 voronoi_date_line_correction_lon_2 = -150 if voronoi_vertex_lon < voronoi_date_line_correction_lon_2 or voronoi_next_vertex_lon < voronoi_date_line_correction_lon_2 or voronoi_vertex_lon > voronoi_date_line_correction_lon_1 or voronoi_next_vertex_lon > voronoi_date_line_correction_lon_1: if voronoi_vertex_lon > voronoi_date_line_correction_lon_1 or voronoi_next_vertex_lon > voronoi_date_line_correction_lon_1: shapely_dateline = LineString([(180, 90),(180, 0),(180, -90)]) if voronoi_vertex_lon < voronoi_date_line_correction_lon_2 or voronoi_next_vertex_lon < voronoi_date_line_correction_lon_2: shapely_dateline = LineString([(-180, 90),(-180, 0),(-180, -90)]) """ Find closest point on Date Line and get geodesic distance. """ shapely_voronoi_vertex = Point(voronoi_vertex_lon, voronoi_vertex_lat) shapely_next_voronoi_vertex = Point(voronoi_next_vertex_lon, voronoi_next_vertex_lat) voronoi_vertex_closest_point_on_dateline = shapely_dateline.interpolate(shapely_dateline.project(shapely_voronoi_vertex)) voronoi_next_vertex_closest_point_on_dateline = shapely_dateline.interpolate(shapely_dateline.project(shapely_next_voronoi_vertex)) voronoi_vertex_closest_point_on_dateline_ogr = ogr.CreateGeometryFromWkt(voronoi_vertex_closest_point_on_dateline.wkt) voronoi_next_vertex_closest_point_on_dateline_ogr = ogr.CreateGeometryFromWkt(voronoi_next_vertex_closest_point_on_dateline.wkt) voronoi_vertex_closest_point_on_dateline_ogr_X = voronoi_vertex_closest_point_on_dateline_ogr.GetX() voronoi_vertex_closest_point_on_dateline_ogr_Y = voronoi_vertex_closest_point_on_dateline_ogr.GetY() voronoi_next_vertex_closest_point_on_dateline_ogr_X = voronoi_next_vertex_closest_point_on_dateline_ogr.GetX() voronoi_next_vertex_closest_point_on_dateline_ogr_Y = voronoi_next_vertex_closest_point_on_dateline_ogr.GetY() inverse_vincenty(flattening, major_axis, voronoi_vertex_lat, voronoi_vertex_lon, voronoi_vertex_closest_point_on_dateline_ogr_Y, voronoi_vertex_closest_point_on_dateline_ogr_X) distance_voronoi_vertex_closest_point_on_dateline = geodesic_distance_crater_area inverse_vincenty(flattening, major_axis, voronoi_next_vertex_lat, voronoi_next_vertex_lon, voronoi_next_vertex_closest_point_on_dateline_ogr_Y, voronoi_next_vertex_closest_point_on_dateline_ogr_X) distance_next_voronoi_vertex_closest_point_on_dateline = geodesic_distance_crater_area """ Check whether the current or the next vertex is closest to the date line. """ minimum_distance_current_and_next_voronoi_vertex_to_dateline = min(distance_voronoi_vertex_closest_point_on_dateline, distance_next_voronoi_vertex_closest_point_on_dateline) """ Use closest distance to the date line to modify the distance between the geodesic vertices for such polygon sections. """ if minimum_distance_current_and_next_voronoi_vertex_to_dateline < 2 * geodesic_distance_voronoi_vertices: geodesic_distance_voronoi_vertices = minimum_distance_current_and_next_voronoi_vertex_to_dateline/5 while geodesic_distance_between_voronoi_vertices > geodesic_distance_voronoi_vertices: distance_geodesic_polygon_vertices += geodesic_distance_voronoi_vertices geodesic_distance_between_voronoi_vertices -= geodesic_distance_voronoi_vertices direct_vincenty(flattening, major_axis, [[voronoi_vertex_lon, voronoi_vertex_lat, azimuth_between_voronoi_vertices, 0, 0]], distance_geodesic_polygon_vertices, 1) voronoi_geodesic_vertex_X = buffer_vertices_list[0][0] voronoi_geodesic_vertex_Y = buffer_vertices_list[0][1] """ Consideration of Date Line intersections. If the voronoi polygon crosses the Date Line, the voronoi polygon is merged from individual polygons voronoi_ring and voronoi_ring_dateline_intersection to avoid Date Line ambiguity. """ """ Polygons that intersect the date line cross the date line twice, polygons that intersect the poles intersect the date line once """ """ detect intersection with date line """ voronoi_dateline_intersection = False if previous_voronoi_geodesic_vertex_X <= 179.999999 and voronoi_geodesic_vertex_X > 179.999999 or previous_voronoi_geodesic_vertex_X > 179.999999 and voronoi_geodesic_vertex_X <= 179.999999:# or previous_voronoi_start_vertex_X <= 179.999999 and voronoi_geodesic_vertex_X > 179.999999 or previous_voronoi_start_vertex_X > 179.999999 and voronoi_geodesic_vertex_X <= 179.999999: voronoi_dateline_intersection = True voronoi_dateline_hemisphere = "East" dateline_voronoi = ogr.Geometry(ogr.wkbLineString) dateline_voronoi.AssignSpatialReference(voronoi_out_polygons_geogr_sr) dateline_voronoi.AddPoint(179.999999, 90) dateline_voronoi.AddPoint(179.999999, -90) if previous_voronoi_geodesic_vertex_X >= -179.999999 and voronoi_geodesic_vertex_X < -179.999999 or previous_voronoi_geodesic_vertex_X < -179.999999 and voronoi_geodesic_vertex_X >= -179.999999:# or previous_voronoi_start_vertex_X >= -179.999999 and voronoi_geodesic_vertex_X < -179.999999 or previous_voronoi_start_vertex_X < -179.999999 and voronoi_geodesic_vertex_X >= -179.999999: voronoi_dateline_intersection = True voronoi_dateline_hemisphere = "West" dateline_voronoi = ogr.Geometry(ogr.wkbLineString) dateline_voronoi.AssignSpatialReference(voronoi_out_polygons_geogr_sr) dateline_voronoi.AddPoint(-179.999999, 90) dateline_voronoi.AddPoint(-179.999999, -90) if voronoi_dateline_intersection == True: voronoi_date_line_intersection_count += 1 previous_current_voronoi_vertex_line = ogr.Geometry(ogr.wkbLineString) previous_current_voronoi_vertex_line.AssignSpatialReference(voronoi_out_polygons_geogr_sr) previous_current_voronoi_vertex_line.AddPoint(previous_voronoi_geodesic_vertex_X, previous_voronoi_geodesic_vertex_Y) previous_current_voronoi_vertex_line.AddPoint(voronoi_geodesic_vertex_X, voronoi_geodesic_vertex_Y) voronoi_dateline_intersection_point = previous_current_voronoi_vertex_line.Intersection(dateline_voronoi) voronoi_dateline_intersection_point_X = voronoi_dateline_intersection_point.GetX() voronoi_dateline_intersection_point_Y = voronoi_dateline_intersection_point.GetY() """ add date line intersection point to voronoi_ring """ if voronoi_date_line_intersection_count % 2 == 1: voronoi_ring.AddPoint(voronoi_dateline_intersection_point_X, voronoi_dateline_intersection_point_Y) """ When crossing to the other hemisphere for the first time, the new polygon longitude must always be -179.999999 if it crosses from west to east and +179.999999 when crossing from east to west. This is the first intersection point with the date line. """ if voronoi_dateline_hemisphere == "East": voronoi_dateline_intersection_point_X = -179.999999 if voronoi_dateline_hemisphere == "West": voronoi_dateline_intersection_point_X = +179.999999 if voronoi_date_line_intersection_count % 2 == 0: voronoi_ring.AddPoint(voronoi_dateline_intersection_point_X, voronoi_dateline_intersection_point_Y) """ When crossing back to the other hemisphere, the new polygon longitude must always be +179.999999 if it crosses from west to east and -179.999999 when crossing from east to west. This is the second intersection point with the date line. """ if voronoi_date_line_intersection_count % 2 == 0: if voronoi_dateline_hemisphere == "East": voronoi_dateline_intersection_point_X = 179.999999 if voronoi_dateline_hemisphere == "West": voronoi_dateline_intersection_point_X = -179.999999 """ add intersection point to second voronoi ring, voronoi_ring_dateline_intersection """ voronoi_ring_dateline_intersection.AddPoint(voronoi_dateline_intersection_point_X, voronoi_dateline_intersection_point_Y) if voronoi_date_line_intersection_count % 2 == 1: old_voronoi_dateline_intersection_point_X = voronoi_dateline_intersection_point_X old_voronoi_dateline_intersection_point_Y = voronoi_dateline_intersection_point_Y """ when entering back to the other hemisphere, add voronoi_ring_dateline_intersection to the voronoi polygon and create a new voronoi_ring_dateline_intersection polygon in case there is another date line intersection """ if voronoi_date_line_intersection_count % 2 == 0: """ add first date line intersection point to close ring """ """ When crossing back to the other hemisphere, the new polygon longitude must always be +179.999999 if it crosses from west to east and -179.999999 when crossing from east to west. This is the closing point for the polygon (the first intersection point). """ if voronoi_dateline_hemisphere == "East": old_voronoi_dateline_intersection_point_X = 179.999999 if voronoi_dateline_hemisphere == "West": old_voronoi_dateline_intersection_point_X = -179.999999 voronoi_ring_dateline_intersection.AddPoint(old_voronoi_dateline_intersection_point_X, old_voronoi_dateline_intersection_point_Y) """ add voronoi_ring_dateline_intersection to voronoi polygon """ voronoi_out_polygons.AddGeometry(voronoi_ring_dateline_intersection) """ get area of voronoi_ring_dateline_intersection. area of voronoi polygon is summed.""" voronoi_ring_dateline_intersection.Transform(geogr_to_proj_voronoi) voronoi_ring_dateline_intersection_area = voronoi_ring_dateline_intersection.GetArea() voronoi_ring_dateline_intersection.Transform(proj_voronoi_to_geogr) voronoi_polygon_area += voronoi_ring_dateline_intersection_area """ Create a new empty voronoi_ring_dateline_intersection in case there is another date line intersection coming up. """ voronoi_ring_dateline_intersection = ogr.Geometry(ogr.wkbLinearRing) voronoi_ring_dateline_intersection.AssignSpatialReference(voronoi_out_polygons_geogr_sr) """ remember current vertex """ previous_voronoi_geodesic_vertex_X = voronoi_geodesic_vertex_X previous_voronoi_geodesic_vertex_Y = voronoi_geodesic_vertex_Y """ Correct coordinates so that lon is between -179.999999 and +179.999999 deg, to avoid 'jumps' in polygons. Values outside this range are still used to determine the presence of date line intersections and the calculation of geodesic coordinates (Vincenty) """ if voronoi_geodesic_vertex_X < -179.999999: voronoi_geodesic_vertex_X += 360 if voronoi_geodesic_vertex_X > 179.999999: voronoi_geodesic_vertex_X -= 360 """ Vertices from geodesic distance calculations are added either to voronoi_ring (no date line intersection) or voronoi_ring_dateline_intersection (date line intersection). """ if voronoi_date_line_intersection_count % 2 == 0: voronoi_ring.AddPoint(voronoi_geodesic_vertex_X, voronoi_geodesic_vertex_Y) if voronoi_date_line_intersection_count % 2 == 1: voronoi_ring_dateline_intersection.AddPoint(voronoi_geodesic_vertex_X, voronoi_geodesic_vertex_Y) geodesic_voronoi_vertices_count += 1 voronoi_vertex_lat_lon_count += 1 """ Here, the processing of a voronoi polygon is finished. If during processing there was only one intersection with the dateline and no crossing back over the date line, the current voronoi polygon intersects the poles. Here, we draw a new polygon from the voronoi_ring and voronoi_ring_dateline_intersection vertices. We check whether there is a north pole or south pole intersection and add the respective pole and the longitudinally-ordered vertices to the polar intersection voronoi polygon. """ if voronoi_date_line_intersection_count % 2 == 1: """ get all vertices in voronoi_ring and voronoi_ring_dateline_intersection and sort them according to their longitudes """ voronoi_polar_intersection_vertices = [] for voronoi_polar_vertex in range(0, voronoi_ring_dateline_intersection.GetPointCount()): voronoi_polar_vertex_coordinates = voronoi_ring_dateline_intersection.GetPoint(voronoi_polar_vertex) voronoi_polar_vertex_lon = voronoi_polar_vertex_coordinates[0] voronoi_polar_vertex_lat = voronoi_polar_vertex_coordinates[1] voronoi_polar_intersection_vertices.append([voronoi_polar_vertex_lon, voronoi_polar_vertex_lat]) for voronoi_polar_vertex in range(0, voronoi_ring.GetPointCount()): voronoi_polar_vertex_coordinates = voronoi_ring.GetPoint(voronoi_polar_vertex) voronoi_polar_vertex_lon = voronoi_polar_vertex_coordinates[0] voronoi_polar_vertex_lat = voronoi_polar_vertex_coordinates[1] voronoi_polar_intersection_vertices.append([voronoi_polar_vertex_lon, voronoi_polar_vertex_lat]) voronoi_polar_intersection_vertices = sorted(voronoi_polar_intersection_vertices, key=lambda coordinates: coordinates[0]) """ find pole that is closest to the impact crater - we assume that this indicates whether this is an intersection with the north pole or the south pole """ inverse_vincenty(flattening, major_axis, center_voronoi_polygon_Y, center_voronoi_polygon_X, 89.99999, 0) distance_crater_north_pole = geodesic_distance_crater_area inverse_vincenty(flattening, major_axis, center_voronoi_polygon_Y, center_voronoi_polygon_X, -89.99999, 0) distance_crater_south_pole = geodesic_distance_crater_area if distance_crater_north_pole <= distance_crater_south_pole: voronoi_polar_vertex_Y = 89.99999 if distance_crater_north_pole > distance_crater_south_pole: voronoi_polar_vertex_Y = -89.99999 """ Draw a new polygon (voronoi_ring) for polar intersections. The poles and date lines are not exactly at +- 179.999999 deg lon or +-90 deg lat. Date lines are sometimes automatically converted from -179.999999 to +179.999999 degrees in OGR which is not very helpful in this case. Polar intersections may result in gaps if the latitude is set to +- 90 deg. """ voronoi_ring = ogr.Geometry(ogr.wkbLinearRing) voronoi_ring.AssignSpatialReference(voronoi_out_polygons_geogr_sr) voronoi_ring.AddPoint(-179.99999, voronoi_polar_vertex_Y) # closest pole for voronoi_polar_intersection_vertex in voronoi_polar_intersection_vertices: voronoi_polar_intersection_vertex_X = voronoi_polar_intersection_vertex[0] voronoi_polar_intersection_vertex_Y = voronoi_polar_intersection_vertex[1] if voronoi_polar_intersection_vertex_X == -179.999999: voronoi_polar_intersection_vertex_X = -179.99999 if voronoi_polar_intersection_vertex_X == 179.999999: voronoi_polar_intersection_vertex_X = 179.99999 voronoi_ring.AddPoint(voronoi_polar_intersection_vertex_X, voronoi_polar_intersection_vertex_Y) voronoi_ring.AddPoint(179.99999, voronoi_polar_vertex_Y) # closest pole voronoi_count += 1 print voronoi_count print "---" """ Get area of voronoi_ring. The total area is eventually summed from voronoi_ring and voronoi_ring_dateline_intersection due to inaccurate area measurements when one polygon intersects the date line intersections. """ voronoi_ring.Transform(geogr_to_proj_voronoi) voronoi_ring_area = voronoi_ring.GetArea() voronoi_ring.Transform(proj_voronoi_to_geogr) voronoi_polygon_area += voronoi_ring_area voronoi_polygon_areas.append(voronoi_polygon_area) """ Add geometry to shapefile """ voronoi_out_polygons.AddGeometry(voronoi_ring) voronoi_polygons_featureDefn = voronoi_polygon_layer.GetLayerDefn() voronoi_polygon_feature = ogr.Feature(voronoi_polygons_featureDefn) voronoi_polygon_feature.SetGeometry(voronoi_out_polygons) voronoi_polygon_feature.SetField("Area", voronoi_polygon_area) voronoi_polygon_layer.CreateFeature(voronoi_polygon_feature) print "Done."
def test_sort_vertices_of_regions(self): sv = SphericalVoronoi(self.points) unsorted_regions = sv.regions sv.sort_vertices_of_regions() assert_array_equal(sorted(sv.regions), sorted(unsorted_regions))
def getVoronoiCollection(data, lat_name, lon_name, bmap = None, v_name = None, full_sphere = False, max_v=.3, min_v=-0.3, cmap = cm.get_cmap('jet'), test_point = None, proj1=None, proj2=None, **kwargs): ''' Perform a Spherical Voronoi Tessellation on the input data. In the case where the data is restricted to one part of the globe, a polygon will not be returned for all objects, as matplotlib polygons won't be able to stretch over half the globe. @param data: Input pandas data frame @param lat_name: Name of latitude column @param lon_name: Name of longitude column @param bmap: Basemap instance used to convert from lat, lon coordinates to projection coordinates @param v_name: Name of value column. Use this to color each cell according to a value. @param full_sphere: Set to true if the data spans the entire globe. If false, a fictional point is created during tessellation and removed later to work around issues when polygons are suppose to span the over half the globe. @param max_v: Specify a maximum value to use when assigning values to the tessellation @param min_v: Specify a minimum value to use when assigning values to the tessellation @param cmap: Matplotlib color map to use @param test_point: Tuple containing the latitude and longitude of the ficitonal point to used to remove polygons that wrap around the earth. If none, a point is automatically chosen @param proj1: PyProj projection of input coordinates @param proj2: PyProj projection of sphere @return Matplotlib patch collection of tessellation, scipy.spatial.SphericalVoronoi object, integer index of objects in patch collection. ''' data = data.copy() if full_sphere == False: if test_point == None: test_lat = -1*np.mean(data[lat_name]) test_lon = np.mean(data[lon_name]) + 180 else: test_lat = test_point[0] test_lon = test_point[1] full_data = data full_data = pd.concat([full_data, pd.DataFrame({lat_name: test_lat, lon_name: test_lon}, index=[full_data.index[0]])]) full_data.set_index(np.arange(len(full_data)), inplace=True) else: full_data = data # print(full_data.tail()) if proj1 != None and proj2 != None: results = pyproj.transform(proj1, proj2, full_data[lon_name].as_matrix(), full_data[lat_name].as_matrix()) full_data[lon_name] = results[0] full_data[lat_name] = results[1] xyz = pd.DataFrame(sphericalToXYZ(full_data[lat_name], full_data[lon_name]),columns=['x','y','z'],index=full_data.index) if v_name != None: full_data = pd.concat([full_data.loc[:,[lat_name,lon_name, v_name]],xyz],axis=1) else: full_data = pd.concat([full_data.loc[:,[lat_name,lon_name, v_name]],xyz],axis=1) unique_index = np.unique(full_data.loc[:,lat_name] + 1j*full_data.loc[:,lon_name],return_index=True)[1] full_data = full_data.iloc[np.sort(unique_index)] voronoi = SphericalVoronoi(full_data.loc[:,['x','y','z']].as_matrix(), **kwargs) voronoi.sort_vertices_of_regions() #latlon_verts = xyzToSpherical(voronoi.vertices[:,0],voronoi.vertices[:,1], voronoi.vertices[:,2]) #if proj1 != None and proj2 != None: # results = pyproj.transform(proj2, proj1, latlon_verts[:,1], latlon_verts[:,0]) # latlon_verts[:, 1] = results[0] # latlon_verts[:, 0] = results[1] #matches = list(map(lambda x: find_match(x, voronoi.regions), range(len(voronoi.regions)))) #patch_list = [] #patch_index = [] #for i, (region,match,(station,row)) in enumerate(zip(voronoi.regions,matches, full_data.iterrows())): # if full_sphere or (len(matches)-1) not in match: # Keep an index of regions in patchcollection # patch_index.append(i) # if bmap != None: # xy = np.array(bmap(latlon_verts[region,1],latlon_verts[region,0])).T # else: # xy = np.array([latlon_verts[region,1],latlon_verts[region,0]]).T # if v_name != None: # value = row[v_name] # scaled_value = (value - min_v) / (max_v - min_v) # if scaled_value > 1: # scaled_value = 1.0 # elif scaled_value < 0: # scaled_value = 0.0 # poly = Polygon(xy, fill=True,facecolor = cmap(scaled_value),edgecolor=cmap(scaled_value)) # else: # poly = Polygon(xy, fill=False) # # patch_list.append(poly) return voronoi
x0, y0, z0 = 0, 0, 0 SphCart(1, np.random.rand(200)*np.pi*2, np.random.rand(200)*np.pi) points = np.random.normal(size = (3,200)) points /= np.linalg.norm(points, axis = 0) points = points.T radius = 1 center = np.array([x0, y0, z0]) sv = SphericalVoronoi(points, radius, center) # sort vertices (optional, helpful for plotting) sv.sort_vertices_of_regions() fig = plt.figure() ax = fig.add_subplot(111, projection='3d') # plot the unit sphere for reference (optional) u = np.linspace(0, 2 * np.pi, 100) v = np.linspace(0, np.pi, 100) x = np.outer(np.cos(u), np.sin(v)) y = np.outer(np.sin(u), np.sin(v))
class Model: @staticmethod def arrangedPointsOnSphere(n): indices = np.arange(n).astype(np.float32) + 0.5 thetas = np.pi * (1 + 5**0.5) * indices phis = np.arccos(1 - 2 * indices / indices.shape[0]) widths = np.sin(phis) heights = np.cos(phis) return np.array( [widths * np.cos(thetas), heights, widths * np.sin(thetas)]).T @staticmethod def randomPointsOnSphere(n): thetas = 2 * np.pi * np.random.sample(n).astype(np.float32) heights = 2 * np.random.sample(n).astype(np.float32) - 1 widths = (1 - heights**2)**0.5 return np.array( [widths * np.cos(thetas), heights, widths * np.sin(thetas)]).T def __init__(self, count): np.random.seed() self.vertices = self.randomPointsOnSphere(count) self.translations = np.zeros(self.vertices.shape, dtype=np.float32) self.invalidate() def invalidate(self): self.sv = None def needsUpdate(self): return self.sv is None def count(self): return self.vertices.shape[0] def temperature(self): return np.square(self.translations).sum() def addVerticesAt(self, positions): newVertices = positions / np.linalg.norm(positions, axis=1)[:, np.newaxis] newTranslations = np.zeros_like(newVertices) self.vertices = np.append(self.vertices, newVertices, axis=0) self.translations = np.append(self.translations, newTranslations, axis=0) self.invalidate() def addVertices(self, count): newVertices = self.randomPointsOnSphere(count) self.addVerticesAt(newVertices) def vertexIdAt(self, pos): return np.dot(self.vertices, pos).argmax() def resetVertex(self, i, pos): self.vertices[i] = pos / np.linalg.norm(pos) self.translations[i] = 0 self.invalidate() def resetAllVertices(self, random): makePoints = self.randomPointsOnSphere if random else self.arrangedPointsOnSphere self.vertices = makePoints(self.count()) self.translations[:] = 0 self.invalidate() def removeVertexIds(self, ids): self.vertices = np.delete(self.vertices, ids, axis=0) self.translations = np.delete(self.translations, ids, axis=0) self.invalidate() def removeVertices(self, count): count = min(count, self.count() - 4) ids = self.vertices.shape[0] - count + np.arange(count) self.removeVertexIds(ids) def _updateSV(self): self.sv = SphericalVoronoi(self.vertices) self.sv.sort_vertices_of_regions() def _updateVertices(self): self.allVertices = np.append(self.vertices, self.sv.vertices).astype(np.float32) def _updateLinks(self): links = [set() for _ in self.vertices] for simplex in self.sv._simplices: a, b, c = simplex[0], simplex[1], simplex[2] links[a].update((b, c)) links[b].update((a, c)) links[c].update((a, b)) self.degrees = np.empty(len(links), dtype=np.int32) self.links = np.empty(len(links), dtype=np.object_) for i, ends in enumerate(links): degree = len(ends) array = np.empty((degree, 2), np.int32) array[:, 0] = i array[:, 1] = np.fromiter(ends, np.int32) self.links[i] = array self.degrees[i] = degree def _updateBordersAndTris(self): self.tris = np.empty(len(self.sv.regions), dtype=np.object_) self.borders = np.empty(len(self.sv.regions), dtype=np.object_) for i, region in enumerate(self.sv.regions): array = np.array(region) + self.count() tris = np.empty((array.size, 3), np.int32) borders = np.empty((array.size, 2), np.int32) tris[:, 0] = i tris[0, 1] = borders[0, 0] = array[-1] tris[1:, 1] = borders[1:, 0] = array[:-1] tris[:, 2] = borders[:, 1] = array self.tris[i] = tris self.borders[i] = borders def updateGeometry(self): self._updateSV() self._updateVertices() self._updateLinks() self._updateBordersAndTris()
def voronoi_plot(points_in, losses, path, cmap): from matplotlib import colors from mpl_toolkits.mplot3d.art3d import Poly3DCollection import matplotlib.pyplot as plt from scipy.spatial import SphericalVoronoi from mpl_toolkits.mplot3d import proj3d # get input points in correct format cart = [point['cartesian'] for point in points_in] points = np.array(cart) center = np.array([0, 0, 0]) radius = 1 # calculate spherical Voronoi diagram sv = SphericalVoronoi(points, radius, center) # sort vertices (optional, helpful for plotting) sv.sort_vertices_of_regions() # generate plot fig = plt.figure() ax = fig.add_subplot(111, projection='3d') # plot the unit sphere for reference (optional) u = np.linspace(0, 2 * np.pi, 100) v = np.linspace(0, np.pi, 100) x = np.outer(np.cos(u), np.sin(v)) y = np.outer(np.sin(u), np.sin(v)) z = np.outer(np.ones(np.size(u)), np.cos(v)) ax.plot_surface(x, y, z, color='y', alpha=0.1) ax.grid(False) ax.set_xticks([]) ax.set_yticks([]) ax.set_zticks([]) plt.axis('off') # normalize and map losses to colormap mi = min(losses) ma = max(losses) norm = matplotlib.colors.Normalize(vmin=mi, vmax=ma, clip=True) mapper = cm.ScalarMappable(norm=norm, cmap=cmap) loss_color = [mapper.to_rgba(l) for l in losses] # indicate Voronoi regions (as Euclidean polygons) for i in range(len(sv.regions)): region = sv.regions[i] random_color = colors.rgb2hex(loss_color[i]) polygon = Poly3DCollection([sv.vertices[region]], alpha=1.0) polygon.set_color(random_color) ax.add_collection3d(polygon) fig.savefig(os.path.join(path, "landscape.png"), dpi=fig.dpi) # determine neighbours neighbours = [] for i in range(len(sv.regions)): vertices = sv.regions[i] neighbours_for_i = [] for j in range(len(sv.regions)): neigh = False shared = 0 vert2 = sv.regions[j] for vert in vertices: if vert in vert2: shared += 1 if shared >= 2: neigh = True break if neigh and i != j: neighbours_for_i.append(j) neighbours.append(neighbours_for_i) regions, steepest_neighbour = steepest_region(neighbours, losses) #plot_boundaries(regions, neighbours, sv, ax) temp = [] for i in range(len(neighbours)): point1 = sv.points[i] point2 = points[steepest_neighbour[i]] temp.append(point2 - point1) x1, y1, z1 = zip(*sv.points) dx, dy, dz = zip(*temp) col = [[0, 0, 0, 0.5] for i in range(len(neighbours))] #ax.quiver(x1, y1, z1, dx, dy, dz, length=0.02, normalize=True, colors=col) # uncomment this get directional arrows as well num_sets = len(regions) colors = cm.rainbow(np.linspace(0, 1, num_sets)) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') ax.plot_surface(x, y, z, color='y', alpha=0.1) ax.grid(False) ax.set_xticks([]) ax.set_yticks([]) ax.set_zticks([]) plt.axis('off') # indicate Voronoi regions (as Euclidean polygons) i = 0 centers = [] for reg in regions: set = regions[reg]['region_set'] centers.append(reg) for index in set: region = sv.regions[index] polygon = Poly3DCollection([sv.vertices[region]], alpha=1.0) polygon.set_color(colors[i]) ax.add_collection3d(polygon) i += 1 x_p, y_p, z_p = zip(*sv.points) x_p = [x_p[i] for i in centers] y_p = [y_p[i] for i in centers] z_p = [z_p[i] for i in centers] ax.scatter(x_p, y_p, z_p, c='k', s=1) fig.savefig(os.path.join(path, "regions.png"), dpi=fig.dpi) plt.show()
def getVoronoiCollection(data, lat_name, lon_name, bmap=None, v_name=None, full_sphere=False, max_v=.3, min_v=-0.3, cmap=matplotlib.cm.get_cmap('jet')): ''' Perform a Spherical Voronoi Tessellation on the input data. In the case where the data is restricted to one part of the globe, a polygon will not be returned for all objects, as matplotlib polygons won't be able to stretch over half the globe. @param data: Input pandas data frame @param lat_name: Name of latitude column @param lon_name: Name of longitude column @param bmap: Basemap instance used to convert from lat, lon coordinates to projection coordinates @param v_name: Name of value column. Use this to color each cell according to a value. @param full_sphere: Set to true if the data spans the entire globe. If false, a fictional point is created during tessellation and removed later to work around issues when polygons are suppose to span the over half the globe. @param max_v: Specify a maximum value to use when assigning values to the tessellation @param min_v: Specify a minimum value to use when assigning values to the tessellation @param cmap: Matplotlib color map to use @return Matplotlib patch collection of tessellation, scipy.spatial.SphericalVoronoi object, integer index of objects in patch collection. ''' if full_sphere == False: test_lat = -1 * np.mean(data[lat_name]) test_lon = np.mean(data[lon_name]) + 180 full_data = data full_data = pd.concat([ full_data, pd.DataFrame({ lat_name: test_lat, lon_name: test_lon }, index=[full_data.index[0]]) ]) full_data.set_index(np.arange(len(full_data)), inplace=True) else: full_data = data # print(full_data.tail()) xyz = pd.DataFrame(sphericalToXYZ(full_data[lat_name], full_data[lon_name]), columns=['x', 'y', 'z'], index=full_data.index) if v_name != None: full_data = pd.concat( [full_data.loc[:, [lat_name, lon_name, v_name]], xyz], axis=1) else: full_data = pd.concat( [full_data.loc[:, [lat_name, lon_name, v_name]], xyz], axis=1) unique_index = np.unique(full_data.loc[:, lat_name] + 1j * full_data.loc[:, lon_name], return_index=True)[1] full_data = full_data.iloc[np.sort(unique_index)] voronoi = SphericalVoronoi(full_data.loc[:, ['x', 'y', 'z']]) voronoi.sort_vertices_of_regions() latlon_verts = xyzToSpherical(voronoi.vertices[:, 0], voronoi.vertices[:, 1], voronoi.vertices[:, 2]) matches = list( map(lambda x: find_match(x, voronoi.regions), range(len(voronoi.regions)))) patch_list = [] patch_index = [] for i, (region, match, (station, row)) in enumerate( zip(voronoi.regions, matches, full_data.iterrows())): if full_sphere or (len(matches) - 1) not in match: # Keep an index of regions in patchcollection patch_index.append(i) if bmap != None: xy = np.array( bmap(latlon_verts[region, 1], latlon_verts[region, 0])).T else: xy = np.array( [latlon_verts[region, 1], latlon_verts[region, 0]]).T if v_name != None: value = row[v_name] scaled_value = (value - min_v) / (max_v - min_v) if scaled_value > 1: scaled_value = 1.0 elif scaled_value < 0: scaled_value = 0.0 poly = Polygon(xy, fill=True, facecolor=cmap(scaled_value), edgecolor=cmap(scaled_value)) else: poly = Polygon(xy, fill=False) patch_list.append(poly) return matplotlib.collections.PatchCollection( patch_list, match_original=True), voronoi, patch_index
def analyze_protein(protein): prot = protein[0] chain = protein[1] print("Downloading {}{}.".format(prot, chain)) cmd = "wget https://files.rcsb.org/download/{}.pdb".format(prot) proc = Popen(shlex.split(cmd), stdout=DEVNULL, stderr=STDOUT) proc.communicate() print("Extracting coordinates.") cmd = "./pdb_to_xyz.R {}.pdb --chain-id={} -o {}.txt".format( prot, chain, prot) proc = Popen(shlex.split(cmd), stdout=DEVNULL, stderr=STDOUT) proc.communicate() print("Removing pdb file.") proc = Popen(["rm", "{}.pdb".format(prot)], stdout=DEVNULL, stderr=STDOUT) proc.communicate() print("Analyzing protein") for proj in projections: cmd = "./polynomial_invariant {}.txt --names-db=internal --arrow --nb-projections={} --output-diagram={}{}_gl_{}.txt".format( prot, proj, prot, chain, proj) proc = Popen(shlex.split(cmd), stdout=DEVNULL, stderr=STDOUT) proc.communicate() name = "{}{}_gl_{}.txt".format(prot, chain, proj) origins, origin_types = [], [] file = open(name, "r") next(file) me = 0 checked = [] vertex_to_index_list = dict() edges_to_add = [] for l in file: line = l.strip('\n').split('\t') if line[3] != 'UNKNOWN': if '*' not in line[3]: origin_types.append(line[3]) origins.append( [float(line[0]), float(line[1]), float(line[2])]) if line[3] not in checked: vertex_to_index_list[repr(line[3])] = me me += 1 origins = numpy.array(origins) voro = SphericalVoronoi(origins) voro.sort_vertices_of_regions() for i in range(len(voro.regions)): for j in range(i + 1, len(voro.regions)): intersect = list(set(voro.regions[i]) & set(voro.regions[j])) if intersect: permissable = set( [1, len(voro.regions[i]), len(voro.regions[j])]) index_A = [voro.regions[i].index(x) for x in intersect] diff_A = [ abs(j - i) for i, j in zip(index_A[:-1], index_A[1:]) ] index_B = [voro.regions[j].index(x) for x in intersect] diff_B = [ abs(j - i) for i, j in zip(index_B[:-1], index_B[1:]) ] # print ("intersect",intersect,"voro[i]",voro.regions[i],"voro[j]",voro.regions[j]) # print ("index_A",index_A,"diff_A",diff_A,"index_B",index_B,"diff_B",diff_B) # print ("perm_A",permissable & set(diff_A),"perm_B", permissable & set(diff_B)) if permissable & set(diff_A) and permissable & set(diff_B): for k in range(int(len(intersect) / 2)): edges_to_add.append( (vertex_to_index_list[repr(origin_types[i])], vertex_to_index_list[repr(origin_types[j])])) # Comment lines 155 - 165 and uncomment lines 169-170 to go to previous version. # for k in range(int(len(intersect)/2)): # edges_to_add.append((vertex_to_index_list[repr(origin_types[i])],vertex_to_index_list[repr(origin_types[j])])) # print ("len",len(voro.vertices)) print("Creating graph.") g = igraph.Graph(len(origin_types), directed=False) origin_types = [ x.split("|")[0].strip('m').strip('s') for x in origin_types ] g.vs["label"] = [x for x in origin_types] g.add_edges(edges_to_add) all_connections = [] for edge in g.es: src = g.vs[edge.source]['label'] tgt = g.vs[edge.target]['label'] if (src == '3_1' and tgt != '3_1'): all_connections.append((src, tgt)) elif (src != '3_1' and tgt == '3_1'): all_connections.append((tgt, src)) proj_errors = 0 error_pairs = [] for pair in all_connections: if theoretical_values[pair[0]][pair[1]] != 1: proj_errors += 1 error_pairs.append(pair) try: errors = round(proj_errors / len(all_connections), 4) except ZeroDivisionError: errors = 'NaN' edges_between = [] for edge in g.es: src = g.vs[edge.source]['label'] tgt = g.vs[edge.target]['label'] if (src == '3_1' and tgt == '2_1'): edges_between.append((src, tgt)) elif (src == '2_1' and tgt == '3_1'): edges_between.append((tgt, src)) if len(edges_between) > 0: try: interface = round(len(edges_between) / len(all_connections), 4) except ZeroDivisionError: interface = 'NaN' else: interface = 'NaN' area_31, area_21 = [], [] all_area_31, all_area_21 = [], [] # voro_areas = voro.calculate_areas() print("voro_areas: ", voro_areas) for i in range(len(voro.regions)): voronoi_cell = numpy.asarray( [list(voro.vertices[x]) for x in voro.regions[i]]) if origin_types[i] == '3_1': sph_pol = SphericalPolygon(voronoi_cell) area_31.append(sph_pol.area()) elif origin_types[i] == '2_1': sph_pol = SphericalPolygon(voronoi_cell) area_21.append(sph_pol.area()) all_area_31 = numpy.sum(area_31) all_area_21 = numpy.sum(area_21) all_graphs.append({ 'n': str(proj), 'Error': errors, 'Interface': interface, 'Protein': prot + chain, 'Spectrum': len(set(origin_types)), 'Error Pairs': set(error_pairs), 'Full Spectrum': set(origin_types), 'Area 3_1': all_area_31, 'Area 2_1': all_area_21, 'Area 2_1/3_1': all_area_21 / all_area_31 })
def getVoronoiCollection(data, lat_name, lon_name, bmap=None, v_name=None, full_sphere=False, max_v=.3, min_v=-0.3, cmap=cm.get_cmap('jet'), test_point=None, proj1=None, proj2=None, **kwargs): ''' Perform a Spherical Voronoi Tessellation on the input data. In the case where the data is restricted to one part of the globe, a polygon will not be returned for all objects, as matplotlib polygons won't be able to stretch over half the globe. @param data: Input pandas data frame @param lat_name: Name of latitude column @param lon_name: Name of longitude column @param bmap: Basemap instance used to convert from lat, lon coordinates to projection coordinates @param v_name: Name of value column. Use this to color each cell according to a value. @param full_sphere: Set to true if the data spans the entire globe. If false, a fictional point is created during tessellation and removed later to work around issues when polygons are suppose to span the over half the globe. @param max_v: Specify a maximum value to use when assigning values to the tessellation @param min_v: Specify a minimum value to use when assigning values to the tessellation @param cmap: Matplotlib color map to use @param test_point: Tuple containing the latitude and longitude of the ficitonal point to used to remove polygons that wrap around the earth. If none, a point is automatically chosen @param proj1: PyProj projection of input coordinates @param proj2: PyProj projection of sphere @return Matplotlib patch collection of tessellation, scipy.spatial.SphericalVoronoi object, integer index of objects in patch collection. ''' data = data.copy() if full_sphere == False: if test_point == None: test_lat = -1 * np.mean(data[lat_name]) test_lon = np.mean(data[lon_name]) + 180 else: test_lat = test_point[0] test_lon = test_point[1] full_data = data full_data = pd.concat([ full_data, pd.DataFrame({ lat_name: test_lat, lon_name: test_lon }, index=[full_data.index[0]]) ]) full_data.set_index(np.arange(len(full_data)), inplace=True) else: full_data = data # print(full_data.tail()) if proj1 != None and proj2 != None: results = pyproj.transform(proj1, proj2, full_data[lon_name].as_matrix(), full_data[lat_name].as_matrix()) full_data[lon_name] = results[0] full_data[lat_name] = results[1] xyz = pd.DataFrame(sphericalToXYZ(full_data[lat_name], full_data[lon_name]), columns=['x', 'y', 'z'], index=full_data.index) if v_name != None: full_data = pd.concat( [full_data.loc[:, [lat_name, lon_name, v_name]], xyz], axis=1) else: full_data = pd.concat( [full_data.loc[:, [lat_name, lon_name, v_name]], xyz], axis=1) unique_index = np.unique(full_data.loc[:, lat_name] + 1j * full_data.loc[:, lon_name], return_index=True)[1] full_data = full_data.iloc[np.sort(unique_index)] voronoi = SphericalVoronoi(full_data.loc[:, ['x', 'y', 'z']].as_matrix(), **kwargs) voronoi.sort_vertices_of_regions() #latlon_verts = xyzToSpherical(voronoi.vertices[:,0],voronoi.vertices[:,1], voronoi.vertices[:,2]) #if proj1 != None and proj2 != None: # results = pyproj.transform(proj2, proj1, latlon_verts[:,1], latlon_verts[:,0]) # latlon_verts[:, 1] = results[0] # latlon_verts[:, 0] = results[1] #matches = list(map(lambda x: find_match(x, voronoi.regions), range(len(voronoi.regions)))) #patch_list = [] #patch_index = [] #for i, (region,match,(station,row)) in enumerate(zip(voronoi.regions,matches, full_data.iterrows())): # if full_sphere or (len(matches)-1) not in match: # Keep an index of regions in patchcollection # patch_index.append(i) # if bmap != None: # xy = np.array(bmap(latlon_verts[region,1],latlon_verts[region,0])).T # else: # xy = np.array([latlon_verts[region,1],latlon_verts[region,0]]).T # if v_name != None: # value = row[v_name] # scaled_value = (value - min_v) / (max_v - min_v) # if scaled_value > 1: # scaled_value = 1.0 # elif scaled_value < 0: # scaled_value = 0.0 # poly = Polygon(xy, fill=True,facecolor = cmap(scaled_value),edgecolor=cmap(scaled_value)) # else: # poly = Polygon(xy, fill=False) # # patch_list.append(poly) return voronoi
def generate(self, N, iterate=5): points = self._samplePoints(N) relaxedPoints = self._relaxPoints(points, iterate) sv = SphericalVoronoi(relaxedPoints) sv.sort_vertices_of_regions() return sv