diff --git a/generalization/n100/building/building_main.py b/generalization/n100/building/building_main.py index 6d25da9..64378f9 100644 --- a/generalization/n100/building/building_main.py +++ b/generalization/n100/building/building_main.py @@ -27,6 +27,8 @@ @timing_decorator def main(): """ + Building N100 Generalization version 1.0 + What: MORE DOCSTRING NEEDED: Runs the building generalization logic. How: @@ -37,7 +39,7 @@ def main(): Simplify building polygons to make them easier to read and fit around other features at a N100 map scale. calculate_polygon_values: - PLACEHOLDER DOCSTRING NEEDS TO BE UPDATED. + Adds required fields for building point for symbology and resolves building conflicts: angle, hierarchy, and invisibility. polygon_propogate_displacement: Propagates displacement for building polygons to ensure their alignment with roads is adjusted @@ -47,7 +49,7 @@ def main(): PLACEHOLDER DOCSTRING NEEDS TO BE UPDATED. polygon_to_point: - PLACEHOLDER DOCSTRING NEEDS TO BE UPDATED. + Merges all points originating from building polygons to a single point feature. calculate_point_values: Adds required fields for building point for symbology and resolves building conflicts: angle, hierarchy, and invisibility. @@ -68,16 +70,19 @@ def main(): PLACEHOLDER DOCSTRING NEEDS TO BE UPDATED. removing_points_and_erasing_polygons_in_water_features: - PLACEHOLDER DOCSTRING NEEDS TO BE UPDATED. + Fixes geometric conflicts between building polygon/point objects and water-features. Allows for + tourist cabins to intersect water-features. removing_overlapping_polygons_and_points: - PLACEHOLDER DOCSTRING NEEDS TO BE UPDATED. + Resolves graphic conflicts and overlaps between building features which persist after RBC, + prioritizing buildings with higher hierarchy values. finalizing_buildings: - PLACEHOLDER DOCSTRING NEEDS TO BE UPDATED. + Separates building points and polygons into their respective features they are going to be delivered as. data_clean_up: - PLACEHOLDER DOCSTRING NEEDS TO BE UPDATED. + Deletes all fields for each feature expect the fields which should be kept in the delivered product. + Then adds last edited date and finally checks and potentially repairs the geometry of each feature. Why: MORE DOCSTRING NEEDED: Because we need to processing building information so it is cartographic usable for N100 scale. """ diff --git a/generalization/n100/building/calculate_polygon_values.py b/generalization/n100/building/calculate_polygon_values.py index 5496eed..d4d2dde 100644 --- a/generalization/n100/building/calculate_polygon_values.py +++ b/generalization/n100/building/calculate_polygon_values.py @@ -12,8 +12,18 @@ @timing_decorator def main(): """ - Summary: + What: Adds required fields for building point for symbology and resolves building conflicts: angle, hierarchy, and invisibility. + + How: + adding_angle_hierarchy_invisibility_fields: + Adds angle, hierarchy and invisibility fields and set their corresponding values. + + adding_symbol_val: + Adds symbol_val and reclassify NBR values for undefined nbr values + + Why: + The angle, hierarchy and invisibility fields are used in future processing, such as polygon_processor and RBC. """ adding_angle_hierarchy_invisibility_fields() @@ -21,6 +31,10 @@ def main(): def adding_angle_hierarchy_invisibility_fields(): + """ + Adds angle, hierarchy and invisibility fields and set their corresponding values. + """ + # Adding multiple fields print("Adding fields...") arcpy.management.AddFields( @@ -39,13 +53,16 @@ def adding_angle_hierarchy_invisibility_fields(): expression_type="PYTHON3", fields=[ ["angle", "0"], - ["hierarchy", "1"], # Hierachy 1 so buildings can be moved around + ["hierarchy", "1"], # Hierarchy 1 so buildings can be moved around ["invisibility", "0"], ], ) def adding_symbol_val(): + """ + Adds symbol_val and reclassify NBR values for undefined nbr values + """ arcpy.AddField_management( in_table=Building_N100.simplify_polygons___spatial_join_polygons___n100_building.value, field_name="symbol_val", diff --git a/generalization/n100/building/data_clean_up.py b/generalization/n100/building/data_clean_up.py index bef1412..c6843c3 100644 --- a/generalization/n100/building/data_clean_up.py +++ b/generalization/n100/building/data_clean_up.py @@ -15,17 +15,41 @@ # Main function @timing_decorator def main(): + """ + What: + Deletes all fields for each feature expect the fields which should be kept in the delivered product. + Then adds last edited date and finally checks and potentially repairs the geometry of each feature. + How: + keep_necessary_fields: + It runs the keep_necessary_fields function for each feature keeping only required fields. + + add_last_edited_date_to_all_feature_classes: + Adds a 'last_edited_date' field to specified feature classes and sets it to the current date and time. + + check_and_repair_geometry: + Checks and potentially repairs geometry for the final outputs. + Why: + For each feature there should be no other field present other than the ones which is specified. + """ + environment_setup.main() keep_necessary_fields( - Building_N100.BygningsPunkt.value, - ["objtype", "byggtyp_nbr", "målemetode", "nøyaktighet", "last_edited_date"], + input_layer=Building_N100.BygningsPunkt.value, + list_of_fields=[ + "objtype", + "byggtyp_nbr", + "målemetode", + "nøyaktighet", + "last_edited_date", + ], ) keep_necessary_fields( - Building_N100.Grunnriss.value, ["objtype", "byggtyp_nbr", "last_edited_date"] + input_layer=Building_N100.Grunnriss.value, + list_of_fields=["objtype", "byggtyp_nbr", "last_edited_date"], ) keep_necessary_fields( - Building_N100.TuristHytte.value, - [ + input_layer=Building_N100.TuristHytte.value, + list_of_fields=[ "objtype", "byggtyp_nbr", "betjeningsgrad", @@ -38,11 +62,12 @@ def main(): ], ) keep_necessary_fields( - Building_N100.OmrissLinje.value, - ["objtype", "målemetode", "nøyaktighet", "last_edited_date"], + input_layer=Building_N100.OmrissLinje.value, + list_of_fields=["objtype", "målemetode", "nøyaktighet", "last_edited_date"], ) keep_necessary_fields( - Building_N100.Piktogram.value, ["byggtyp_nbr", "last_edited_date"] + input_layer=Building_N100.Piktogram.value, + list_of_fields=["byggtyp_nbr", "last_edited_date"], ) check_and_repair_geometry() @@ -51,10 +76,19 @@ def main(): @timing_decorator -def keep_necessary_fields(input_layer, list_of_fields): +def keep_necessary_fields(input_layer: str, list_of_fields: list[str]): """ - Summary: - Deletes all fields from the input feature class except for a specified set of fields. + What: + Deletes all fields from the input feature class except for a fields specified in a list. + + How: + Retrieves all fields from the input feature. It has a static set of fields always to be kept regardless + of parameter input. It then removes all static and provided fields from the list of fields to remove. + Then deletes all unspecified fields. + + Args: + input_layer (str): The input feature to clean up. + list_of_fields (list[str]): The fields to be kept in addition to static fields. """ # Provide the path to your feature class feature_class_to_clean_up = input_layer @@ -89,8 +123,7 @@ def keep_necessary_fields(input_layer, list_of_fields): def add_last_edited_date_to_all_feature_classes(): """ - Summary: - Adds a 'last_edited_date' field to specified feature classes and sets it to the current date and time. + Adds a 'last_edited_date' field to specified feature classes and sets it to the current date and time. """ all_final_layers = [ Building_N100.BygningsPunkt.value, @@ -129,6 +162,9 @@ def add_last_edited_date_to_all_feature_classes(): def check_and_repair_geometry(): + """ + Checks and potentially repairs geometry for the final outputs. + """ input_features_validation = { "building_points": Building_N100.BygningsPunkt.value, "building_polygons": Building_N100.Grunnriss.value, diff --git a/generalization/n100/building/finalizing_buildings.py b/generalization/n100/building/finalizing_buildings.py index d449393..af34369 100644 --- a/generalization/n100/building/finalizing_buildings.py +++ b/generalization/n100/building/finalizing_buildings.py @@ -12,6 +12,25 @@ def main(): + """ + What: + Separates building points and polygons into their respective features they are going to be delivered as. + How: + removing_points_in_and_close_to_urban_areas: + Makes sure there are no building points near urban areas, except for hospital, churches and tourist huts. + + selecting_all_tourist_cabins: + Selects tourist cabins from building points to be delivered as a separate feature. + + building_polygons_to_line: + Converts building polygons to lines, to creat omrisslinje feature. + + selecting_hospital_and_churches_for_pictogram_featureclass: + Selects building points categorized as hospitals or churches for inclusion in a pictogram feature. + + assigning_final_file_names: + Copies final feature classes to their respective output file locations in the "final_outputs.gdb" + """ environment_setup.main() removing_points_in_and_close_to_urban_areas() selecting_all_tourist_cabins() @@ -23,8 +42,7 @@ def main(): @timing_decorator def removing_points_in_and_close_to_urban_areas(): """ - Summary: - Selects and processes building points based on their proximity to urban areas, keeping those further away and merging specific points. + Makes sure there are no building points near urban areas, except for hospital, churches and tourist huts. """ # Defining sql expression to select urban areas urban_areas_sql_expr = "objtype = 'Tettbebyggelse' Or objtype = 'Industriområde' Or objtype = 'BymessigBebyggelse'" @@ -75,8 +93,7 @@ def removing_points_in_and_close_to_urban_areas(): @timing_decorator def selecting_all_tourist_cabins(): """ - Summary: - Selects building points categorized as tourist cabins and distinguishes them from other building points. + Selects tourist cabins from building points to be delivered as a separate feature. """ selecting_tourist_cabins = "byggtyp_nbr = 956" @@ -97,8 +114,7 @@ def selecting_all_tourist_cabins(): def building_polygons_to_line(): """ - Summary: - Converts building polygons to lines + Converts building polygons to lines, to creat omrisslinje feature. """ arcpy.management.PolygonToLine( in_features=Building_N100.removing_overlapping_polygons_and_points___polygons_NOT_intersecting_road_buffers___n100_building.value, @@ -123,8 +139,7 @@ def building_polygons_to_line(): def selecting_hospital_and_churches_for_pictogram_featureclass(): """ - Summary: - Selects building points categorized as hospitals or churches for inclusion in a pictogram feature class. + Selects building points categorized as hospitals or churches for inclusion in a pictogram feature. """ custom_arcpy.select_attribute_and_make_permanent_feature( input_layer=Building_N100.finalizing_buildings___all_points_except_tourist_cabins___n100_building.value, @@ -136,8 +151,7 @@ def selecting_hospital_and_churches_for_pictogram_featureclass(): @timing_decorator def assigning_final_file_names(): """ - Summary: - Copies final feature classes to their respective output file locations in the "final_outputs.gdb" + Copies final feature classes to their respective output file locations in the "final_outputs.gdb". """ arcpy.management.CopyFeatures( Building_N100.finalizing_buildings___tourist_cabins___n100_building.value, diff --git a/generalization/n100/building/polygon_to_point.py b/generalization/n100/building/polygon_to_point.py index 77ae8db..41a984c 100644 --- a/generalization/n100/building/polygon_to_point.py +++ b/generalization/n100/building/polygon_to_point.py @@ -15,8 +15,12 @@ @timing_decorator def main(): """ - This function creates points from small polygons lost during aggregation, and merges - them together with collapsed points from the tools simplify building and simplify polygon. + What: + Merges all points originating from building polygons to a single point feature. + How: + building_polygons_to_points: + First does a spatial join on all collapsed points from simplify_polygons. Then merges all points from building polygons + to a single point feature. """ environment_setup.main() building_polygons_to_points() @@ -24,6 +28,10 @@ def main(): @timing_decorator def building_polygons_to_points(): + """ + First does a spatial join on all collapsed points from simplify_polygons. Then merges all points from building polygons + to a single point feature. + """ # List of building points which will be spatially joined with building polygons input_points = [ f"{Building_N100.simplify_polygons___simplify_polygon___n100_building.value}_Pnt", diff --git a/generalization/n100/building/removing_overlapping_polygons_and_points.py b/generalization/n100/building/removing_overlapping_polygons_and_points.py index 8af8cd0..ca2d121 100644 --- a/generalization/n100/building/removing_overlapping_polygons_and_points.py +++ b/generalization/n100/building/removing_overlapping_polygons_and_points.py @@ -25,6 +25,50 @@ @timing_decorator def main(): + """ + What: + Resolves graphic conflicts and overlaps between building features which persist after RBC, + prioritizing buildings with higher hierarchy values. + How: + copying_previous_file: + Copies the input file and makes sure the CLUSTER_ID field is not present. + + removing_building_polygons_overlapping_church_hospitals: + Removes building polygons overlapping hospitals, churches or tourist huts + + polygons_overlapping_roads_to_points: + Transforms building polygons intersecting roads to points, then merges these points with other point features. + + adding_new_hierarchy_value_to_points: + Calculates and assigns a new hierarchy value to building points based on their nbr code + + remove_points_that_are_overlapping_roads: + Removes points that are overlapping with roads but makes sure not to lose any hospitals, churches, or tourist huts + + detecting_graphic_conflicts: + Detects graphic conflicts within a given set of features based on a 20 meter conflict distance. + + selecting_points_close_to_graphic_conflict_polygons: + Selects points based on their proximity to graphic conflict polygons. + + finding_clusters_amongst_the_points: + Identifies clusters amongst points close to graphic conflict polygons. The distance needs to be large enough + to prevent graphic overlap to be in different clusters, however small enough to not merge close graphic cluster patterns + in a single super cluster. + + selecting_points_in_a_cluster_and_not_in_a_cluster: + Selects points that have been found in clusters. + + keep_point_with_highest_hierarchy_for_each_cluster: + Iterates through each cluster and retains the point with the highest hierarchy value within the cluster. + Deletes all other points in the cluster. + + merging_final_points_together: + Merges all point outputs to a single point feature. + + Why: + There should be no graphic conflicts between buildings in the final output. + """ environment_setup.main() copying_previous_file() removing_building_polygons_overlapping_church_hospitals() @@ -42,8 +86,7 @@ def main(): @timing_decorator def copying_previous_file(): """ - Summary: - Copies an existing feature class and assigns it a new name. + Copies the input file and makes sure the CLUSTER_ID field is not present. """ # Copying and assigning new name to layer @@ -71,6 +114,9 @@ def copying_previous_file(): def removing_building_polygons_overlapping_church_hospitals(): + """ + Removes building polygons overlapping hospitals, churches or tourist huts + """ custom_arcpy.select_attribute_and_make_permanent_feature( input_layer=Building_N100.removing_overlapping_polygons_and_points___all_building_points___n100_building.value, expression="byggtyp_nbr IN (970, 719, 671, 956)", @@ -99,9 +145,7 @@ def removing_building_polygons_overlapping_church_hospitals(): def polygons_overlapping_roads_to_points(): """ - Summary: - Processes polygons that overlap with road buffers and transforms them to points. - Also identifies and keeps polygons that do not intersect with road buffers. + Transforms building polygons intersecting roads to points, then merges these points with other point features. """ road_lines_to_buffer_symbology = LineToBufferSymbology( @@ -151,8 +195,7 @@ def polygons_overlapping_roads_to_points(): @timing_decorator def adding_new_hierarchy_value_to_points(): """ - Summary: - Calculates and assigns a new hierarchy value to building points based on their nbr code + Calculates and assigns a new hierarchy value to building points based on their nbr code """ # Determining and assigning symbol val arcpy.management.CalculateField( @@ -181,9 +224,7 @@ def adding_new_hierarchy_value_to_points(): def remove_points_that_are_overlapping_roads(): """ - Summary: - Processes and filters points to remove those overlapping with road buffers, - while preserving hospital and church points. + Removes points that are overlapping with roads but makes sure not to lose any hospitals, churches, or tourist huts """ # Selecting all points that are NOT hospital and churches or tourist huts custom_arcpy.select_attribute_and_make_permanent_feature( @@ -253,8 +294,7 @@ def remove_points_that_are_overlapping_roads(): @timing_decorator def detecting_graphic_conflicts(): """ - Summary: - Detects graphic conflicts within a given set of features based on a 20 meter conflict distance. + Detects graphic conflicts within a given set of features based on a 20 meter conflict distance. """ custom_arcpy.apply_symbology( input_layer=Building_N100.removing_overlapping_polygons_and_points___points_no_road_conflict___n100_building.value, @@ -276,8 +316,7 @@ def detecting_graphic_conflicts(): @timing_decorator def selecting_points_close_to_graphic_conflict_polygons(): """ - Summary: - Selects points based on their proximity to graphic conflict polygons. + Selects points based on their proximity to graphic conflict polygons. """ polygon_processor = PolygonProcessor( @@ -324,9 +363,9 @@ def selecting_points_close_to_graphic_conflict_polygons(): @timing_decorator def finding_clusters_amongst_the_points(): """ - Summary: - Identifies clusters among points based on proximity and density. - Specifically, finds clusters of points that are close to graphic conflict polygons. + Identifies clusters amongst points close to graphic conflict polygons. The distance needs to be large enough + to prevent graphic overlap to be in different clusters, however small enough to not merge close graphic cluster patterns + in a single super cluster. """ # Finding church clusters arcpy.gapro.FindPointClusters( @@ -341,9 +380,7 @@ def finding_clusters_amongst_the_points(): @timing_decorator def selecting_points_in_a_cluster_and_not_in_a_cluster(): """ - Summary: - Selects and categorizes points based on their cluster status. - Points are divided into those that are within a cluster and those that are not. + Selects points that have been found in clusters. """ expression_cluster = "CLUSTER_ID > 0" expression_not_cluster = "CLUSTER_ID < 0" @@ -374,9 +411,8 @@ def selecting_points_in_a_cluster_and_not_in_a_cluster(): @timing_decorator def keep_point_with_highest_hierarchy_for_each_cluster(): """ - Summary: - Iterates through each cluster and retains the point with the highest hierarchy value within the cluster. - Deletes all other points in the cluster. + Iterates through each cluster and retains the point with the highest hierarchy value within the cluster. + Deletes all other points in the cluster. """ # Iterate over each cluster with arcpy.da.SearchCursor( @@ -423,8 +459,7 @@ def keep_point_with_highest_hierarchy_for_each_cluster(): @timing_decorator def merging_final_points_together(): """ - Summary: - Merges multiple point feature layers into a single final output layer. + Merges all point outputs to a single point feature. """ # Merge the final hospital and church layers diff --git a/generalization/n100/building/removing_points_and_erasing_polygons_in_water_features.py b/generalization/n100/building/removing_points_and_erasing_polygons_in_water_features.py index 2f79a38..c95e63d 100644 --- a/generalization/n100/building/removing_points_and_erasing_polygons_in_water_features.py +++ b/generalization/n100/building/removing_points_and_erasing_polygons_in_water_features.py @@ -14,9 +14,31 @@ @timing_decorator def main(): + """ + What: + Fixes geometric conflicts between building polygon/point objects and water-features. Allows for + tourist cabins to intersect water-features. + How: + selecting_water_polygon_features: + Creates water polygon feature which are within a distance of polygon features. + + erasing_parts_of_building_polygons_in_water_features: + Erases parts of building polygons that intersect with buffered water features. + + transforming_small_polygons_to_points: + Transforms small building polygons to points, and maintains large enough building polygons + + merge_polygons: + Merges building polygons that where not too close to water-features with the corrected building polygons. + + removing_points_in_water_features: + Selects points that do not intersect with any water features, making sure no tourist cabins are lost. Then applies symbology to the remaining points. + Why: + There should be no geometric conflicts between building polygon/point objects and water-features, except for tourist huts. + """ + environment_setup.main() selecting_water_polygon_features() - selecting_water_features_close_to_building_polygons() erasing_parts_of_building_polygons_in_water_features() transforming_small_polygons_to_points() merge_polygons() @@ -26,9 +48,7 @@ def main(): @timing_decorator def selecting_water_polygon_features(): """ - Summary: - Selects water-related polygon features from a land cover dataset based on specific types - and saves them to a new layer. + Creates water polygon feature which are within a distance of polygon features. """ sql_expression_water_features = f"objtype = 'FerskvannTørrfall' Or objtype = 'Innsjø' Or objtype = 'InnsjøRegulert' Or objtype = 'Havflate' Or objtype = 'ElvBekk'" custom_arcpy.select_attribute_and_make_permanent_feature( @@ -37,13 +57,6 @@ def selecting_water_polygon_features(): output_name=Building_N100.removing_points_and_erasing_polygons_in_water_features___water_features___n100_building.value, ) - -@timing_decorator -def selecting_water_features_close_to_building_polygons(): - """ - Summary: - Select water features that are within a specified distance from building polygons. - """ custom_arcpy.select_location_and_make_permanent_feature( input_layer=Building_N100.removing_points_and_erasing_polygons_in_water_features___water_features___n100_building.value, overlap_type=custom_arcpy.OverlapType.WITHIN_A_DISTANCE, @@ -56,8 +69,7 @@ def selecting_water_features_close_to_building_polygons(): @timing_decorator def erasing_parts_of_building_polygons_in_water_features(): """ - Summary: - Erases parts of building polygons that intersect with buffered water features. + Erases parts of building polygons that intersect with buffered water features. """ # Buffering the water features with 15 Meters @@ -95,6 +107,9 @@ def erasing_parts_of_building_polygons_in_water_features(): @timing_decorator def transforming_small_polygons_to_points(): + """ + Transforms small building polygons to points, and maintains large enough building polygons + """ sql_expression_correct_size_polygons = ( f"Shape_Area >= {N100_Values.minimum_selection_building_polygon_size_m2.value}" ) @@ -124,8 +139,7 @@ def transforming_small_polygons_to_points(): @timing_decorator def merge_polygons(): """ - Summary: - Merges building polygons that are either too close to water features or have had parts erased due to intersection with water features. + Merges building polygons that where not too close to water-features with the corrected building polygons. """ arcpy.management.Merge( inputs=[ @@ -139,8 +153,7 @@ def merge_polygons(): @timing_decorator def removing_points_in_water_features(): """ - Summary: - Selects points that do not intersect with any water features and applies symbology to the filtered points. + Selects points that do not intersect with any water features, making sure no tourist cabins are lost. Then applies symbology to the remaining points. """ arcpy.management.Merge( @@ -166,7 +179,7 @@ def removing_points_in_water_features(): inverted=True, ) - # Select points that DO NOT intersect any waterfeatures + # Select points that DO NOT intersect any water-features custom_arcpy.select_location_and_make_permanent_feature( input_layer=Building_N100.removing_points_and_erasing_polygons_in_water_features___not_tourist_cabins___n100_building.value, overlap_type=custom_arcpy.OverlapType.INTERSECT,