I am trying to determine how to return coordinates of shared pixels from shared edges in an image (example image above). Essentially, three things would be returned.
An image that draws over the shared edges in red (or another colour,
it doesn't matter.
A dataframe that lists the shared pixel coordinates.
What I call a complexity index, which essentially lists which
polygon has the most shared edges. So, using the image provided,
polygon 1 has one shared edge, polygons 2 and 3 have three shared
edges, polygon 4 has two shared edges, and polygon 5 has one shared
edge.
I've had no real luck with this. Below is some code that I've attempted. I've started by doing contour selection and creating a nested dictionary (contour_df) with image contour data (contour_df) that lists all the pixel information from my image. A big problem I'm running into off the bat is that itertools.permutations is running every pixel coordinate combination, which takes forever, and I can't get past it to try anything else.
Ultimately, I believe I'm approaching this problem incorrectly and may need to start from scratch. Thanks for any and all suggestions.
def complexity_estimator(contour_df):
adjacency_list = []
for i in range(0, contour_df.shape[0]):
if contour_df.iloc[i]["parent_index"] == -1:
adjacency_list.append(0)
else:
# list coordinates for the contour we are interested In
contour_coordinate = contour_df.iloc[i]["contour"]
# list of coordinates of each of the siblings that we are interested In (list of listS)
contour_coordinate_siblings = contour_df[contour_df["parent_index"] == contour_df.iloc[i]["parent_index"]]['contour'].values
count = 0
for sibling_contour in contour_coordinate_siblings:
# compare contour_coordinate with sibling_contour
adjacent = complexity_measure(contour_coordinate,sibling_contour)
if adjacent == True:
count = count + 1
adjacency_list.append(count)
contour_df['complexity'] = adjacency_list
return contour_df
def complexity_measure(contour_coordinates1, contour_coordinates2):
if np.array_equal(contour_coordinates1, contour_coordinates2):
return False
else:
new_pairs = [list(zip(x,contour_coordinates2)) for x in itertools.permutations(contour_coordinates2,len(contour_coordinates2))]
print (new_pairs)
#dist = math.hypot(x2 - x1, y2 - y1)
return True
Related
On a 2d plane, there is a large circle centered at (0, 0) with a radius of Ro. It encloses 100 or so smaller circles distributed randomly across the parent circle with unique properties (e.g. radii and positions) each one associated with a unique integer ID. (It is possible that some smaller sub-circles are partially or entirely inside some larger sub-circles.)
The entire plane is gridded uniformly into pixels with sides being horizontal and vertical (along coordinate axes). The size of the pixels is fixed and known a priori but otherwise much smaller than the size of parent circle; there are on the order of few times 10^5 all over the parent circle.
1% of the area of the parent circle is colored in the form of a few clumps across the parent circle covering 10^3 pixels. These colored pixels are mostly inside sub-circles; all are entirely inside the parent circle. We are given the 2D Cartesian coordinates for (the centers of) all colored grids.
Each colored grid is associated with the smallest sub-circle that contains it. If the pixel falls within multiple sub-circles, only the smallest of the circles should be chosen.
Finally, I would like to calculate the total number of colored grids associated with each sub-circle subject to the above condition. To write this in python, one should store all of the colored pixels in a 2d array. Then, for each sub-circle, one should query the array to find all grids contained within that sub-circle. For each such grid, one should test whether it is inside the sub-circle. If it is, one should mark that grid as associated with that sub-circle, unless it is already associated with a smaller sub-circle.
Here is my try:
totals = {}
sub_circle_catalog = []
for x, y in zip(vals1, vals2):
enclosing_circles = {}
for id, position, radius in zip(ids_data, positions_data, radiuss_data):
if (np.sqrt(pow(x-position[0], 2)+pow(y-position[1], 2)) < radius):
enclosing_circles[id] = float('{:.3f}'.format(radius))
# choose the corresponding ID with the smallest radius here
smallest_enclosing_circle = min(enclosing_circles, key=enclosing_circles.get)
sub_circle_catalog.append((enclosing_circles[smallest_enclosing_circle], 1))
# add up all special grids in each sub-circle when looping over all grids
for key, value in sub_circle_catalog:
totals[key] = totals.get(key, 0) + value
I want to know if I am doing the association correctly. I don't know how to implement the constraint that each grid is assigned only once to a sub-circle. In particular, how to loop over all special grids in order to calculate total budget of each sub-circle?
After finding all sub-circles containing the special colorful grids, one needs to choose the smallest sub-circle. I was doing this incorrectly in the sense that it was done before all sub-circles are looped over. However, my correction here was that I made sure that all sub-circles are looped over for any single special grid and just before moving to the next special grid in the row, I need to assert a line that takes care of the smallest local sub-circle and store it into some list. As each special grid is taken care of this way, all the smallest sub-circles are added to the empty list (initialized at the beginning). Once all special grids are investigated this way, we can add the sum of the individual contributions to the empty dictionary (initialized at the beginning). All this is being done in the same scope as that of the first for statement. Here is the correct code:
totals = {}
sub_circle_catalog = []
for x, y in zip(vals1, vals2):
enclosing_circles = {}
for id, position, radius in zip(ids_data, positions_data, radiuss_data):
if (np.sqrt(pow(x-position[0], 2)+pow(y-position[1], 2)) < radius):
enclosing_circles[id] = float('{:.4f}'.format(radius))
# choose the corresponding ID with the smallest radius here
if enclosing_circles != {}:
smallest_enclosing_circle = min(enclosing_circles, key=enclosing_circles.get)
sub_circle_catalog.append((enclosing_circles[smallest_enclosing_circle], 1))
# add up all special grids in each sub-circle when looping over all grids
for key, value in sub_circle_catalog:
totals[key] = totals.get(key, 0) + value
totals = collections.OrderedDict(sorted(totals.items()))
totals = list(totals.items())
with open('/the/path/to/the/file.txt', "a") as my_file:
print('{}'.format(totals), file=my_file)
my_file.close()
I have two trajectories (i.e. two lists of points) and I am trying to find the intersection points for both these trajectories. However, if I represent these trajectories as lines, I might miss real world intersections (just misses).
What I would like to do is to represent the line as a polygon with certain width around the points and then find where the two polygons intersect with each other.
I am using the python spatial library but I was wondering if anyone has done this before. Here is a picture of the line segments which don't intersect because they just miss each other. Below is the sample data code that represents the trajectory of two objects.
object_trajectory=np.array([[-3370.00427248, 3701.46800775],
[-3363.69164715, 3702.21408203],
[-3356.31277271, 3703.06477984],
[-3347.25951787, 3704.10740164],
[-3336.739511 , 3705.3958357 ],
[-3326.29355823, 3706.78035903],
[-3313.4987339 , 3708.2076586 ],
[-3299.53433345, 3709.72507366],
[-3283.15486406, 3711.47077376],
[-3269.23487255, 3713.05635557]])
target_trajectory=np.array([[-3384.99966703, 3696.41922372],
[-3382.43687562, 3696.6739521 ],
[-3378.22995178, 3697.08802862],
[-3371.98983789, 3697.71490469],
[-3363.5900481 , 3698.62666805],
[-3354.28520354, 3699.67613798],
[-3342.18581931, 3701.04853915],
[-3328.51519511, 3702.57528111],
[-3312.09691577, 3704.41961271],
[-3297.85543763, 3706.00878621]])
plt.plot(object_trajectory[:,0],object_trajectory[:,1],'b',color='b')
plt.plot(vehicle_trajectory[:,0],vehicle_trajectory[:,1],'b',color='r')
Let's say you have two lines defined by numpy arrays x1, y1, x2, and y2.
import numpy as np
You can create an array distances[i, j] containing the distances between the ith point in the first line and the jth point in the second line.
distances = ((x1[:, None] - x2[None, :])**2 + (y1[:, None] - y2[None, :])**2)**0.5
Then you can find indices where distances is less than some threshold you want to define for intersection. If you're thinking of the lines as having some thickness, the threshold would be half of that thickness.
threshold = 0.1
intersections = np.argwhere(distances < threshold)
intersections is now a N by 2 array containing all point pairs that are considered to be "intersecting" (the [i, 0] is the index from the first line, and [i, 1] is the index from the second line). If you want to get the set of all the indices from each line that are intersecting, you can use something like
first_intersection_indices = np.asarray(sorted(set(intersections[:, 0])))
second_intersection_indices = np.asarray(sorted(set(intersections[:, 1])))
From here, you can also determine how many intersections there are by taking only the center value for any consecutive values in each list.
L1 = []
current_intersection = []
for i in range(first_intersection_indices.shape[0]):
if len(current_intersection) == 0:
current_intersection.append(first_intersection_indices[i])
elif first_intersection_indices[i] == current_intersection[-1]:
current_intersection.append(first_intersection_indices[i])
else:
L1.append(int(np.median(current_intersection)))
current_intersection = [first_intersection_indices[i]]
print(len(L1))
You can use these to print the coordinates of each intersection.
for i in L1:
print(x1[i], y1[i])
Turns out that the shapely package already has a ton of convinience functions that get me very far with this.
from shapely.geometry import Point, LineString, MultiPoint
# I assume that self.line is of type LineString (i.e. a line trajectory)
region_polygon = self.line.buffer(self.lane_width)
# line.buffer essentially generates a nice interpolated bounding polygon around the trajectory.
# Now we can identify all the other points in the other trajectory that intersects with the region_polygon that we just generated. You can also use .intersection if you want to simply generate two polygon trajectories and find the intersecting polygon as well.
is_in_region = [region_polygon.intersects(point) for point in points]
I have a part of perimeter of a polygon and need to close it.Please refer this image
As I can see there is only one unique way to close the polygon without dividing the polygon and without the edges intersecting.
And the closing edges would be b->c,d->e,f->g,h->a
Is there any algo to achieve this?
I can think of only one brute force method, try every possible combination and check if it forms a closed polygon(Any good algos to check if it is closed polygon?)
Is there any better way or a known algorithm?
Note: The vertices should be connected by single straight lines only and polygon is not necessarily convex
Also, You can safely assume that these segments always form a polygon because I get these line segments from a polygon and Im trying to recreate the polygon
I think that in "well-behaved" (small gaps, not too irregular shape, etc.) cases, one might get away with following approach. The idea is to assume that the solution (particular permutation of the input line segments which are then assumed to be connected with straight lines) minimizes the length of the resulting MultiLineString defining the boundary of the polygon of interest.
To tackle this problem, the implementation below uses the 2-opt heuristic to the traveling salesman problem. It proceeds in following steps:
the set of vertices is defined as the union of the endpoints of all input line segments
it tries to connect these points in order to minimize the total length of the resulting MultiLineString under the constraint that the points belonging to the same input line segment are always connected (i.e., the 2-opt algorithm is allowed to split only edges connecting different line segments - this is handled by the extra if condition in the main double for-loop).
The result is then:
import logging
import random
import sys
from shapely.geometry import LineString, Polygon
from shapely.ops import polygonize, linemerge
#prevent shapely from showing an error message on is_valid tests
logger = logging.getLogger()
logger.setLevel(logging.ERROR)
#input lines (LineStrings)
lines = [
[(3.15,3.94), (4.06,3.91), (4.27,3.49)],
[(0.84,2.99), (0.97,3.67), (1.68,3.91), (2.68,3.92)],
[(4.46,3.23), (5.12,2.97), (4.60,2.00)],
[(4.13,1.44), (4.41,0.68), (1.14,1.99)]
]
random.shuffle(lines)
N, pnts = 0, []
pnt2line = {}
for line_id, line in enumerate(lines):
#for each line, save its endpoints and remember
#to which line each point belongs
for pnt in [line[0], line[-1]]:
pnt2line[N] = line_id
pnts.append(pnt)
N += 1
#as initial guess, try to connect these points sequentially
route = [i for i in range(0, N)]
def nrm_idx(N, idx):
return (N + idx) % N
def get_polygon(route):
#for given route, attempt to construct the resulting polygon
segments = []
m = len(route)
for idx in range(0, m):
i, j = route[idx], route[nrm_idx(m, idx+1)]
if pnt2line[i] == pnt2line[j]:
#if two consecutive points belong to the same line, consider this line
segments.append(lines[pnt2line[i]])
else:
#otherwise, connect these points with a straight line
segments.append([pnts[i], pnts[j]])
return Polygon(linemerge(segments))
def get_weight(route):
P = get_polygon(route)
return P.length if P.is_valid else sys.maxsize
def edge_is_fixed(pnt_i, pnt_j):
#check if an edge specified by point pnt_i/pnt_j can be dissected or not
#in the latter case, the points belong to the same line/line segment
return (pnt2line[pnt_i] == pnt2line[pnt_j])
def opt_swap(route, i, k):
#perform 2-opt swap
return route[0:i] + route[i:k+1][::-1] + route[k+1:]
flag = True
while flag:
flag = False
best_weight = get_weight(route)
for i in range(0, N-1):
for k in range(i+1, N):
if edge_is_fixed(route[nrm_idx(N, i-1)], route[i]) or edge_is_fixed(route[k], route[nrm_idx(N, k+1)]):
continue
new_route = opt_swap(route, i, k)
weight = get_weight(new_route)
if weight < best_weight:
route = new_route[:]
best_weight = weight
flag = True
P = get_polygon(route)
for x, y in P.exterior.coords:
print(x, y)
For your input (approximated), the result is then indeed:
Here's something that might work:
- Make a set containing only the open points (points that are only on one edge, i.e. the labelled ones in your diagram)
- Run a convex hull algorithm on that set
- Use the edges of the convex hull to complete the polygon with the existing edges. (I.e if the convex hull contains A->B, but A and B are already indirectly connected via adjacent edges in your pre-existing set of edges, discard the edge A->B in the convex hull)
EDIT
I previously suggested co-opting convex hull algorithms but that approach has shortcomings, including the case where the points would not make a convex shape.
Note that, according to your stipulations, there are sets that would not have solutions, such as:
(it's not possible to complete this into a polygon with no crossing lines, using only single straight lines between open points)
I have a large 4-dimensional dataset of Temperatures [time,pressure,lat,lon].
I need to find all grid points within a region defined by lat/lon indices and calculate an average over the region to leave me with a 2-dimensional array.
I know how to do this if my region is a rectangle (or square) but how can this be done with an irregular polygon?
Below is an image showing the regions I need to average together and the lat/lon grid the data is gridded to in the array
I believe this should solve your problem.
The code below generates all cells in a polygon defined by a list of vertices.
It "scans" the polygon row by row keeping track of the transition columns where you (re)-enter or exit the polygon.
def row(x, transitions):
""" generator spitting all cells in a row given a list of transition (in/out) columns."""
i = 1
in_poly = True
y = transitions[0]
while i < len(transitions):
if in_poly:
while y < transitions[i]:
yield (x,y)
y += 1
in_poly = False
else:
in_poly = True
y = transitions[i]
i += 1
def get_same_row_vert(i, vertices):
""" find all vertex columns in the same row as vertices[i], and return next vertex index as well."""
vert = []
x = vertices[i][0]
while i < len(vertices) and vertices[i][0] == x:
vert.append(vertices[i][1])
i += 1
return vert, i
def update_transitions(old, new):
""" update old transition columns for a row given new vertices.
That is: merge both lists and remove duplicate values (2 transitions at the same column cancel each other)"""
if old == []:
return new
if new == []:
return old
o0 = old[0]
n0 = new[0]
if o0 == n0:
return update_transitions(old[1:], new[1:])
if o0 < n0:
return [o0] + update_transitions(old[1:], new)
return [n0] + update_transitions(old, new[1:])
def polygon(vertices):
""" generator spitting all cells in the polygon defined by given vertices."""
vertices.sort()
x = vertices[0][0]
transitions, i = get_same_row_vert(0, vertices)
while i < len(vertices):
while x < vertices[i][0]:
for cell in row(x, transitions):
yield cell
x += 1
vert, i = get_same_row_vert(i, vertices)
transitions = update_transitions(transitions, vert)
# define a "strange" polygon (hook shaped)
vertices = [(0,0),(0,3),(4,3),(4,0),(3,0),(3,2),(1,2),(1,1),(2,1),(2,0)]
for cell in polygon(vertices):
print cell
# or do whatever you need to do
The general class of problems is called "Point in Polygon", where the (fairly) standard algorithm is based on drawing a test line through the point under consideration and counting the number of times it crosses polygon boundaries (its really cool/weird that it works so simply, I think). This is a really good overview which includes implementation information.
For your problem in particular, since each of your regions are defined based on a small number of square cells - I think a more brute-force approach might be better. Perhaps something like:
For each region, form a list of all of the (lat/lon) squares which define it. Depending on how your regions are defined, this may be trivial, or annoying...
For each point you are examining, figure out which square it lives in. Since the squares are so well behaves, you can do this manually using opposite corners of each square, or using a method like numpy.digitize.
Test whether the square the point lives in, is in one of the regions.
If you're still having trouble, please provide some more details about your problem (specifically, how your regions are defined) --- that will make it easier to offer advice.
I've written a bit of code to do a simple spatial join in QGIS 2 and 2.2 (points that lie within a buffer to take attribute of the buffer). However, I'd like to employ a QgsSpatialIndex in order to speed things up a bit. Where can I go from here:
pointProvider = self.pointLayer.dataProvider()
rotateProvider = self.rotateBUFF.dataProvider()
all_point = pointProvider.getFeatures()
point_spIndex = QgsSpatialIndex()
for feat in all_point:
point_spIndex.insertFeature(feat)
all_line = rotateProvider.getFeatures()
line_spIndex = QgsSpatialIndex()
for feat in all_line:
line_spIndex.insertFeature(feat)
rotate_IDX = self.rotateBUFF.fieldNameIndex('bearing')
point_IDX = self.pointLayer.fieldNameIndex('bearing')
self.pointLayer.startEditing()
for rotatefeat in self.rotateBUFF.getFeatures():
for pointfeat in self.pointLayer.getFeatures():
if pointfeat.geometry().intersects(rotatefeat.geometry()) == True:
pointID = pointfeat.id()
bearing = rotatefeat.attributes()[rotate_IDX]
self.pointLayer.changeAttributeValue(pointID, point_IDX, bearing)
self.pointLayer.commitChanges()
To do this kind of spatial join, you can use the QgsSpatialIndex (http://www.qgis.org/api/classQgsSpatialIndex.html) intersects(QgsRectangle) function to get a list of candidate featureIDs or the nearestNeighbor (QgsPoint,n) function to get the list of the n nearest neighbours as featureIDs.
Since you only want the points that lie within the buffer, the intersects function seems most suitable. I have not tested if a degenerate bbox (point) can be used. If not, just make a very small bounding box around your point.
The intersects function returns all features that have a bounding box that intersects the given rectangle, so you will have to test these candidate features for a true intersection.
Your outer loop should be on the points (you want to to add attribute values to each point from their containing buffer).
# If degenerate rectangles are allowed, delta could be 0,
# if not, choose a suitable, small value
delta = 0.1
# Loop through the points
for point in all_point:
# Create a search rectangle
# Assuming that all_point consist of QgsPoint
searchRectangle = QgsRectangle(point.x() - delta, point.y() - delta, point.x() + delta, point.y() + delta)
# Use the search rectangle to get candidate buffers from the buffer index
candidateIDs = line_index.intesects(searchRectangle)
# Loop through the candidate buffers to find the first one that contains the point
for candidateID in candidateIDs:
candFeature == rotateProvider.getFeatures(QgsFeatureRequest(candidateID)).next()
if candFeature.geometry().contains(point):
# Do something useful with the point - buffer pair
# No need to look further, so break
break