Commit 0b72648f by Hoek, Steven

### Extra methods were added in order to indicate points which are representative...

`Extra methods were added in order to indicate points which are representative for a shape - esp. for those with centroids outside of the actual shape`
parent 834c702e
 ... ... @@ -46,7 +46,7 @@ class Triangle(object): if hasattr(self, "__len__"): assert len(self.__points) == 3, "Triangle must have 3 corners!" # Calculate the centroid by averaging coordinates # Calculate the centroid by averaging the coordinates if hasattr(self.__points[0], "__len__"): xc = sum([pt[0] for pt in self.__points]) / 3 yc = sum([pt[1] for pt in self.__points]) / 3 ... ... @@ -89,6 +89,8 @@ class SimplePoint(object): self.y = y class SimpleShape(object): # TODO: things may not work in case of island polygons # Points either have x and y attributes or are sequences of length 2 shape = None def __init__(self): self.shape = InnerShape() ... ... @@ -127,9 +129,12 @@ class SimpleShape(object): total_area = total_xweighted = total_yweighted = 0.0 for tr in triangles: mytr = Triangle(tr) total_area += mytr.area() total_xweighted += mytr.centroid()[0] * mytr.area() total_yweighted += mytr.centroid()[1] * mytr.area() centroid = mytr.centroid() if self.contains_point(centroid): area = mytr.area() total_area += area total_xweighted += centroid[0] * area total_yweighted += centroid[1] * area result = (total_xweighted / total_area, total_yweighted / total_area) except Exception as e: ... ... @@ -137,11 +142,51 @@ class SimpleShape(object): finally: return result def get_nearest_point(self, otherpoint): def get_nearest_point(self, otherpoint) -> typing.Vertex: # Validate input if hasattr(otherpoint, "__len__"): x0, y0 = otherpoint[0], otherpoint[1] elif hasattr(otherpoint, "x") and hasattr(otherpoint, "y"): x0, y0 = otherpoint.x, otherpoint.y # Get the 2 points that are nearest to the other point pt1, pt2 = self.get_nearest_edge_points(otherpoint) # Determine a line through the otherpoint perpendicular to the line through these points line1 = typing.get_standard_line(typing.Vertex(pt1[0], pt1[1]), typing.Vertex(pt2[0], pt2[1])) if line1.B == 0: result = typing.Vertex(pt1[0], y0) elif line1.A == 0: result = typing.Vertex(x0, pt1[1]) else: line2 = self.get_plumb_line(line1, otherpoint) result = typing.get_line_intersection(line1, line2) # Check that the result lies on the edge of the polygon A = (min(pt1[0], pt2[0])) <= result.x and (max(pt1[0], pt2[0]) >= result.x) B = (min(pt1[1], pt2[1])) <= result.y and (max(pt1[1], pt2[1]) >= result.y) if (not A) or (not B): result = typing.Vertex(pt1[0], pt1[1]) return result def get_nearest_edge_points(self, otherpoint) -> (typing.Vertex, typing.Vertex): # Process input if hasattr(otherpoint, "__len__"): x0, y0 = otherpoint[0], otherpoint[1] elif hasattr(otherpoint, "x") and hasattr(otherpoint, "y"): x0, y0 = otherpoint.x, otherpoint.y # Get the 2 points of the polygon that are nearest to the given point points = sum(self.get_point_series(), []) mydicts = [{"point":(p.x, p.y), "distance":distance((p.x, p.y), otherpoint)} for p in points] minval = min([d["distance"] for d in mydicts]) result = list(filter(lambda d: abs(d["distance"] - minval) < 0.000000001, mydicts))[0] mydicts = [{"point":(p.x, p.y), "distance":distance((p.x, p.y), (x0, y0))} for p in points] sorted_points = sorted(mydicts, key=lambda p: p['distance']) pt1, pt2 = sorted_points[0]["point"], sorted_points[1]["point"] return typing.Vertex(pt1[0], pt1[1]), typing.Vertex(pt2[0], pt2[1]) def get_plumb_line(self, line, point) -> typing.StandardLine: # Validate / process input if line.A == 0: raise ValueError("Line without slope not allowed as input!") if hasattr(point, "__len__"): x0, y0 = point[0], point[1] elif hasattr(point, "x") and hasattr(point, "y"): x0, y0 = point.x, point.y # Determine a line perpendicular to the given line which passes through the given point intercept = y0 - (line.B / line.A) * x0 result = typing.StandardLine(-1 * line.B / line.A, 1.0, intercept) return result def get_area(self): ... ... @@ -153,9 +198,70 @@ class SimpleShape(object): result = 0.0 for tr in triangles: mytr = Triangle(tr) centroid = mytr.centroid() if self.contains_point(centroid): result += mytr.area() return result def contains_point(self, point): # Initialise if hasattr(point, "__len__"): x, y = point[0], point[1] else: x, y = point.x, point.y poly = [(p.x, p.y) for p in sum(self.get_point_series(), [])] n = len(poly) result = False # Loop over the points p1x, p1y = poly[0] for i in range(n+1): p2x, p2y = poly[i % n] if y > min(p1y, p2y): if y <= max(p1y, p2y): if x <= max(p1x, p2x): if p1y != p2y: xinters = (y - p1y) * (p2x - p1x) / float((p2y - p1y)) + p1x if p1x == p2x or x <= xinters: result = not result p1x, p1y = p2x, p2y return result def get_point_inside(self, edgepoint, distance) -> typing.Vertex: # Initialise result = None # Validate input try: if distance <= 0.0: raise ValueError("Invalid value for distance!") if hasattr(edgepoint, "__len__"): x, y = edgepoint[0], edgepoint[1] else: x, y = edgepoint.x, edgepoint.y # Get the nearest 2 edge points of the polygon and check that the point lies on the edge of the polygon pt1, pt2 = self.get_nearest_edge_points(edgepoint) eps = 0.0000000001 if abs(pt1.x - pt2.x) < eps and abs(pt1.y - pt2.y) < eps: # TODO: find a good way to handle this raise ValueError("Two consecutive points of the shape are located too close to each other!") A = (min(pt1[0], pt2[0])) <= x and (max(pt1[0], pt2[0]) >= x) B = (min(pt1[1], pt2[1])) <= y and (max(pt1[1], pt2[1]) >= y) if A and B: # At least the edgepoint is located inside the boundary box around pt1 and pt2 # Make sure a point inside the shape is found that is distance away from the edge line1 = typing.get_standard_line(typing.Vertex(pt1[0], pt1[1]), typing.Vertex(pt2[0], pt2[1])) if line1.A == 0: if self.contains_point((x, y + distance)): result = typing.Vertex(x, y + distance) else: result = typing.Vertex(x, y - distance) else: line2 = self.get_plumb_line(line1, edgepoint) slope = line2.A / line2.B dx = distance / sqrt(1 + slope**2) dy = slope * dx if self.contains_point((x + dx, y + dy)): result = typing.Vertex(x + dx, y + dy) else: result = typing.Vertex(x - dx, y - dy) else: raise ValueError("Given point is not located on the edge!") except Exception as e: print(e) finally: return result class InnerShape(object): # A list called parts holds index which indicates the point of next part # A list called points holds list / array with only 2 places ... ...
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!