|
31 | 31 | from source.guistatics import GUIStatics
|
32 | 32 | import copy
|
33 | 33 | from PIL import ImageTk
|
34 |
| -from shapely.geometry import Polygon |
| 34 | +from shapely.geometry import Polygon, Point, MultiPolygon |
| 35 | +from shapely.ops import unary_union |
| 36 | +import numpy as np |
35 | 37 |
|
36 | 38 | class Geometry(tk.Toplevel):
|
37 | 39 | """
|
@@ -875,48 +877,103 @@ def check_geometry_detail(self):
|
875 | 877 | Detailed check for geometry
|
876 | 878 | :return:
|
877 | 879 | """
|
| 880 | + # TODO: this needs a lot of bugfixing. detects problems for good geometry... |
878 | 881 | check_passed = True
|
879 |
| - |
880 | 882 | check_fail_str = ''
|
881 | 883 | check_failed_list = []
|
| 884 | + |
882 | 885 | for ip1, poly1 in enumerate(self.polygons.values()):
|
883 | 886 | poly1_shapely = Polygon([(node[0], node[1]) for node in poly1['coordinates']])
|
| 887 | + |
| 888 | + # check polygon shape |
| 889 | + # check if > 1 node have same position |
| 890 | + nodes_polygon = np.array(poly1['coordinates']) |
| 891 | + nodes_polygon_complex = [node[0] + 1j * node[1] for node in nodes_polygon] |
| 892 | + if len(set(nodes_polygon_complex)) != len(nodes_polygon): |
| 893 | + check_fail_str = f"Geometry error: At least two nodes of polygon {ip1} are identical" |
| 894 | + check_failed_list.append(check_fail_str) |
| 895 | + # check if polygon is valid (lines not crossing, area > 0) |
| 896 | + if not poly1_shapely.is_valid: |
| 897 | + check_fail_str = f"Geometry error: Polygon {ip1} invalid (Segments crossing/Zero Area)" |
| 898 | + check_failed_list.append(check_fail_str) |
| 899 | + |
| 900 | + # check single points |
| 901 | + if self.points and self.points != {'None'}: |
| 902 | + for sp, point in enumerate(self.points.values()): |
| 903 | + p_in_pos_poly = False |
| 904 | + if poly1_shapely.touches(Point(point[0], point[1])): |
| 905 | + check_fail_str = f"Geometry error: Point {sp} on boundary of Polygon {ip1}" |
| 906 | + check_failed_list.append(check_fail_str) |
| 907 | + if poly1_shapely.contains(Point(point[0], point[1])): |
| 908 | + p_in_pos_poly = True |
| 909 | + if poly1['area_neg_pos'] == 'Negative': |
| 910 | + p_in_pos_poly = False |
| 911 | + check_fail_str = f"Geometry error: Point {sp} inside of negative Polygon {ip1}" |
| 912 | + check_failed_list.append(check_fail_str) |
| 913 | + if not p_in_pos_poly: |
| 914 | + check_fail_str = f"Geometry error: Point {sp} not inside of any positive Polygon" |
| 915 | + check_failed_list.append(check_fail_str) |
| 916 | + # check two polygons |
884 | 917 | for ip2, poly2 in enumerate(list(self.polygons.values())[ip1 + 1:], start=ip1+1):
|
885 | 918 | poly2_shapely = Polygon([(node[0], node[1]) for node in poly2['coordinates']])
|
| 919 | + |
886 | 920 | # check if positive polygons overlap
|
887 | 921 | if poly1['area_neg_pos'] == 'Positive' and poly2['area_neg_pos'] == 'Positive':
|
888 | 922 | if poly1_shapely.overlaps(poly2_shapely):
|
889 |
| - check_fail_str = f"Geometryerror: Positive Polygon {ip1} and Positive Polygon {ip2} overlap" |
| 923 | + check_fail_str = f"Geometry error: Positive Polygon {ip1} and Positive Polygon {ip2} overlap" |
890 | 924 | check_failed_list.append(check_fail_str)
|
891 | 925 | # check if negative polygons overlap
|
892 | 926 | if poly1['area_neg_pos'] == 'Negative' and poly2['area_neg_pos'] == 'Negative':
|
893 | 927 | if poly1_shapely.overlaps(poly2_shapely):
|
894 |
| - check_fail_str = f"Geometryerror: Negative Polygon {ip1} and Negative Polygon {ip2} overlap" |
| 928 | + check_fail_str = f"Geometry error: Negative Polygon {ip1} and Negative Polygon {ip2} overlap" |
| 929 | + check_failed_list.append(check_fail_str) |
| 930 | + if poly1_shapely.touches(poly2_shapely): |
| 931 | + check_fail_str = f"Geometry error: Negative Polygon {ip1} and Negative Polygon {ip2} intersect/touch at least once" |
895 | 932 | check_failed_list.append(check_fail_str)
|
896 | 933 | # check if negative polygon inside positive polygon
|
897 | 934 | if poly1['area_neg_pos'] == 'Positive' and poly2['area_neg_pos'] == 'Negative':
|
898 |
| - if poly1_shapely.intersects(poly2_shapely): |
899 |
| - if not poly1_shapely.contains(poly2_shapely): |
900 |
| - check_fail_str = f"Geometryerror: Positive Polygon {ip1} and Negative Polygon {ip2} intersect" |
| 935 | + if poly1_shapely.contains(poly2_shapely): |
| 936 | + for node in poly2['coordinates']: |
| 937 | + if poly1_shapely.touches(Point(node[0], node[1])): |
| 938 | + check_fail_str = f"Geometry error: Positive Polygon {ip1} and Negative Polygon {ip2} overlap (on boundary)" |
901 | 939 | check_failed_list.append(check_fail_str)
|
| 940 | + break |
902 | 941 | if poly1['area_neg_pos'] == 'Negative' and poly2['area_neg_pos'] == 'Positive':
|
903 |
| - if poly1_shapely.intersects(poly2_shapely): |
904 |
| - if not poly2_shapely.contains(poly1_shapely): |
905 |
| - check_fail_str = f"Geometryerror: Negative Polygon {ip1} and Positive Polygon {ip2} intersect" |
| 942 | + if poly2_shapely.contains(poly1_shapely): |
| 943 | + for node in poly1['coordinates']: |
| 944 | + if poly2_shapely.touches(Point(node[0], node[1])): |
| 945 | + check_fail_str = f"Geometry error: Negative Polygon {ip1} and Positive Polygon {ip2} overlap (on boundary)" |
906 | 946 | check_failed_list.append(check_fail_str)
|
| 947 | + break |
| 948 | + # check if two adjacent positive polygons share the same nodes |
| 949 | + if poly1['area_neg_pos'] == 'Positive' and poly2['area_neg_pos'] == 'Positive': |
| 950 | + shared_boundary = poly1_shapely.intersection(poly2_shapely) |
| 951 | + if shared_boundary.geom_type == 'LineString': |
| 952 | + coords = list(shared_boundary.coords) |
| 953 | + elif shared_boundary.geom_type == 'MultiLineString': |
| 954 | + coords = [list(line.coords) for line in shared_boundary] |
| 955 | + elif shared_boundary.geom_type == 'Point': |
| 956 | + check_fail_str = f"Geometry error: Polygon {ip1} and Polygon {ip2} share only one node" |
| 957 | + check_failed_list.append(check_fail_str) |
| 958 | + else: |
| 959 | + coords = [] |
| 960 | + poly1nodes_complex = [node[0] + 1j * node[1] for node in np.array(poly1['coordinates'])] |
| 961 | + poly2nodes_complex = [node[0] + 1j * node[1] for node in np.array(poly2['coordinates'])] |
| 962 | + shared_nodes_complex = [node[0] + 1j * node[1] for node in coords] |
| 963 | + in_poly1 = len(set(poly1nodes_complex).intersection(set(shared_nodes_complex))) |
| 964 | + in_poly2 = len(set(poly2nodes_complex).intersection(set(shared_nodes_complex))) |
| 965 | + if in_poly1 != in_poly2: |
| 966 | + check_fail_str = f"Geometry error: Polygon {ip1} and Polygon {ip2} do not share same nodes on common boundary" |
| 967 | + check_failed_list.append(check_fail_str) |
907 | 968 |
|
| 969 | + # check if all positive polygons are connected |
| 970 | + all_pos_polygons = [Polygon(poly['coordinates']) for poly in self.polygons.values() if poly['area_neg_pos'] == 'Positive'] |
| 971 | + unioned_polygons = unary_union(all_pos_polygons) |
| 972 | + if isinstance(unioned_polygons, MultiPolygon): |
| 973 | + check_fail_str = f"Geometry error: Positive Polygons are not connected properly" |
| 974 | + check_failed_list.append(check_fail_str) |
908 | 975 |
|
909 |
| - |
910 |
| - # # Check if negative polygon intersects with positive polygon (boundary crossed/shared) |
911 |
| - # if poly1['area_neg_pos'] == 'Positive' and poly2['area_neg_pos'] == 'Negative': |
912 |
| - # if poly1_shapely.intersects(poly2_shapely) and not poly1_shapely.contains(poly2_shapely): |
913 |
| - # check_fail_str = f"Geometryerror: Positive Polygon {ip1} and Negative Polygon {ip2} intersect" |
914 |
| - # check_failed_list.append(check_fail_str) |
915 |
| - # if poly1['area_neg_pos'] == 'Negative' and poly2['area_neg_pos'] == 'Positive': |
916 |
| - # if poly1_shapely.intersects(poly2_shapely) and not poly2_shapely.contains(poly1_shapely): |
917 |
| - # check_fail_str = f"Geometryerror: Negative Polygon {ip1} and Positive Polygon {ip2} intersect" |
918 |
| - # check_failed_list.append(check_fail_str) |
919 |
| - |
| 976 | + check_failed_list = list(set(check_failed_list)) |
920 | 977 | for elem in check_failed_list:
|
921 | 978 | print(elem)
|
922 | 979 |
|
|
0 commit comments