[docs]defget_coordinates(file_path):""" Helper Method - Gets coordinates from a GeoJSON file Parameters ---------- file_path: str Path location of geojson file Returns ------- coordinates_list: List[float] List of vertices from GeoJSON polygon """withopen(file_path)asf:data=json.load(f)coordinates_list=[]forfeatureindata["features"]:geometry=feature["geometry"]geometry_type=geometry["type"]coordinates=geometry["coordinates"]ifgeometry_typein["Point","LineString"]:coordinates_list.append(coordinates)elifgeometry_type=="Polygon":forpolygonincoordinates:coordinates_list.extend(polygon)elifgeometry_type=="MultiPolygon":formultipolygonincoordinates:forpolygoninmultipolygon:coordinates_list.extend(polygon)returncoordinates_list
[docs]defvertex_count(file_path):""" Counts vertices from a GeoJSON file. Parameters ---------- file_path: str Path location of geojson file Returns ------- vertex_num: int Number of vertices in geojson file """vertex_num=len(get_coordinates(file_path))-1returnvertex_num
[docs]defreduce_vertex(file_path,ratio):""" Reduces the number of vertices in a polygon Parameters ---------- file_path: str Path location of geojson file ratio: float Sets the concave_hull ratio """withfiona.open(file_path)ascollection:hulls=[concave_hull(shape(feat["geometry"]),ratio)forfeatincollection]dissolved_hulls=mapping(unary_union(hulls))withopen("reduced_vertex.geojson","w")asf:json.dump(dissolved_hulls,f)
[docs]defcheck_hole(file_path):""" Checks if a polygon contains a hole. Parameters ---------- file_path: str Path location of geojson file Returns ------- boolean: bool True if there is a hole in the polygon, false if there is not """withopen(file_path)asf:geojson=json.load(f)forfeatureingeojson["features"]:iffeature["geometry"]["type"]=="Polygon":coordinates=feature["geometry"]["coordinates"]iflen(coordinates)>1:returnTruereturnFalse
[docs]deffill_holes(file_path:str):""" Fills holes of GeoJSON by deleting interior ring coordinates and creating a new GeoJSON with new coordinates Parameters ---------- file_path: str Path location of geojson file """coordinates_list=get_coordinates(file_path)new_coordinates_list=[]first_entry=coordinates_list[0]new_coordinates_list.append(first_entry)forcoordinateincoordinates_list[1:]:new_coordinates_list.append(coordinate)ifcoordinate==first_entry:breakgeojson={"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[new_coordinates_list]},"properties":{},}],}withopen("filled_holes.geojson","w")asf:json.dump(geojson,f)
[docs]defcheck_overlap(file_path:str)->bool:""" Checks if two polygons are overlapping from a GeoJSON file Parameters ---------- file_path: str Path location of geojson file Returns ------- boolean: bool True if there is overlap and false if there is no overlap """coordinates_list=get_coordinates(file_path)polygon1=[]polygon2=[]first_entry=coordinates_list[0]polygon1.append(first_entry)divider=0forcoordinateincoordinates_list[1:]:polygon1.append(coordinate)ifcoordinate==first_entry:divider=coordinates_list[1:].index(coordinate)+1breakfirst_entry=coordinates_list[divider]forcoordinateincoordinates_list[divider+1:]:polygon2.append(coordinate)ifcoordinate==first_entry:divider=coordinates_list[1:].index(coordinate)+1breakreturnPolygon(polygon1).intersects(Polygon(polygon2))
[docs]deffix_overlap(file_path:str):""" Fixes the overlap of two polygons by deleting the overlapped area and merging the two polygons into one polygon Parameters ---------- file_path: str Path location of geojson file """coordinates_list=get_coordinates(file_path)polygon1=[]polygon2=[]first_entry=coordinates_list[0]polygon1.append(first_entry)divider=0forcoordinateincoordinates_list[1:]:polygon1.append(coordinate)ifcoordinate==first_entry:divider=coordinates_list[1:].index(coordinate)+1breakfirst_entry=coordinates_list[divider]forcoordinateincoordinates_list[divider+1:]:polygon2.append(coordinate)ifcoordinate==first_entry:divider=coordinates_list[1:].index(coordinate)+1breakcombined_polygon=unary_union([Polygon(polygon1),Polygon(polygon2)])geojson={"type":"FeatureCollection","features":[{"type":"Feature","geometry":mapping(combined_polygon),"properties":{}}],}withopen("corrected_overlap.geojson","w")asf:json.dump(geojson,f)
[docs]defclipping_check(file_path:str,AOI_Coordinates:list)->bool:""" Checks if a given polygon clips outside the contracted AOI coordinates Parameters ---------- file_path: str Path location of geojson file AOI_Coordinates: List[float] List of coordinates representing the AOI bounds Returns ------- boolean: bool True if it clips outside and false if it does not """given_polygon=Polygon(get_coordinates(file_path))first_array=AOI_Coordinates[0]last_array=AOI_Coordinates[-1]iffirst_array!=last_array:AOI_Coordinates.append(first_array.copy())AOI_Bounds=Polygon(AOI_Coordinates)returnnotgiven_polygon.within(AOI_Bounds)
[docs]deffix_clipping(file_path:str,AOI_Coordinates:list):""" Deletes all parts of a Polygon that exceed the AOI coordinate bounds Parameters ---------- file_path: str Path location of geojson file AOI_Coordinates: List[float] List of coordinates representing the AOI bounds """first_array=AOI_Coordinates[0]last_array=AOI_Coordinates[-1]iffirst_array!=last_array:AOI_Coordinates.append(first_array.copy())combined_polygon=Polygon(AOI_Coordinates).intersection(Polygon(get_coordinates(file_path)))geojson={"type":"FeatureCollection","features":[{"type":"Feature","geometry":mapping(combined_polygon),"properties":{}}],}withopen("corrected_clipping.geojson","w")asf:json.dump(geojson,f)
[docs]defsimplify(file_path:str,ratio:int,AOI_Coordinates:list):""" Runs all checks and fixes to adhere to Planet's AOI Geometry Limits Parameters ---------- file_path: str Path location of geojson file AOI_Coordinates: List[float] List of coordinates representing the AOI bounds """update_file_path=file_path# Vertices Checkifvertex_count(file_path)>500:print("Too many vertices, reducing number")reduce_vertex(update_file_path)update_file_path="reduced_vertex.geojson"# Holes and Exterior Rings Checkifcheck_hole(update_file_path):print("Polygon contains hole, filling hole")fill_holes(update_file_path)update_file_path="filled_holes.geojson"# Overlapping and Intersections Checkifcheck_overlap(update_file_path):print("Polygons are overlapping, combining polygons to eliminate overlap")fix_overlap(update_file_path)update_file_path="corrected_overlap.geojson"# Clipping outside AOI Checkifclipping_check(update_file_path,AOI_Coordinates):print("Polygon clips outside of AOI, cutting outside areas")fix_clipping(update_file_path,AOI_Coordinates)update_file_path="corrected_clipping.geojson"try:os.rename(update_file_path,"Simplified.geojson")print("Your new simplified file is called Simplified.geojson. For more information on Planet's AOI Geometry Limits, check out this link: https://developers.planet.com/docs/subscriptions/tools/")exceptPermissionError:print("Permission denied.")ifos.path.exists("reduced_vertex.geojson"):os.remove("reduced_vertex.geojson")ifos.path.exists("filled_holes.geojson"):os.remove("filled_holes.geojson")ifos.path.exists("corrected_overlap.geojson"):os.remove("corrected_overlap.geojson")