I'm trying to (roughly) equally space the points of a line to a predefined distance.
It's ok to have some tolerance between the distances but as close as possible would be desirable.
I know I could manually iterate through each point in my line and check the p1 distance vs p2 and add more points if needed.
But I wondered if anyone knows if there is a way to achieve this with shapely as I already have the coords in a LineString.
One way to do that is to use interpolate method that returns points at specified distances along the line. You just have to generate a list of the distances somehow first. Taking the input line example from Roy2012's answer:
import numpy as np
from shapely.geometry import LineString
from shapely.ops import unary_union
line = LineString(([0, 0], [2, 1], [3, 2], [3.5, 1], [5, 2]))
Splitting at a specified distance:
distance_delta = 0.9
distances = np.arange(0, line.length, distance_delta)
# or alternatively without NumPy:
# points_count = int(line.length // distance_delta) + 1
# distances = (distance_delta * i for i in range(points_count))
points = [line.interpolate(distance) for distance in distances] + [line.boundary[1]]
multipoint = unary_union(points) # or new_line = LineString(points)
Note that since the distance is fixed you can have problems at the end of the line as shown in the image. Depending on what you want you can include/exclude the [line.boundary[1]] part which adds the line's endpoint or use distances = np.arange(0, line.length, distance_delta)[:-1] to exclude the penultimate point.
Also, note that the unary_union I'm using should be more efficient than calling object.union(other) inside a loop, as shown in another answer.
Splitting to a fixed number of points:
n = 7
# or to get the distances closest to the desired one:
# n = round(line.length / desired_distance_delta)
distances = np.linspace(0, line.length, n)
# or alternatively without NumPy:
# distances = (line.length * i / (n - 1) for i in range(n))
points = [line.interpolate(distance) for distance in distances]
multipoint = unary_union(points) # or new_line = LineString(points)
You can use the shapely substring operation:
from shapely.geometry import LineString
from shapely.ops import substring
line = LineString(([0, 0], [2, 1], [3,2], [3.5, 1], [5, 2]))
mp = shapely.geometry.MultiPoint()
for i in np.arange(0, line.length, 0.2):
s = substring(line, i, i+0.2)
mp = mp.union(s.boundary)
The result for this data is given below. Each circle is a point.
Related
I have multiple Lines in 3D space. Each Line is described by 2 points, a start point, and a stop point. I'm trying to find the Line that best 'fits' these Lines (minimize the distance between the new line, and the other Lines). Ideally, I'd also like to know the least-square distance of the new line to the other lines (or some other statistic for deciding whether it is a good fit or not).
Below is Code I've written so far that does what I want, but I feel there is a faster, analytical way to calculate it, and speed is extremely important for my application.
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize._minimize import minimize
import time
def min_line(FINAL_LINE,LINES):
sqr=0
x0,x1,y0,y1,z0,z1=FINAL_LINE
#for each original line, calculate the distance from it to the new line
for l in LINES:
p1,p2,dist=closestDistanceBetweenLines(l[0],l[1],np.array([x0,y0,z0]),np.array([x1,y1,z1]))
#add that distance to the total
sqr+=np.square(dist)
return sqr
def closestDistanceBetweenLines(a0,a1,b0,b1):
'''
https://stackoverflow.com/questions/2824478/shortest-distance-between-two-line-segments
Given two lines defined by numpy.array pairs (a0,a1,b0,b1)
Return the closest points on each segment and their distance
'''
# Calculate denomitator
A = a1 - a0
B = b1 - b0
magA = np.linalg.norm(A)
magB = np.linalg.norm(B)
_A = A / magA
_B = B / magB
cross = np.cross(_A, _B);
denom = np.linalg.norm(cross)**2
# If lines are parallel (denom=0) test if lines overlap.
# If they don't overlap then there is a closest point solution.
# If they do overlap, there are infinite closest positions, but there is a closest distance
if not denom:
d0 = np.dot(_A,(b0-a0))
# Segments overlap, return distance between parallel segments
return [],[],np.linalg.norm(((d0*_A)+a0)-b0)
# Lines criss-cross: Calculate the projected closest points
t = (b0 - a0);
detA = np.linalg.det([t, _B, cross])
detB = np.linalg.det([t, _A, cross])
t0 = detA/denom;
t1 = detB/denom;
pA = a0 + (_A * t0) # Projected closest point on segment A
pB = b0 + (_B * t1) # Projected closest point on segment B
return pA,pB,np.linalg.norm(pA-pB)
if __name__ == '__main__':
#setup the 3d figure for plotting
fig = plt.figure()
ax = plt.axes(projection='3d')
#4 sets of start/stop points forming 4 lines
a_start=np.array([0, 0, 0])
a_end=np.array([0.1, 1, 1])
b_start=np.array([0, 2, 0])
b_end=np.array([-0.1, 1, 1])
c_start=np.array([1, 0, 0])
c_end=np.array([.9, 1, 1])
d_start=np.array([1, 2, 0])
d_end=np.array([1.1, 1, 1])
e_start=np.array([1, 0, 0])
e_end=np.array([.9, 1, 2])
f_start=np.array([1, 2, 0])
f_end=np.array([1.1, 1, 2])
#turn them into a single Numpy array
a=[a_start,a_end]
b=[b_start,b_end]
c=[c_start,c_end]
d=[d_start,d_end]
e=[e_start,e_end]
f=[f_start,f_end]
all_points=np.array([a,b,c,d])
if(len(all_points)>2):
rets=minimize(min_line,[0,0,0,1,1,1], all_points)
print(rets)
#plot the original lines Green
for p in all_points:
ax.plot3D([p[0][0],p[1][0]],[p[0][1],p[1][1]],[p[0][2],p[1][2]],'g')
#plot the new, fit line Black
ax.plot3D([rets.x[0],rets.x[1]],[rets.x[2],rets.x[3]],[rets.x[4],rets.x[5]],'k')
plt.show()
Each Lines Starting point will always remain the same. The Starting points will never be the same as each other (they are all coplanar though). There will always be at least 3 Starting Lines.
Current Code Fitted to Lines A,B,C,D.
I have P_0 points spread randomly in a 2d box. Then I divide them in two groups S and I. If some points of S come too close to I, they are deleted from the S group and added to the I group. The problem I am facing is that sometimes they are not correctly deleted from S, but they are properly added to I. Hence, the total number of points keeps erroneously growing.
Here is the code:
from scipy.spatial import cKDTree
import numpy as np
import matplotlib.pyplot as plt
P_0 = 100 # initial susceptible population
# dimensions of box
Lx = 5.0
Ly = 5.0
# generate P_0 random points inside box
X = np.random.uniform(0, Lx, P_0)
Y = np.random.uniform(0, Ly, P_0)
pts = np.column_stack((X, Y)) # array of 2d points
S = np.arange(10, P_0) # indices of the susceptible
I = np.arange(10) # indices of the infected
# Divide points into infected and susceptible groups
r_I = pts[I]
r_S = pts[S]
tree = cKDTree(r_S)
# idx represents the indices to points in r_S which are closer than r to
# points in r_I
idx = tree.query_ball_point(r_I, r=0.4)
idx = np.hstack(idx) # flatten the lists into one numpy array
idx = idx.astype(int) # Make sure idx indices have int type
print idx
# plot points
plt.figure()
plt.plot (r_S[:, 0], r_S[:, 1], 'bo') # plot all r_S points
plt.plot (r_S[idx, 0], r_S[idx, 1], 'ko') # color those points nearest to r_I
plt.plot (r_I[:, 0], r_I[:, 1], 'ro') # identify the r_I points
print len(S), len(I), len(S)+len(I)
I= np.append(I, S[idx]) # add the closest points to I
S = np.delete(S, idx) # delete the closest points from S
# points in r_I
idx = tree.query_ball_point(r_I, r=0.4)
idx = np.hstack(idx) # flatten the lists into one numpy array
idx = idx.astype(int) # Make sure idx indices have int type
print idx
# plot points
plt.figure()
plt.plot (r_S[:, 0], r_S[:, 1], 'bo') # plot all r_S points
plt.plot (r_S[idx, 0], r_S[idx, 1], 'ko') # color those points nearest to r_I
plt.plot (r_I[:, 0], r_I[:, 1], 'ro') # identify the r_I points
print len(S), len(I), len(S)+len(I)
I= np.append(I, S[idx]) # add the closest points to I
S = np.delete(S, idx) # delete the closest points from S
plt.figure('S group')
plt.plot (pts[S, 0], pts[S, 1], 'bo') # plot the updated r_S points
plt.figure('I group')
plt.plot (pts[I, 0], pts[I, 1], 'ro') # plot the updated r_I points
print len(S), len(I), len(S)+len(I), len(idx)
plt.show()
So, I don't know why not all points in r_S closer than r, sometimes aren't deleted from S.
One might have to run the code a few times for the error to appear, or just increase P_0 to 1000 for example or increase the value of r. it might be a problem with idx and the way I am using numpy delete.
You could double check your assumption by swapping the actions (doing a move by doing the deletion first and the addition second, only on a successful deletion) and testing the deletion in a separate variable.
Compare the resulting size after deletion with the original size of the group. If sizes match, no deletion had occurred (for whatever reason), which is a signal not to add something on the other side.
Then you could have a print of groups on the upper case and see the indexes which participate to shed some light into the tunnel.
As I just commented, I just had to eliminate the duplicates in idx.
I added the line
idx = np.unique(idx)
just below idx = idx.astype(int)
I have two points on a line, say X1(x1,y1,z1) and X2(x2,y2,z2) and I have an external point X(x0,y0,z0) nearby this line. I want to write a python code that will return me coordinate of point D(x,y,z) on the line X1X2 such that line XD is perpendicular to X1X2 (projection). This problem is close to previously discussed one at here. I am looking for a working script in python for the 3D coordinates.
Thanks
Thanks MedAli for the suggestion. I end-up with the following solution:
""" Calculates coordinates of projection point of a P0
point on the line is the line(p1,p2) """
p = np.array(p0)
a = np.array(p1)
b = np.array(p2)
ap = p - a
ab = b - a
proPoint = a + np.dot(ap, ab)/np.dot(ab,ab) * ab
return proPoint
You can use the scikit-spatial library.
Having two points, you can define the line in the following way:
from skspatial.objects import Line, Point
point_x1 = Point([3, 6, 1])
point_x2 = Point([2, 1, 10])
line = Line.from_points(point_x1, point_x2)
Then the projected point on the line can be computed as:
point_x = Point([0, -1, -7])
point_xd = line.project_point(point_x)
I'm trying to split a Shapely LineString at the nearest point to some other coordinate. I can get the closest point on the line using project and interpolate but I am unable to split the line at this point as it is not a vertex.
I need to split the line along the edge, not snapped to the nearest vertex, so that the nearest point becomes a new vertex on the line.
Here's what I've done so far:
from shapely.ops import split
from shapely.geometry import Point, LineString
line = LineString([(0, 0), (5,8)])
point = Point(2,3)
# Find coordinate of closest point on line to point
d = line.project(point)
p = line.interpolate(d)
print(p)
# >>> POINT (1.910112359550562 3.056179775280899)
# Split the line at the point
result = split(line, p)
print(result)
# >>> GEOMETRYCOLLECTION (LINESTRING (0 0, 5 8))
Thanks!
As it turns out the answer I was looking for was outlined in the documentation as the cut method:
def cut(line, distance):
# Cuts a line in two at a distance from its starting point
if distance <= 0.0 or distance >= line.length:
return [LineString(line)]
coords = list(line.coords)
for i, p in enumerate(coords):
pd = line.project(Point(p))
if pd == distance:
return [
LineString(coords[:i+1]),
LineString(coords[i:])]
if pd > distance:
cp = line.interpolate(distance)
return [
LineString(coords[:i] + [(cp.x, cp.y)]),
LineString([(cp.x, cp.y)] + coords[i:])]
Now I can use the projected distance to cut the LineString:
...
d = line.project(point)
# print(d) 3.6039927920216237
cut(line, d)
# LINESTRING (0 0, 1.910112359550562 3.056179775280899)
# LINESTRING (1.910112359550562 3.056179775280899, 5 8)
I have about 50,000 data points in 3D on which I have run scipy.spatial.Delaunay from the new scipy (I'm using 0.10) which gives me a very useful triangulation.
Based on: http://en.wikipedia.org/wiki/Delaunay_triangulation (section "Relationship with the Voronoi diagram")
...I was wondering if there is an easy way to get to the "dual graph" of this triangulation, which is the Voronoi Tesselation.
Any clues? My searching around on this seems to show no pre-built in scipy functions, which I find almost strange!
Thanks,
Edward
The adjacency information can be found in the neighbors attribute of the Delaunay object. Unfortunately, the code does not expose the circumcenters to the user at the moment, so you'll have to recompute those yourself.
Also, the Voronoi edges that extend to infinity are not directly obtained in this way. It's still probably possible, but needs some more thinking.
import numpy as np
from scipy.spatial import Delaunay
points = np.random.rand(30, 2)
tri = Delaunay(points)
p = tri.points[tri.vertices]
# Triangle vertices
A = p[:,0,:].T
B = p[:,1,:].T
C = p[:,2,:].T
# See http://en.wikipedia.org/wiki/Circumscribed_circle#Circumscribed_circles_of_triangles
# The following is just a direct transcription of the formula there
a = A - C
b = B - C
def dot2(u, v):
return u[0]*v[0] + u[1]*v[1]
def cross2(u, v, w):
"""u x (v x w)"""
return dot2(u, w)*v - dot2(u, v)*w
def ncross2(u, v):
"""|| u x v ||^2"""
return sq2(u)*sq2(v) - dot2(u, v)**2
def sq2(u):
return dot2(u, u)
cc = cross2(sq2(a) * b - sq2(b) * a, a, b) / (2*ncross2(a, b)) + C
# Grab the Voronoi edges
vc = cc[:,tri.neighbors]
vc[:,tri.neighbors == -1] = np.nan # edges at infinity, plotting those would need more work...
lines = []
lines.extend(zip(cc.T, vc[:,:,0].T))
lines.extend(zip(cc.T, vc[:,:,1].T))
lines.extend(zip(cc.T, vc[:,:,2].T))
# Plot it
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
lines = LineCollection(lines, edgecolor='k')
plt.hold(1)
plt.plot(points[:,0], points[:,1], '.')
plt.plot(cc[0], cc[1], '*')
plt.gca().add_collection(lines)
plt.axis('equal')
plt.xlim(-0.1, 1.1)
plt.ylim(-0.1, 1.1)
plt.show()
As I spent a considerable amount of time on this, I'd like to share my solution on how to get the Voronoi polygons instead of just the edges.
The code is at https://gist.github.com/letmaik/8803860 and extends on the solution of tauran.
First, I changed the code to give me vertices and (pairs of) indices (=edges) separately, as many calculations can be simplified when working on indices instead of point coordinates.
Then, in the voronoi_cell_lines method I determine which edges belong to which cells. For that I use the proposed solution of Alink from a related question. That is, for each edge find the two nearest input points (=cells) and create a mapping from that.
The last step is to create the actual polygons (see voronoi_polygons method). First, the outer cells which have dangling edges need to be closed. This is as simple as looking through all edges and checking which ones have only one neighboring edge. There can be either zero or two such edges. In case of two, I then connect these by introducing an additional edge.
Finally, the unordered edges in each cell need to be put into the right order to derive a polygon from them.
The usage is:
P = np.random.random((100,2))
fig = plt.figure(figsize=(4.5,4.5))
axes = plt.subplot(1,1,1)
plt.axis([-0.05,1.05,-0.05,1.05])
vertices, lineIndices = voronoi(P)
cells = voronoi_cell_lines(P, vertices, lineIndices)
polys = voronoi_polygons(cells)
for pIdx, polyIndices in polys.items():
poly = vertices[np.asarray(polyIndices)]
p = matplotlib.patches.Polygon(poly, facecolor=np.random.rand(3,1))
axes.add_patch(p)
X,Y = P[:,0],P[:,1]
plt.scatter(X, Y, marker='.', zorder=2)
plt.axis([-0.05,1.05,-0.05,1.05])
plt.show()
which outputs:
The code is probably not suitable for large numbers of input points and can be improved in some areas. Nevertheless, it may be helpful to others who have similar problems.
I came across the same problem and built a solution out of pv.'s answer and other code snippets I found across the web. The solution returns a complete Voronoi diagram, including the outer lines where no triangle neighbours are present.
#!/usr/bin/env python
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from scipy.spatial import Delaunay
def voronoi(P):
delauny = Delaunay(P)
triangles = delauny.points[delauny.vertices]
lines = []
# Triangle vertices
A = triangles[:, 0]
B = triangles[:, 1]
C = triangles[:, 2]
lines.extend(zip(A, B))
lines.extend(zip(B, C))
lines.extend(zip(C, A))
lines = matplotlib.collections.LineCollection(lines, color='r')
plt.gca().add_collection(lines)
circum_centers = np.array([triangle_csc(tri) for tri in triangles])
segments = []
for i, triangle in enumerate(triangles):
circum_center = circum_centers[i]
for j, neighbor in enumerate(delauny.neighbors[i]):
if neighbor != -1:
segments.append((circum_center, circum_centers[neighbor]))
else:
ps = triangle[(j+1)%3] - triangle[(j-1)%3]
ps = np.array((ps[1], -ps[0]))
middle = (triangle[(j+1)%3] + triangle[(j-1)%3]) * 0.5
di = middle - triangle[j]
ps /= np.linalg.norm(ps)
di /= np.linalg.norm(di)
if np.dot(di, ps) < 0.0:
ps *= -1000.0
else:
ps *= 1000.0
segments.append((circum_center, circum_center + ps))
return segments
def triangle_csc(pts):
rows, cols = pts.shape
A = np.bmat([[2 * np.dot(pts, pts.T), np.ones((rows, 1))],
[np.ones((1, rows)), np.zeros((1, 1))]])
b = np.hstack((np.sum(pts * pts, axis=1), np.ones((1))))
x = np.linalg.solve(A,b)
bary_coords = x[:-1]
return np.sum(pts * np.tile(bary_coords.reshape((pts.shape[0], 1)), (1, pts.shape[1])), axis=0)
if __name__ == '__main__':
P = np.random.random((300,2))
X,Y = P[:,0],P[:,1]
fig = plt.figure(figsize=(4.5,4.5))
axes = plt.subplot(1,1,1)
plt.scatter(X, Y, marker='.')
plt.axis([-0.05,1.05,-0.05,1.05])
segments = voronoi(P)
lines = matplotlib.collections.LineCollection(segments, color='k')
axes.add_collection(lines)
plt.axis([-0.05,1.05,-0.05,1.05])
plt.show()
Black lines = Voronoi diagram, Red lines = Delauny triangles
I do not know of a function to do this, but it does not seem like an overly complicated task.
The Voronoi graph is the junction of the circumcircles, as described in the wikipedia article.
So you could start with a function that finds the center of the circumcircles of a triangle, which is basic mathematics (http://en.wikipedia.org/wiki/Circumscribed_circle).
Then, just join centers of adjacent triangles.